dayjs icon indicating copy to clipboard operation
dayjs copied to clipboard

Duration between dates seems to be incorrect

Open simeon9696 opened this issue 3 years ago • 11 comments

Describe the bug Duration seems to be incorrect. The difference between March 29th, 2020 and March 28th 2021 should be less than 12 months

Expected behavior Calculating the difference between two days one year apart from each other should yield a difference of one year.

Information

  • Day.js Version: 1.10.3
  • OS: Windows 10 20H2
  • Browser Chrome 89
  • Time zone: GMT -4:00

I'm trying to get the difference between two dates:

  • A fixed date (March 29th, 2020)
  • Todays date (at the time of writing March 28th, 2021)

I do that with:

      const lockdown = dayjs("3/29/2020", "M/DD/YYYYY"); //29th March 2020
      const currentDate = dayjs();
 
      // get the difference between the moments
      const diff = currentDate.diff(lockdown);
    
      //express as a duration
      const diffDuration = dayjs.duration(diff);

   
      // display
      const timeInLockDown = {
        months: diffDuration.months(),
        days: diffDuration.days(),
        hours: diffDuration.hours(),
        minutes: diffDuration.minutes(),
        seconds: diffDuration.seconds()
      };

This yields diffDuration as:

days: 4
hours: 1
milliseconds: 709
minutes: 4
months: 12
seconds: 5
years: 0

Which doesn't make sense to me? It shouldn't 12 months and 4 days. Also, I think that if it is 12 months and 4 days, then years should be 1 but in this instance it's 0.

Is this expected behaviour?

simeon9696 avatar Mar 28 '21 05:03 simeon9696

Just to add to @simeon9696 's issue, using the CustomParseFormat plugin along with Duration plugin returns NaN for diff in above example.

https://runkit.com/6052c5915dac89001a8e3602/606151bf64ef4e00192ea162

bornova avatar Mar 29 '21 04:03 bornova

I just noticed a bug and it seems related to this issue:

// dayjs version 1.10.5
import dayjs from 'dayjs';
import dayjsDurationPlugin from 'dayjs/plugin/duration';

const a = dayjs('3/19/2018');
const b = dayjs('3/17/2021');
const diff = b.diff(a, 'day'); // => 1094 correct
dayjs.duration(diff, 'days'); // => { years: 2, months: 12, days: 4 } incorrect

I would expect the output to be { years: 3, days: 4 }.

avand avatar May 27 '21 00:05 avand

is there any movement on this? just stumbled across it myself

caterpaula avatar Nov 17 '21 10:11 caterpaula

Is there any further on this, please? I too have encountered this issue.

Oliver-Saer avatar Apr 14 '22 15:04 Oliver-Saer

any update?

xmsz avatar Jun 16 '22 05:06 xmsz

I encountered this issue today too.

Example

startDate = dayjs("2021-09-21")
endDate = dayjs("2050-09-21")

dayjs(startDate).add(29, "y").toDate() // returns "2050-09-21", same as endDate as expected -- 29 years' difference

dayjs.duration(endDate.diff(startDate)) // returns an object containing 29 years and 7 days instead

Thoughts

As the code @avand pasted above actually also shows, the issue is the interaction between diff and duration.

The method diff by itself, if you don't specify a unit of time argument, basically returns the difference between the two dates in milliseconds. This difference is accurate but has no context.

If you then pass this milliseconds result to duration it seems to simply work out how long that should be in terms of fixed month lengths, etc. The milliseconds have no context with them as to the dates they originally sat between. Hence you get these weird rounding errors.

That's my theory anyway.

Workaround?

Until this is fixed, a workaround might be to use diff more specifically.

So say for me, for now, I want my interface to recognise that from 2021-09-21 to 2050-09-21 is 29 years but that 2021-09-21 to 2050-09-20 (just a day less) is still 28 years.

I can do this with:

dayjs("2050-09-21").diff(dayjs("2021-09-21"), "years")

If I then pass this onto duration, the result I get back is accurate.

Summary of problem

In short I think both the way diff gets rid of the context of its calculations and the fact that duration doesn't expect any context is the problem here.

guypursey avatar Aug 28 '22 11:08 guypursey

Further evidence for the above observed in comment on a related issue.

Brief thread on more accurate duration requirements also adds to this.

guypursey avatar Aug 28 '22 11:08 guypursey

Same problem here: const monthDifference = dayjs("2023.03.04").diff(dayjs("2023.02.06"), "month"); Result: 0 Though, this does works: const monthDifference = dayjs("2023.03.04").get("month") - (dayjs("2023.02.06").get("month")); Result: 1

ablarer avatar Feb 06 '23 11:02 ablarer

Here too:

const diffMonth = to.diff(from, 'month');
console.log({fromParam, toParam, diffMonth, diffMonth2: to.get('month') - from.get('month')});

{fromParam: '2023-05-29', toParam: '2023-07-02', diffMonth: 1, diffMonth2: 2}

"dayjs": "^1.11.7"

vendramini avatar May 05 '23 14:05 vendramini

"dayjs": "1.11.10"

In my experience, you can't rely on the duration method when calculating duration in months or years. This is due to the fact that months do not contain the same number of days, and the calculations use one fixed value: ~30.41666 (30 days 10 hours).

Example:

const dateFrom = "2024-02-02T00:00:00"; // February 2, 2024
let dateTo = "2024-03-03T00:00:00"; // March 3, 2024
const duration1 = dayjs.duration(dayjs(dateTo).diff(dayjs(dateFrom))); // 0 months, 30 days, 0 hours

dateTo = "2024-03-04T00:00:00"; // March 4, 2024urs
const duration2 = dayjs.duration(dayjs(dateTo).diff(dayjs(dateFrom))); // 1 month. 0 days, 14 hours

umnyash avatar Jan 18 '24 16:01 umnyash