简体   繁体   中英

Calculate Duration Between Two Dates with Javascript

I basically want to have two dates, ie:

 var startDate = new Date("01/01/2001 01:00:00");
 var currentDate = new Date();

Then I want to get the duration by subtracting startDate from currentDate and display it in the form of: a years b month(s) c day(s) d hour(s) e second(s) .

My problem so far has been that if you take the shortcut, ie:

var duration = currentDate.getTime() - startDate.getTime();
var seconds = duration / 1000;
var minutes = duration / 1000 / 60;

etc...

Then you get a false duration because, for example, months aren't always 30 days, years aren't always 365 days (leap years), etc. Not to mention that somehow when I did this it was off by roughly a year.

I've looked around the web and through various posts on StackOverflow but I couldn't find a working example of a JavaScript method that accurately subtracts one date from another and allows you to get the details (year, month, day, hour, minute, second) from the result date.

There is a website that can already do this exactly how I'd like to do it: http://www.timeanddate.com

There is no obvious simple solution to the problem if you want a "correct" result because months have different numbers of days.

But at the same time, the human brain gets ever more sloppy when it comes to large date ranges. So "1 year, 11 months" is almost as good as "almost two years". So before you start with a 100% correct solution, ask yourself if the result is good enough when you fake it and simply use 30 day months.

If that really isn't an option: Get rid of the hour and minutes; they'll just be confusing. Create two dates which both start at midnight (ie HH:MM:SS is 00:00:00).

Now the question is how many days, months and years is between them? That depends. The time between June, 1. and August, 31. is almost two months but there is no month between the two dates. It's 61 days, 0 months . Or 1 month, 30 days ? Which is "correct"?

The mathematical correct solution will give you results with as much as 61 days difference which users will find irritating.

The human solution isn't mathematically correct, so you will end up having to code heuristics that fake human feeling of time.

Which is why so many sites say "more than one month" if some dates differ for more than 30 days and continue with "half a year", "a year" (for anything between 330 to 380 days) and "more than a year", etc. They use the sloppy human brain to their advantage instead of trying to come up with an "exact" result (which a) isn't really helpful either and b) no one really agrees what "exact" is supposed to mean).

I've been struggling with this exact problem for a few days now. Finally made something I think work. It looks like crap now, sloppy comments and stupid var names, and Ill do a cleanup tomorrow. But I figured I'll post it anyways in case u need it.

EDIT: I cleaned up the code and made a gist of it. Check it out here: https://gist.github.com/4705863

EDIT2: Damn it, found a bug. I'm on it. Fixed the bug, works fine now!

    // Time difference function
function timeDiff(start, end) {
    //today, now!
    //Get the diff
    var diff = end - start;
    //Create numbers for dividing to get hour, minute and second diff
    var units = [
    1000 * 60 * 60 *24,
    1000 * 60 * 60,
    1000 * 60,
    1000
    ];

    var rv = []; // h, m, s array
    //loop through d, h, m, s. we are not gonna use days, its just there to subtract it from the time
    for (var i = 0; i < units.length; ++i) {
        rv.push(Math.floor(diff / units[i]));
        diff = diff % units[i];
    }

    //Get the year of this year
    var thisFullYear = end.getFullYear();
    //Check how many days there where in last month
    var daysInLastMonth = new Date(thisFullYear, end.getMonth(), 0).getDate();
    //Get this month
    var thisMonth = end.getMonth(); 
    //Subtract to get differense between years
    thisFullYear = thisFullYear - start.getFullYear();
    //Subtract to get differense between months
    thisMonth = thisMonth - start.getMonth();
    //If month is less than 0 it means that we are some moths before the start date in the year.
    // So we subtract one year, and add the negative number (month) to 12. (12 + -1 = 11)
    subAddDays = daysInLastMonth - start.getDate();
    thisDay = end.getDate();
    thisMonth = thisMonth - 1;
    if(thisMonth < 0){
        thisFullYear = thisFullYear - 1;
        thisMonth = 12 + thisMonth;
        //Get ends day of the month
    }
    //Subtract the start date from the number of days in the last month, add add the result to todays day of the month
    subAddDays = daysInLastMonth - start.getDate();
    subAddDays = thisDay + subAddDays;


    if(subAddDays >= daysInLastMonth){
        subAddDays = subAddDays - daysInLastMonth;
        thisMonth++;
        if (thisMonth > 11){
            thisFullYear++;
            thisMonth = 0;
        }
    }
    return {
        years: thisFullYear,
        months: thisMonth,
        days: subAddDays,
        hours: rv[1],
        minutes: rv[2],
        seconds: rv[3]
    };
}
//The start date/From date. Here i add one hour to offset it. 
//var start = new Date(1814, 3, 20, 1);
//The end date. today, now!
//var end = new Date();
//Get the difference
//var d = timeDiff(start, end);
// Log that bitch
//console.log('years: '+ d.years + '. months: '+ d.months + '. days: ' + d.days + '. hours:' +d.hours+ '. minutes:' + d.minutes + '. seconds: ' + d.seconds);

http://www.w3schools.com/jsref/jsref_obj_date.asp

myDate.UTC() // get the number of milliseconds since the Unix epoch

So

delta_ms = laterDate.UTC() - earlierDate.UTC();
delta_days = Math.round(delta_ms / (1000 * 60 * 60 * 24));

If you want months and years, you need to specify whether you want calendar difference (eg 1st May to 1st June is 1 month, but 28th Feb to 30th March is...?), or against a 'standard' year like 365 days and 30 day months, etc.

This is not straightforward, even if it seems like it should be. For example in finance there are at least 5 ways to count the gap between two dates, using standard months or not, and using standard dates or not. Generating the date after some date is easy (1 calendar month after 1st Feb is 1st March) but going backward is not, as the mapping is not 1:1.

Also, note that this is an absolute value which does not need adjusting for leap years, leap seconds, summer time, timezones and all the other ephemera of time formats.

You can consider those cases too. Consider 2 dates 2nd Mar 2008 and 5th Jun 2013 .

             Mar
2008  ________###########################
2009  ###################################
2010  ###################################
2011  ###################################
2012  ####################################  (leap)
2013  ###############--------------------
                  Jun

You can subtract the years in between (ie 2009, 2010, 2011, 2012). Now here 2012 is a leap year but it doesn't matter because it will be counted in years not months .

Years = 4

Now, you are left with this

            2 
Mar 08  ____################################
Apr 08 ####################################
May 08 #####################################
Jun 08 ####################################
Jul 08 #####################################
Aug 08 #####################################
Sep 08 ####################################
Oct 08 #####################################
Nov 08 ####################################
Dec 08 #####################################
Jan 12 #####################################
Feb 12 ###################################
Mar 12 #####################################
Apr 12 ####################################
May 12 #####################################
Jun 12 #########----------------------------
                5

Just count the full months. It doesn't matter how many days they contains. They are being counted in months not days .

Months = 14

           2 
Mar 08  ____################################
Jun 12  #########----------------------------
                5

Now count the remaining days in Mar2008 and Jun2012 (excluding the end dates). Here the count comes out to be 29(Mar) + 4(Jun) = 33.

Days = 33

But 33 days seems odd as it can be converted into 1month + some days. If this happens, the question arises which month to choose - either Mar(31 days), Jun(30 days) or just reduce 30days to add a month. I think while considering huge differences, this would hardly matter. If we consider March, then the difference would be

4 Years 15 Months 2 Days or simply 5 Years 3 Months 2 Days

The same approach can be continued for time.

EDITED

Let `date1` be greater than `date2`
Let max(month) gives the total days in the month

years = date1.year - date2.year - 1;
months = date1.month - date2.month - 1;
days = (max(date2.month) - date1.day) + (date2.day - 1);
if(days >= 30)       //Here you can take totals days in date1 or date2
{
    month++;
    days-=30;
}
while(month >= 12)      //Here month can reach a value of 24 because it can be incremented above too
{
    years++;
    month-=12;
}
print("Difference is " + years + " years " + month + " months " + days + " days");

Leaving the answer for myself in future

Simply use moment and its duration plugin

and use like

moment.duration(moment('then').diff(moment('now'))).format('d [Days] hh:mm:ss')

it works

var years = currentDate.getYear() - startDate.getYear();
var months = currentDate.getMonth() - startDate.getMonth();
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM