Add a decimal year to a moment.js date - momentjs

How can I add a decimal/float value to a moment.js date object?
moment('2017-09-20').add(1.234, 'years');
does only change the year and month, not the day. I was expecting that moment will calculate the correct date of '2017-09-20' plus one year plus the nearest day of the 0.234th of the year 2018. But instead it prints out '2017-11-20'.

You can create a moment.duration for 1.234 years and then add using moment().add(Duration);.
EDIT:
1.234 years is equal to 1 year and 2.808 months as you can see using toISOString(). The same duration is equal to 451 days. As the asDays() states:
moment.duration().asDays() gets the length of the duration in days.
So you can use asDays() output as input of add(Number, String);.
Here a live example:
// Create moment duration for 1.234 years
var dur = moment.duration(1.234, 'years');
// 1.234 years is equal to 1 year and 2.808 months
console.log(dur.toISOString()); // P1Y2.808M
// 1.234 years is also equal to 451 days
console.log(dur.asDays()); // 451
// Add duration using moment().add(Duration);
var m1 = moment('2017-09-20').add(dur);
console.log(m1.format()); // 2018-12-20T00:00:00+01:00
// Add number of days
var m2 = moment('2017-09-20').add(dur.asDays(), 'd');
console.log(m2.format()); // 2018-12-15T00:00:00+01:00
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>

Related

Filter specific months in several years

var x1= ee.ImageCollection('LANDSAT/LC08/C01/T1_SR').filterBounds(geometry)
.filterDate('2019-07-01', '2019-10-30')
.sort('CLOUD_COVER');
How could I filter the dates in Sep, Oct, and Nov between 2014 and 2020?
Here is an example of how to filter by year, then specific months within those years.
// Create image collection of S-2 imagery for the perdiod 2015-2020
var S2 = ee.ImageCollection('COPERNICUS/S2')
//filter start and end date
.filterDate('2015-07-01', '2020-10-31')
.filter(ee.Filter.calendarRange(9, 11,'month'))

momentjs calculates date difference incorrectly

In my angular web application, I want to compare two dates to see if a person is less than 18 years old when she/he entered the company. Here is the code I use to do this:
const dayOfBirth = moment(formControl.value, this.dateFormat, true).startOf('day');
const entranceDateControl = this.wizardFormGroup.get('entranceDate');
const entranceDate = moment(entranceDateControl.value, this.dateFormat, true).startOf('day');
// Check validation rule R3: Age is less than 18 compared to entrance date
const difference = moment.duration(Math.abs(entranceDate.diff(dayOfBirth)));
if (difference.years() < 18) {
const validationMessage = this.getValidationMessage('R3', formControlName);
return validationMessage ? validationMessage.message : null;
}
As you can see, I am using startOf('day') to get rid of any time component so that I only handle dates. I use diff() to get the difference between two dates and then duration() to convert the difference to years, months, days, etc. Using this code, the validation message should NOT show when the person is turning 18 years old on the day when she/he entered the company.
Upon testing this, I came across what is, in my opinion, strange behavior. Depending on months and years used, it gave different results. For instance, for these dates it was Ok:
dayOfBirth = 1998-03-01, 1998-04-01, ..., 2000-02-01
entranceDate = 2016-03-01, 2016-04-01, ..., 2018-02-01
But the following dates returned the validation message:
dayOfBirth = 2000-03-01, 2000-04-01, ..., 2002-02-01
entranceDate = 2018-03-01, 2000-04-01, ..., 2020-02-01
After these dates, i.e. using 2002-03-01 and onward, it works again. I also got wrong result for the dates preceding 1998-03-01.
Now, I had a closer look at the Duration object and I noticed that for the times where it was less than 18 years, it had calculated 864 milliseconds less then when it came to the right conclusion that it was 18 years between the dates.
Correct duration
----------------
dayOfBirth = 1998-03-01, 1998-04-01, ..., 2000-02-01
entranceDate = 2016-03-01, 2016-04-01, ..., 2018-02-01
Duration = 568080000000 ms
Wrong duration
--------------
dayOfBirth = 2000-03-01, 2000-04-01, ..., 2002-02-01
entranceDate = 2018-03-01, 2000-04-01, ..., 2020-02-01
Duration = 567993600000 ms
Duration difference
-------------------
568080000000 - 567993600000 = 86400000 ms = 24 hours = 1 day
Has anyone an explanation for this? Can it be considered a bug in momentjs? Any viable workaround for this?
I didn't go into details in moment source code but it seems duration() is playing tricks with you. Simplify the code and rely only on diffas follow and you should be good (at least it seems to work for the samples you provided). And it's easier on the eyes :)
const moment = require('moment')
const dayOfBirth = moment('2000-03-01').startOf('day');
const entranceDate = moment('2018-03-01').startOf('day');
const difference = entranceDate.diff(dayOfBirth, 'years')
if (difference < 18) {
console.log( '<18')
} else {
console.log( '>=18')
}
will output >=18

Calculating time difference in sas (military time)

I am having trouble figuring out how to calculate duration of a time variable
Any thoughts on how to tackle this?
A military time value encoded as a integer number h,hmm can be processed by converting the number to a SAS time value and then performing delta computations using certain assumptions.
data sleep_log;
input name $ boots_down boots_up;
datalines;
Joe 2000 0600 slept over midnight
Joe 1000 1230 slept into lunch
Joe 1630 1700 30 winks
Joe 0100 0100 out cold!
run;
data sleep_data;
set sleep_log;
down = hms(
int(boots_down / 100) /* extract hours */
, mod(boots_down , 100) /* extract minutes */
, 0 /* seconds not logged, use zero */
);
up = hms(
int(boots_up / 100) /* extract hours */
, mod(boots_up , 100) /* extract minutes */
, 0 /* seconds not logged, use zero */
);
* SAS time values are linear and simple arithmetic can apply;
if up <= down
then delta = '24:00't + up - down; /* presume roll over midnight */
else delta = up - down;
format down up delta time5.;
run;
A more robust log would also record the day, eliminating presumptions and providing a proper time dimension.
You can extract the Hours and Minutes from your numeric military time HHMM , then create a SAS time using HMS() function.
Extract Hours: Divide your HHMM by 100 and save as integer to get hours,
Extract Minutes: get the Remainder (MOD) of HHMM by 100 to get the minutes,
Create a new time variable using HMS(Hour,Minute,Second),
Create a new Datetime for each using DHMS(date,hour,minute,second)
Full Code:
data have;
input sleep awake date_s date_w;
informat date_s date9. date_w date9.;
format sleep z4. awake z4. date_s date9. date_w date9.;
datalines;
2300 0500 12feb2018 13feb2018
2000 0300 11feb2018 12feb2018
0530 1230 10feb2018 10feb2018
;
run;
data want;
set have;
new_sleep_time=hms(int(sleep/100),int(mod(sleep,100)),0);
new_awake_time=hms(int(awake/100),int(mod(awake,100)),0);
dt_awake=dhms(date_w,hour(new_awake_time),minute(new_awake_time),0);
dt_sleep=dhms(date_s,hour(new_sleep_time),minute(new_sleep_time),0);
diff=dt_awake-dt_sleep;
keep new_sleep_time new_awake_time dt_awake dt_sleep diff;
format new_sleep_time time8. new_awake_time time8. diff time8. dt_awake datetime21. dt_sleep datetime21.;
run;
Output:
new_sleep_time=23:00:00 new_awake_time=5:00:00 diff=6:00:00 dt_awake=13FEB2018:05:00:00 dt_sleep=12FEB2018:23:00:00
new_sleep_time=20:00:00 new_awake_time=3:00:00 diff=7:00:00 dt_awake=12FEB2018:03:00:00 dt_sleep=11FEB2018:20:00:00
new_sleep_time=5:30:00 new_awake_time=12:30:00 diff=7:00:00 dt_awake=10FEB2018:12:30:00 dt_sleep=10FEB2018:05:30:00

To calculate Moving/Rolling back Weekly (7 days) Sum:

Please help to calculate Moving/Rolling back Weekly Sum of Amount($4) based on Distributor wise ($2) and Rolling Date wise.
Want to set vaiable like
RollingStartDate ==01/05/2015 and RollingInterval==7 and RollingEndDate ==08/05/2015
For Example :
1st May 2015 Rolling 7 Days data set would be from 01/05/2015 to 25/04/2015
2nd May 2015 Rolling 7 Days data set would be from 02/05/2015 to 26/04/2015
....................................................................
7th May 2015 Rolling 7 Days data set would be from 07/05/2015 to 01/05/2015
8th May 2015 Rolling 7 Days data set would be from 08/05/2015 to 02/05/2015
Input.csv
Des,Date,Distributor,Amount,Loc
aaa,25/04/2015,abc123,25,bbb
aaa,25/04/2015,xyz456,75,bbb
aaa,26/04/2015,xyz456,50,bbb
aaa,27/04/2015,abc123,250,bbb
aaa,27/04/2015,abc123,100,bbb
aaa,29/04/2015,xyz456,50,bbb
aaa,30/04/2015,abc123,25,bbb
aaa,01/05/2015,xyz456,75,bbb
aaa,01/05/2015,abc123,50,bbb
aaa,02/05/2015,abc123,25,bbb
aaa,02/05/2015,xyz456,75,bbb
aaa,04/05/2015,abc123,30,bbb
aaa,04/05/2015,xyz456,35,bbb
aaa,05/05/2015,xyz456,12,bbb
aaa,06/05/2015,abc123,32,bbb
aaa,06/05/2015,xyz456,43,bbb
aaa,07/05/2015,xyz456,87,bbb
aaa,08/05/2015,abc123,58,bbb
aaa,08/05/2015,xyz456,98,bbb
Example: 8th May 2015 Rolling 7 Days data set would be from 08/05/2015 to 02/05/2015
aaa,02/05/2015,abc123,25,bbb
aaa,02/05/2015,xyz456,75,bbb
aaa,04/05/2015,abc123,30,bbb
aaa,04/05/2015,xyz456,35,bbb
aaa,05/05/2015,xyz456,12,bbb
aaa,06/05/2015,abc123,32,bbb
aaa,06/05/2015,xyz456,43,bbb
aaa,07/05/2015,xyz456,87,bbb
aaa,08/05/2015,abc123,58,bbb
aaa,08/05/2015,xyz456,98,bbb
Output for 8th May 2015 Rolling 7 Days data set
RollingDate,Distributor,Amount
08/05/2015,abc123,145
08/05/2015,xyz456,350
I am able to obtain the above output from this command :
awk -F, '{key=$3;b[key]=b[key]+$4} END {for(i in a) print i","b[i]}'
Kindly suggest how to derive weekly split-up data sets then Sum.
Desired Output:
RollingDate,Distributor,Amount
01/05/2015,abc123,450
01/05/2015,xyz456,250
02/05/2015,abc123,450
02/05/2015,xyz456,250
03/05/2015,abc123,450
03/05/2015,xyz456,200
04/05/2015,abc123,130
04/05/2015,xyz456,235
05/05/2015,abc123,130
05/05/2015,xyz456,247
06/05/2015,abc123,162
06/05/2015,xyz456,240
07/05/2015,abc123,137
07/05/2015,xyz456,327
08/05/2015,abc123,145
08/05/2015,xyz456,350
Edit#1
1.
The logic is to find a Sum of Amount is billed to the distributor for the period of 7days range, i.e if i need to calculate sum for 1st May then I need to consider the line items from 1st May,30th Apr,29th Apr,28th Apr,27th Apr,26th Apr and 25th Apr , It is equivalent to 1st May (-) minus 6 days back ... like wise 2nd May rolling date is equal to from 2nd May to 26th May ( 2nd May minus 6 days back ..)
2.
Date format is DD/MM/YYYY - 02/05/2015 is 2nd May
Since the file contains 2 to 3 months deatils , dont want to select the first date (25/04/2015) from file then do minus 6 days back analysis , hence "RollingStartDate" will help from which dates need to consider the data , "RollingInterval" will help to do the analysis for "7 days" moving back or "14 days" moving back or "30 days monthly " moving back analysis.
"RollingEndDate" will help to avoid if actual file contains any future date data availabe , in this case if 09th or 15th may date line items need to be excluded ...
Here's a solution that just excludes dates that don't have 7 days before them instead of requiring a specific start/stop range:
$ cat tst.awk
BEGIN { FS=OFS=","; window=(window?window:7); secsPerDay=24*60*60 }
NR==1 { print "RollingDate", $3, $4; next }
{
endSecs = mktime(gensub(/(..)\/(..)\/(....)/,"\\3 \\2 \\1 0 0 0","",$2))
if (begSecs=="") {
begSecs = endSecs + ((window-1) * secsPerDay)
}
amount[endSecs][$3] += $4
dists[$3]
}
END {
for (currSecs=begSecs; currSecs<=endSecs; currSecs+=secsPerDay) {
for (dayNr=1; dayNr<=window; dayNr++) {
rollSecs = currSecs - ((dayNr-1) * secsPerDay)
for (dist in dists) {
sum[dist] += (rollSecs in amount ? amount[rollSecs][dist] : 0)
}
}
for (dist in dists) {
print strftime("%d/%m/%Y",currSecs), dist, sum[dist]
delete sum[dist]
}
}
}
.
$ awk -f tst.awk file
RollingDate,Distributor,Amount
01/05/2015,xyz456,250
01/05/2015,abc123,450
02/05/2015,xyz456,250
02/05/2015,abc123,450
03/05/2015,xyz456,200
03/05/2015,abc123,450
04/05/2015,xyz456,235
04/05/2015,abc123,130
05/05/2015,xyz456,247
05/05/2015,abc123,130
06/05/2015,xyz456,240
06/05/2015,abc123,162
07/05/2015,xyz456,327
07/05/2015,abc123,137
08/05/2015,xyz456,350
08/05/2015,abc123,145
.
To use some different window size than 7 days, just set it on the command line:
$ awk -v window=5 -f tst.awk file
RollingDate,Distributor,Amount
29/04/2015,xyz456,175
29/04/2015,abc123,375
30/04/2015,xyz456,100
30/04/2015,abc123,375
01/05/2015,xyz456,125
01/05/2015,abc123,425
02/05/2015,xyz456,200
02/05/2015,abc123,100
03/05/2015,xyz456,200
03/05/2015,abc123,100
04/05/2015,xyz456,185
04/05/2015,abc123,130
05/05/2015,xyz456,197
05/05/2015,abc123,105
06/05/2015,xyz456,165
06/05/2015,abc123,87
07/05/2015,xyz456,177
07/05/2015,abc123,62
08/05/2015,xyz456,275
08/05/2015,abc123,120
The above uses GNU awk for true 2D arrays and time functions. Hopefully it's clear enough that you can make any modifications you need to include/exclude specific date ranges.

Groovy Time durations

I'm trying to get the difference between 2 dates in days, hours, and seconds:
import groovy.time.*
Date now = new Date()
// Using deprecated constructor just for this example
Date newYearsDay2000 = new Date(2000, 0, 1)
use (TimeCategory) {
now - newYearsDay2000
}
This prints:
-690023 days, -14 hours, -38 minutes, -27.182 seconds
Which is obviously nothing like the difference between today's date and 2000/1/1, where am I going wrong?
Thanks,
Don
Could be an issue with the deprecated constructor?
If you use Calendar (and the Groovy updated method) to create the newYearsDay2000 var, you get:
import groovy.time.*
import static java.util.Calendar.*
Date now = new Date()
// Use the static imported Calendar class
Date newYearsDay2000 = instance.updated( year:2000, month:JANUARY, day:1 ).time
use( TimeCategory ) {
now - newYearsDay2000
}
which gives the result:
3925 days, 23 hours, 59 minutes, 59.999 seconds
Edit
Yeah, the JavaDoc for Date shows that constructor with the comment:
Date(int year, int month, int date)
Deprecated. As of JDK version 1.1, replaced by Calendar.set(year + 1900, month, date) or GregorianCalendar(year + 1900, month, date).
Which leads me to believe that:
Date newYearsDay2000 = new Date(2000, 0, 1)
Is actualy creating the Date for new Years Day in the year 3900
Date
Parameters:
year - the year minus 1900.

Resources