New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
.add() edge case failure #3997
Comments
Durations are fixed numbers of milliseconds. A duration that is 1 month long is exactly the number of milliseconds in 30 calendar days. The intended use case for durations is around measuring... well durations. Lengths of time that are not related to dates. I think the code you are looking for might be code for calendar computations, where the amount of days in a month changes based on the month in question. Does this look right? moment('2017-03-01').add(1, 'month').subtract(1, 'millisecond').format()
"2017-03-31T23:59:59-07:00" |
This statement is not correct. As you can see in your example, 31 days are added, not 30. And in my example, 28 days are added, again not 30. When adding a duration to a moment, units such as months and years must be interpreted in a context, since they have a different duration depending on whether a year is a leap year and due to different numbers of days in months. In my opinion, the only sensible context is the context of the moment to which the duration is added. Possibly this can be achieved by simply changing the order to first handle years, then months, then shorter units. |
add-subtract.js performs the calculation for milliseconds, days and months, in that order. Other periods such as years, weeks, hours etc are stored in a duration as their closest shorter counterparts. Is there another use case where reversing this order of operation would produce undesired results? I can't come up with such a situation. |
Yes, duration addition with higher-order units uses calendar math, not timestamp math. I think I agree that the current behavior is incorrect wrt order of operations. A more common case: moment('2016-02-28').add(moment.duration(1, 'month').add(3, 'days'))
//should that behave like a)
moment('2016-02-28').add(1, 'month').add(3, 'days') // => "2016-03-31T00:00:00-04:00"
//or like b)
moment('2016-02-28').add(3, 'days').add(1, 'month') // => "2016-04-02T00:00:00-04:00" b is the current behavior and seems much less intuitive than a.
|
For what it is worth, I have checked that changing the order does not break any tests. However I can't push my changes. |
This looks like a problem with duration moment.duration(1, 'day').add(1, 'hour').toString(); // 'P1DT1H'
moment.duration(1, 'day').subtract(1, 'hour').toString(); // 'P1DT1H' - the same! |
@ashsearle I think that's a different bug, having to do with serializing negative duration components to string, not with the mechanics of add and subtract itself. |
@JohanRonnblom Why can't you push your changes? A pull request would be great here |
[bugfix] Add duration fields in month, day, time order, fixes #3997
Description of the Issue and Steps to Reproduce:
Adding something just less than a month produces unexpected results:
var start = moment('2017-03-01');
var dur = moment.duration(1, 'month').subtract(1, 'second');
start.add(dur).toDate();
Expected:
Cause: moment apparently subtracts milliseconds first, then presumably uses daysInMonth() on the intermediate object to subtract 28 days for February. Rather, it should use daysInMonth() on the original object.
Reporting system info (probably completely irrelevant):
console.log( (new Date()).toString())
The text was updated successfully, but these errors were encountered: