moment / moment

Parse, validate, manipulate, and display dates in javascript.
momentjs.com
MIT License
47.96k stars 7.05k forks source link

How many days are there in a month when using moment.duration to convert duration? #5667

Open haoyuanqiang opened 4 years ago

haoyuanqiang commented 4 years ago

Describe the bug I discovered one problem while using moment.duration to convert duration. The duration is more than one month.

const duration = moment.duration(4768933190); // 4768933190ms ≈ 55days
console.log(`${duration.months()}-${duration.days()} ${duration.hours()}:${duration.minutes()}:${duration.seconds()}`)

The expected output is 1-25 4:42:13, but the real output is 1-24 4:42:13. I thought one month was 30 days but it's not.

I wrote one small program for test:

moment.duration(1, 'months').asDays();   // output 30
// 86400000ms = 1day
moment.duration(30 * 86400000).asMonths(); // output 0.9856465225158627
duration = moment.duration(31 * 86400000);
console.log(`${duration.months()}-${duration.days()} ${duration.hours()}:${duration.minutes()}:${duration.seconds()}`)// output 1-0 0:0:0

Why are the days inconsistent between two method constructing a duration of one month?

To Reproduce Run these code to reproduce the behavior:

moment.duration(1, 'months').asDays();
moment.duration(30 * 86400000).asMonths();

Expected behavior

The days of one month are expected to an accurate number whether it is 30 or whatever

Screenshots none

Desktop (please complete the following information):

Moment-specific environment

Tue Aug 04 2020 14:34:23 GMT+0800 (中国标准时间)
2020/8/4 下午2:34:23
-480
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.52
2.24.0

Additional context none

MinusFour commented 4 years ago

The problem with this is that month durations vary according to which month/year it is. It could be any value between 28-31. moment averages the amount of days in a month by a span of 400 years which turns out to be 30.436875. As to why they are not consistent, days are being rounded down (from 30.436875) otherwise those are the days you would get in 1 month.

I'm unsure if moment can ever guarantee:

let n = months;
let days = moment.duration(n, 'months').asDays();
let nMonths = moment.duration(days, 'd').asMonths();

n === nMonths;

For any months (even if days are not rounded). At the very least not without some library that can handle precision accurately or changing how moment calculates days for each month.