Moment JS Date Difference as Month - javascript

I'm trying to get the difference between current local time and December 1, 2021 at 4 pm. There is a difference of 6 months and 2 hours. In this case I expect the answer to be something similar to 6.02. But 5.98 is coming. How can I get the answer I want?
enter image description here

According to the moment.js docs, the standard way to get the difference between two dates in your example would be now.diff(date, 'months', true), which should return a floating point number greater than 6.
now.diff(date) returns the millisecond difference between those two points in time. Calling moment.duration({milliseconds}).asMonths() is not ideal because some months may be 30 days long and others may be 31 days long. It appears that moment.js uses somewhere in between 30 and 31 days as the duration of one month. To address this issue, moment.js have discussed calendar diffs in the docs:
moment#diff has some special handling for month and year diffs. It is optimized to ensure that two months with the same date are always a whole number apart.
So Jan 15 to Feb 15 should be exactly 1 month.
Feb 28 to Mar 28 should be exactly 1 month.
Feb 28 2011 to Feb 28 2012 should be exactly 1 year.

The definition of "a month" can only be a "fuzzy" one, as the months of the calendar are of different lengths. One way of defining it would be to divide the year into 12 equal parts and use that as a "month-metric":
function monthsUntil(year,month,day,hour=0){
const trg=new Date(year,month-1,day,hour,0,0),
now=new Date(), nxt=new Date();
nxt.setFullYear(nxt.getFullYear()+1);
return (12*(trg-now)/(nxt-now)).toFixed(4);
}
console.log(monthsUntil(2022,12,1,16))

Related

How to get remaining days from dayjs when getting months

So I'm having trouble finding a solution to this online, and if there's one thing I hate; it's working with dates.
I need to be able to calculate a person's age in months and days. So if they were born July 3rd, 2020 I need something like 2 months, 21 days.
I have the code for the months here: return dayjs(dayjs()).diff(value, 'month'); where value is the date, but am struggling to think of a way to get those days to be accurate.
Anything would be great! Thank you.
I would suggest that you subtract the the numbers that represent the days in your dates from one another and take the absolute value.
In your example, with the dates July 3rd, 2020 and September 24th, 2020, this would mean doing 3-24 = -21, which would be 21 after taking the absolute value.
Note that your problem is sort of ill-posed. It doesn't really make sense to give someone's age in months, as the amount of days change in a given month. But what you can do is define that x months later just means changing the value of the months in a date. I.e., two months after July 3rd would be September 3rd. This way you can just take whatever difference remains and use this as number of days, as shown above.

getMonth() returning the following day for some reason

I'm retrieving a list of dates from my database and getting the month from those dates.
When I get month for this date it returns 0 for January.
var date = new Date('2015-12-31T22:57:12.000Z').getMonth();
When I get month for a similar date it returns 11 for December.
var date2 = new Date('2015-12-31T12:24:29.000Z').getMonth();
This will give the right result:
var date = new Date('2015-12-31T22:57:12.000Z').getUTCMonth();
If your timezone is GMT+02, then you can try like this also:
var date = new Date('2015-12-31T22:57:12.000+0200').getMonth();
As you want to why this both Dec string dates returning different month? This is the answer:
From your profile i could see your are from south Africa which is GMT+2 timezone.
First date string : 2015-12-31T22:57:12.000Z, which is 31 Dec and 22.57 hours mid night. While creating date object out of string date at client browser side, It's takes your local timezone into account. Its GMT + 2.
Thus, it adds 2 hours: 2015-12-31T22:57:12.000Z + 2 hours => 22.57 hours shift to next day after addition of 2 hours(22.57 +2 => next day 00.57), which would be January month morning 0.57 hours. So it returns 0 (January) Month.
While second : 2015-12-31T12:24:29.000Z which is 12:24 hours which shifts to 14.24 in same day after addition on 2 hours as above. So you are still in 31 Dec. hence you receive December for this.
The months in javascript are numbered from 0 to 11, days from 1 to 31

How to calculate relative date from now in only days using momentjs?

I am trying to get the amount of days relative to the current time, returning only days as units. So if something happened a week ago, it would say 7 days. If it happened 2 months ago, it would return that time in days as well.
I am aware of how to get there but I am having trouble putting the pieces together.
I have my days as a data attribute "data-order" so data-order="2019-4-2 00:00" or "2019-4-2" if it makes calculations easier.
$(".pop-cal").each(function (i, obj) {
moment.relativeTimeThreshold("m", 1);
moment.relativeTimeThreshold("d", 25 * 100);
var date = $(this).attr("data-order");
var momentDate = moment(date).fromNow();
$(this).attr("data-content", momentDate);
});
This is getting me dates, but they are always off.
My expected results would be similar confirmation to using google search and saying "58 days ago" and getting Tuesday February 12th 2019.
What I am currently getting as result are "59 days ago" on a moment time created on "2019-2-12.
The fromNow method isn't suitable if you need the exact number of days, because it works by converting the time to a Duration (measured in months and milliseconds), and then converting the Duration to a human readable form (humanize method).
As the Duration docs state:
It is much better to use moment#diff for calculating days or years
between two moments than to use Durations.
You can see the problem if you perform fromNow on February 28th and March 1st, 1 day apart but giving a fromNow of 4 days apart (due to 28 days in February instead of 31 days).
moment("2019-03-01").fromNow() // "45 days ago"
moment("2019-02-28").fromNow() // "49 days ago"
The moment#diff method can give you the exact difference between two moments in days, e.g. between moments a and b:
a.diff(b, 'days')

Why doesn't moment.js year diff year not match exactly 1?

I have executed these simple JS code lines
moment.duration(moment(new Date(2013,1,1)).diff(moment(new Date(2012,1,1)))).asYears()
moment.duration(moment(new Date(2012,1,1)).diff(moment(new Date(2011,1,1)))).asYears()
moment.duration(moment(new Date(2011,1,1)).diff(moment(new Date(2010,1,1)))).asYears()
moment.duration(moment(new Date(2010,1,1)).diff(moment(new Date(2009,1,1)))).asYears()
Outputs
1.0020739645577939
0.9993360575508053
0.9993360575508053
0.9993360575508053
Surely, there must be something wrong?
OR perhaps this anomaly has something to do with leap years? 2012 was a leap year
So I tried the next leap year 2016
moment.duration(moment(new Date(2016,1,1)).diff(moment(new Date(2015,1,1)))).asYears()
which outputs same as previous non leap years, so maybe has nothing to do with it
0.9993360575508053
Anyone has any idea what is going on?
First, note that new Date(year,1,1) refers to February 1st, not January. Javascript, following the UNIX time_t convention, numbers months starting with 0, with the idea being that you'll use the month number to index into an array of month names instead of using it directly. So to construct a Date object referring to January 1st, you should call new Date(year,0,1).
Second, taking a difference that's a whole number of days and converting to years will never result in a a whole number. You should instead ask Moment to give the difference in years in the first place:
> moment(new Date(2013,0,1)).diff(moment(new Date(2012,0,1)),'year');
1
If you want the result to include fractions of a year instead of being truncated to a whole number, add a truthy third parameter to diff. Here we're counting from July to January:
> moment(new Date(2013,0,1)).diff(moment(new Date(2012,6,1)),'year',true)
0.5
Your method doesn't work because a calendar year is not a fixed number of seconds; it's either 365 days, which is 31,536,000 seconds, or 366 days, which is 31,622,400 seconds. Since leap years usually happen every four years, the average calendar year is 365.25 days, or 31,557,600 seconds; and in fact, a light-year is defined as the the distance light travels in that exact number of seconds.
But in the modern Gregorian calendar, the leap-century rules mean that the average length of a year, which you have to take over 400 years to get a complete cycle, is really a little shorter: 365.2425 days or 31,556,952 seconds. This is the value Moment uses, but it only works as an average; any anniversaries that aren't an exact multiple of 400 years will never give you an integer result. Instead, as you found out, a common year will count as 31,536,000 / 31,556,952 = 0.99933605755085028689843... of a year, while a leap year will count as 31,622,400 / 31,556,952 = 1.00207396455779379453376886... years.
In your last example you got the common year value because you were asking for the length of the year from 2015 to 2016 - which is the length of the year 2015. If you count from 2016 to 2017, you get the expected leap year value:
> moment.duration(moment(new Date(2017,0,1)).diff(moment(new Date(2016,0,1)))).asYears()
1.0020739645577939
More specifically, you get the leap year length whenever the interval includes a Feb 29th. So if you're measuring the delta between the same date in consecutive years, you'll get 366 days if you start on any date between March 1st of the year before a leap year and February 28th of the leap year itself. If you start counting from February 29th, then of course there is no February 29th in the following year; you'll get 365 days if you count to February 28th and 366 if you count to March 1st. Starting with March 1st in the leap year the time to the recurrence of the same date is back to 365 days:
> moment.duration(moment(new Date(2016,1,28)).diff(moment(new Date(2015,1,28)))).asYears()
0.9993360575508053
> moment.duration(moment(new Date(2016,2,1)).diff(moment(new Date(2015,2,1)))).asYears()
1.0020739645577939
> moment.duration(moment(new Date(2017,1,28)).diff(moment(new Date(2016,1,28)))).asYears()
1.0020739645577939
> moment.duration(moment(new Date(2017,2,1)).diff(moment(new Date(2016,2,1)))).asYears()
0.9993360575508053
Try using:
moment(new Date(2013,1,1)).diff(moment(new Date(2012,1,1)),'year');

Tips for working with Pre-1000 A.D. Dates in JavaScript

I'm curious if anyone has any good solutions for accurately building dates prior to the year 1000 A.D. - particularly the years 1 - 100 AD.
For example, if I want to build a date for the start of the 1st millenium, I can't just do...
new Date(Date.UTC(1,0,1,0,0,0,0));
because it tries to be "smart" and assume that 1 is 1901, which gives me...
Sun Dec 31 1900 18:00:00 GMT-0600 (CST)
The same thing goes for the year 99...
new Date(Date.UTC(99,0,1,0,0,0,0));
which becomes
Thu Dec 31 1998 18:00:00 GMT-0600 (CST)
Thoughts?
i prefer:
var d = new Date(Date.UTC(year, month, day, hour, min, sec, 0));
d.setUTCFullYear(year);
this always works for all supported year values.
the setUTCFullYear() call fixes JavaScript's intentional bug if you ask me.
Have you tried using the setUTC... functions on a date object after its creation?
setUTCDate()
setUTCFullYear()
setUTCMonth()
setUTCHours()
setUTCMinutes()
setUTCSeconds()
Here is the basic solution I came up with. If a date is prior to year 1000, I just add a 1000 to it while constructing the date, then use setUTCFullYear() afterwards.
if (year >= 0 && year < 1000) {
var d = new Date(Date.UTC(year + 1000,mon,day,hour,min,sec,0));
d.setUTCFullYear(d.getFullYear() - 1000);
return d;
}
1000 may be overkill since I was only having problems with pre-100 dates... but, whatever.
You have to set the year again, like setFullYear() or setUTCFullYear().
The Date can store 285 616 years before and after 1. 1. 1970.
var d = new Date( 0000, 1, 29 ); // Thu Mar 01 1900 00:00:00 GMT+0100
d.setFullYear(-0004); // Wed Mar 01 -0004 00:00:00 GMT+0057
d.setFullYear( 0000, 1, 29 ); // Tue Feb 29 0000 00:00:00 GMT+0057
// Yes, year zero was a leap year
Explanation:
new Date( year [4-digit number, 0–99 map to 1900–1999], month [0-11], day [def. 1], hours, minutes, seconds, milisecs ); is same like Date.UTC but in local timezone.
hours, minutes and seconds will be automatically filled with zeros.
the year lower than 1900 is converted to 1900 by default.
the year 1900 is not a leap year, so it is shifted to next closest day 1. Mar.
so, we have to set the year to zero 0000 again (year always must be min. 4-digit in this case), month and day. We use setFullYear() method with optional parameters for month and day; then if the year will be a leap year, it won’t be shifted.
It's exactly what Date.UTC and the Date constructor function (called with numbers as arguments) are supposed to do. A simple workaround is to use Date.parse, which will not apply any corrections
new Date(Date.parse('0001-01-04'));
new Date(Date.parse('0001-01-04T18:00:00Z'));
Make some arbitrary date d and call d.setUTCFullYear(myDate). This seems to be working for me in Chrome's console.
Others have provided hints at a fix. The javascript date object was copied from Java, so has all its bugs too. There is not much point to Gregorian dates before 1582 anyway since before that various other calendars were in use.

Categories