开发者

Using .date() in JS to calculate the difference between two dates, what am I doing wrong?

Afternoon,

I'm calculating the difference between two dates using JS. I am trying to get a result accurate to a second. I've been getting on very well with it, but noticed an issue. The first two dates shown below show a difference of 5 years, but as soon as I add one more year to the future date, it shows 5 years, 1 Day. Where is the extra day coming from? When the future date hits 2020, it adds another day into the result, this is a recurring pattern. Can someone enlighten me? I don't have any leap year calculations involved yet, I've put my code below:

$(document).ready(function(){

    var todaysDate = new Date('2010/11/24 23:00:00');   
    var futureDate = new Date('2020/11/24 22:59:00');

    calculateTime(todaysDate,futureDate);
});

function calculateTime(todaysDate,futureDate){  
    var difference = futureDate.getTime() - todaysDate.getTime(); 

    var years = Math.floor(difference/1000/60/60/24/365);
        difference -= years*1000*60*60*24*365;

    var days = Math.floor(difference/1000/60/60/24);
        difference -= days*1000*60*60*24;

    var hours = Math.floor(difference/1000/60/60);
        difference -= hours*1000*60*60;

    var minutes = Math.floor(difference/1000/60);
        difference -= minutes*60*1000;

    var seconds = Math.floor(difference/1000);

    var result = years + ' Years, ';
        result += days + ' Days, ';
        result += hours + ' Hours, ';
        result += minutes + ' Minutes, ';
        r开发者_运维百科esult += seconds + ' Seconds';

    $('#time').html(result)
 }


Yep, 2020 is a leap year, so if your initial test was for 2010-2019, then you try 2010-2020, there's an extra day due to the leap year. Even if you're not explicitly handling leap years, the Javascript date object is Leap-year aware, so the difference will include an extra day's worth of seconds.

Try doing a difference between

1) 2010/11/24 -> 2020/02/28
2) 2010/11/24 -> 2020/03/01

You should end up with with 86,400,000 more in the difference.


Yes, the problem is with leap years. Your line

var years = Math.floor(difference/1000/60/60/24/365);

isn't calculating the number of years difference between the two dates, it's calculating the number of 365-day periods between them instead


The biggest error is to not have written a test suite:

  1. To check for edge case you know at development time
  2. To easily check later for other edge case you discover
  3. Testable code is usually more modular (see below the mock object I had to create to workaround the strong link of your function to jQuery: it would have been easier if the jQuery call had been out of calculateTime)

This is called test-driven development.

Now here are some others errors I've found:

  1. Your date strings format does not respect the specification. Your system understand your format, but some may not.
  2. Your date strings do not specify a timezone, so you will have issues on locale which have daylight saving.
  3. Your function will give you effective elapsed time, but that may not be what your users are interested in. In particular when daylight saving is involved: you may have +1/-1 hour.

Here is a test suite that works from a Windows command line prompt (use cscript testsuite.js to run it):

// jQuery mock object for calculateTime testing
// $('#time').html(result) => result is stored in $.result
var $ = (function() {
    var $, $$ = {
        html: function(result) {
            $.result = result
        }
    }
    return $ = function(ignore) {
        return $$
    }
})();

// Henryz's unmodified function
function calculateTime(todaysDate,futureDate) {
    var difference = futureDate.getTime() - todaysDate.getTime(); 

    var years = Math.floor(difference/1000/60/60/24/365);
        difference -= years*1000*60*60*24*365;

    var days = Math.floor(difference/1000/60/60/24);
        difference -= days*1000*60*60*24;

    var hours = Math.floor(difference/1000/60/60);
        difference -= hours*1000*60*60;

    var minutes = Math.floor(difference/1000/60);
        difference -= minutes*60*1000;

    var seconds = Math.floor(difference/1000);

    var result = years + ' Years, ';
        result += days + ' Days, ';
        result += hours + ' Hours, ';
        result += minutes + ' Minutes, ';
        result += seconds + ' Seconds';

    return $('#time').html(result)
}

function test(a, b, expected) {
    var toString = Object.prototype.toString;
    if (toString.apply(a) != '[object Date]') a = new Date(a);
    if (toString.apply(b) != '[object Date]') b = new Date(b);
    calculateTime(a, b);
    var got = $.result;
    WScript.Echo(
        got === expected
        ? "ok"
        : "not ok - ["+a.toString()+"] to ["+b.toString()+"]\r\n#      Got: "+got+"\r\n# Expected: "+expected
    );
}

test('2010/11/24 23:00:00', '2011/11/24 23:00:00', '1 Years, 0 Days, 0 Hours, 0 Minutes, 0 Seconds');
test('Nov 24, 2010 23:00:00', 'Nov 24, 2011 23:00:00', '1 Years, 0 Days, 0 Hours, 0 Minutes, 0 Seconds');
test('2010/11/24 23:00:00', '2020/11/24 22:59:00', '10 Years, 0 Days, 0 Hours, 0 Minutes, 0 Seconds');
test('2011/03/26 23:00:00', '2011/03/27 23:00:00', '0 Years, 1 Days, 0 Hours, 0 Minutes, 0 Seconds');
test('2010/10/30 23:00:00', '2010/10/31 23:00:00', '0 Years, 1 Days, 0 Hours, 0 Minutes, 0 Seconds');

Here is my output (local timezone (France) offset changed on 2010-10-31 and 2011-03-26):

ok
ok
not ok - [Wed Nov 24 23:00:00 UTC+0100 2010] to [Tue Nov 24 22:59:00 UTC+0100 2020]
#      Got: 10 Years, 2 Days, 23 Hours, 59 Minutes, 0 Seconds
# Expected: 10 Years, 0 Days, 0 Hours, 0 Minutes, 0 Seconds
not ok - [Sat Mar 26 23:00:00 UTC+0100 2011] to [Sun Mar 27 23:00:00 UTC+0200 2011]
#      Got: 0 Years, 0 Days, 23 Hours, 0 Minutes, 0 Seconds
# Expected: 0 Years, 1 Days, 0 Hours, 0 Minutes, 0 Seconds
not ok - [Sat Oct 30 23:00:00 UTC+0200 2010] to [Sun Oct 31 23:00:00 UTC+0100 2010]
#      Got: 0 Years, 1 Days, 1 Hours, 0 Minutes, 0 Seconds
# Expected: 0 Years, 1 Days, 0 Hours, 0 Minutes, 0 Seconds

So you should completely rewrite the function or better, use an existing, tested implementation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜