4

I want to calculate the number of days between 2 dates. Common problem.

For exemple :

var d0 = new Date("2016-02-27");
var d1 = new Date("2017-08-25");

Many people recommend using epoch difference :

var res = (d1 - d0) / 1000 / 60 / 60 / 24;
// res = 545 days

But I was doubtful so I wrote a naive function :

function days(d0, d1)
{
    var d = new Date(d0);
    var n = 0;
    while(d < d1)
    {
        d.setDate(d.getDate() + 1);
        n++;
    }
    return n;
}

This function and epoch difference essentially output the same result but not with my particuliar exemple. Probably because 2016 is a leap year.

res = days(d0, d1);
// res = 546 days

Any idea why ?

Community
  • 1
  • 1
Liios
  • 93
  • 8
  • This is a very well understood and documented subject, some light googling would reveal a plethora of information. – Matt Sep 06 '16 at 09:04
  • Some light googling revealed people just stick using epoch difference without messing to much with date-hell. – Liios Sep 06 '16 at 09:10
  • This is weird, but it is not the leap day that causes the difference. If you try 2016-02-27 until 2016-03-01 everything is right. – Fels Sep 06 '16 at 09:34
  • But if you try 2016-03-27 and 2017-03-27 you have 365 and 366 respectively. – Liios Sep 06 '16 at 09:58

2 Answers2

1

After testing, it appears that the loop stop at 2017-08-26, instead of 2017-08-25.

When you print the value of d0 and d1, here is the result :

d0: Sat Feb 27 2016 01:00:00 GMT+0100 (Central Europe Standard Time)

d1: Fri Aug 25 2017 02:00:00 GMT+0200 (Central Europe Daylight Time)

As you can see, there is a one-hour shift between the two dates, so when the index of the loop reaches Aug 25 2017, this shift is still there and makes the "lower than" operation true, where it should be false.

Make sure to normalize your dates before using it.

Community
  • 1
  • 1
Senua
  • 543
  • 4
  • 17
0

It doesn't have anything to do with leap year, but rather because you are conflating UTC and local time.

new Date("2016-02-27") // JS will interpret this input as UTC due to the hyphens

d.setDate(d.getDate() + 1);  // these get/set functions work with the LOCAL date

Both of these need to be working in the same context. Since you can't be sure if the local time zone will experience daylight saving time or not, or if you'll cross a DST transition, working in UTC is a much safer bet than working in local time.

Simply change your function to use:

d.setUTCDate(d.getUTCDate() + 1);

Alternatively, consider a library like moment.js where these things are already worked out.

moment("2017-08-25").diff("2016-02-27", "days")  // 545

The epoch based approach you showed is also correct, as long as the inputs are interpreted as UTC.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575