Moment: Is it a way to .add() or .subtract() float values?

Created on 20 Jun 2015  ·  3Comments  ·  Source: moment/moment

moment().add(1, "months") // >> Mon Jul 20 2015 13:36:12 GMT+0200
moment().add(1.9, "months") // >> Mon Jul 20 2015 13:36:12 GMT+0200
// Excpected: Mon Aug 17 2015 09:34:37 GMT+0200

For now, I have to do:

var duration = moment.duration(1.9, "months"); 
var ms = duration.asMilliseconds();
var date = moment().add(ms, "ms");

Why can not these methods add or substract float values?

Most helpful comment

This is by design, but could probably be explained better in the documentation.

You can use decimals when adding or subtracting hours, minutes, seconds, or milliseconds, but you can't subdivide days or larger components.

The reason is that there is a logical difference between time math and date math.

Time math assumes a linear time scale, just incrementing or decrementing the UTC-based timestamp. This handles DST transitions in real-time. For example, for most places in the United States, 2015-03-08T01:00:00 plus one hours would be 2015-03-08T03:00:00 because the hour from 2:00 to 3:00 was skipped for the DST transition.

Date math does not use a linear time scale, but rather uses the dates on the calendar. Using the same example, 2015-03-08T01:00:00 plus one _day_ would be 2015-03-09T01:00:00, even though only 23 hours have passed. Therefore, a "day" is this context is not a fixed amount of time, and thus subdividing it would be nonsensical. (Would half a day be 12 hours later, or 11.5 hours later on this day?)

Even without DST, consider that not all months are 30 days, and not all years are 365 days. "Adding" a year, doesn't really mean "add 365 days", it means, "advance the position on the calendar to the same month and day on the next year". This creates certain ambiguities, such as when adding one year to Feb 29th in a leap year and ending in a non-leap year. We choose to land on Feb 28th, but landing on Mar 1st would be equally valid.

As another example, consider that August 31 plus one month, minus one month, is August 30th.

Since date math doesn't follow the normal addition and subtraction rules of mathematics, it also can't handle division or multiplication operations, and thus you cannot add 1.9 months.

To think about it another way, what would you expect the result of midnight June 15th + 1.5 months to be? Any of the following could be done by a human:

  • 2015-06-15T00:00 + (0.5 * 30 days in June) = 2015-06-30T00:00 + (1 month = 30 days) = 2015-07-30T00:00
  • 2015-06-15T00:00 + (0.5 * 30 days in June) = 2015-06-30T00:00 + (1 month = 31 days) = 2015-07-31T00:00
  • 2015-06-15T00:00 + 1 month = 2015-07-15T00:00 + (0.5 * 31 days in July) = 2015-07-30T12:00
  • 2015-06-15T00:00 + [(61 days in June and July / 2 = 30.5 avg days in these months) * 1.5 months = 45.75 days] = 2015-07-30T18:00

So here are four different answers that are all logical results - which is why you cannot subdivide units when doing date math.

All 3 comments

This is by design, but could probably be explained better in the documentation.

You can use decimals when adding or subtracting hours, minutes, seconds, or milliseconds, but you can't subdivide days or larger components.

The reason is that there is a logical difference between time math and date math.

Time math assumes a linear time scale, just incrementing or decrementing the UTC-based timestamp. This handles DST transitions in real-time. For example, for most places in the United States, 2015-03-08T01:00:00 plus one hours would be 2015-03-08T03:00:00 because the hour from 2:00 to 3:00 was skipped for the DST transition.

Date math does not use a linear time scale, but rather uses the dates on the calendar. Using the same example, 2015-03-08T01:00:00 plus one _day_ would be 2015-03-09T01:00:00, even though only 23 hours have passed. Therefore, a "day" is this context is not a fixed amount of time, and thus subdividing it would be nonsensical. (Would half a day be 12 hours later, or 11.5 hours later on this day?)

Even without DST, consider that not all months are 30 days, and not all years are 365 days. "Adding" a year, doesn't really mean "add 365 days", it means, "advance the position on the calendar to the same month and day on the next year". This creates certain ambiguities, such as when adding one year to Feb 29th in a leap year and ending in a non-leap year. We choose to land on Feb 28th, but landing on Mar 1st would be equally valid.

As another example, consider that August 31 plus one month, minus one month, is August 30th.

Since date math doesn't follow the normal addition and subtraction rules of mathematics, it also can't handle division or multiplication operations, and thus you cannot add 1.9 months.

To think about it another way, what would you expect the result of midnight June 15th + 1.5 months to be? Any of the following could be done by a human:

  • 2015-06-15T00:00 + (0.5 * 30 days in June) = 2015-06-30T00:00 + (1 month = 30 days) = 2015-07-30T00:00
  • 2015-06-15T00:00 + (0.5 * 30 days in June) = 2015-06-30T00:00 + (1 month = 31 days) = 2015-07-31T00:00
  • 2015-06-15T00:00 + 1 month = 2015-07-15T00:00 + (0.5 * 31 days in July) = 2015-07-30T12:00
  • 2015-06-15T00:00 + [(61 days in June and July / 2 = 30.5 avg days in these months) * 1.5 months = 45.75 days] = 2015-07-30T18:00

So here are four different answers that are all logical results - which is why you cannot subdivide units when doing date math.

Thank you very much for taking the time to write this very detailed answer. I better understand the logic behind the operation of these functions.

I couldn't resist my self for thanking Matt, I've crossed the same issue that Delgan experienced, and mate! your answer is so complete, thank you.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RobinvanderVliet picture RobinvanderVliet  ·  3Comments

vbullinger picture vbullinger  ·  3Comments

alvarotrigo picture alvarotrigo  ·  3Comments

M-Zuber picture M-Zuber  ·  3Comments

ghost picture ghost  ·  3Comments