12

How to get difference between 2 Dates in Years, Months and days using moment.js? For example the difference between 4/5/2014 & 2/22/2013 should be calculated as 1 Year, 1 Month and 14 Days.

Nick
  • 780
  • 2
  • 13
  • 33

3 Answers3

18

Moment.js can't handle this scenario directly. It does allow you to take the difference between two moments, but the result is an elapsed duration of time in milliseconds. Moment does have a Duration object, but it defines a month as a fixed unit of 30 days - which we know is not always the case.

Fortunately, there is a plugin already created for moment called "Precise Range", which does the right thing. Looking at the source, it does something similar to torazaburo's answer - but it properly accounts for the number of days in the month to adjust.

After including both moment.js and this plugin (readable-range.js) in your project, you can simply call it like this:

var m1 = moment('2/22/2013','M/D/YYYY');
var m2 = moment('4/5/2014','M/D/YYYY');
var diff = moment.preciseDiff(m1, m2);
console.log(diff);

The output is "1 year 1 month 14 days"

Community
  • 1
  • 1
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
6

You hardly need moment.

d1 = new Date(2014, 3, 5);                // April 5, 2014
d2 = new Date(2013, 1, 22);               // February 22, 2013
diff = new Date(
    d1.getFullYear()-d2.getFullYear(), 
    d1.getMonth()-d2.getMonth(), 
    d1.getDate()-d2.getDate()
);

This takes advantage of the fact that the Date constructor is smart about negative values. For instance, if the number of months is negative, it will take that into account and walk back the year.

console.log(diff.getYear(), "Year(s),", 
    diff.getMonth(), "Month(s), and", 
    diff.getDate(), "Days.");

>> 1 Year(s), 1 Month(s), and 11 Days. 

Your calculation is wrong--it's not 14 days, it's six remaining days in February and the first five days of April, so it's 11 days, as the computer correctly computes.

Second try

This might work better given @MattJohnson's comment:

dy = d1.getYear()  - d2.getYear();
dm = d1.getMonth() - d2.getMonth();
dd = d1.getDate()  - d2.getDate();

if (dd < 0) { dm -= 1; dd += 30; }
if (dm < 0) { dy -= 1; dm += 12; }

console.log(dy, "Year(s),", dm, "Month(s), and", dd, "Days.");
  • 2
    Nice thinking about reusing the `Date` object's ability to walk out of range. However, there's a few problems with his approach. 1) The `Date` object takes on DST behavior of the local time zone, which can skew certain results, and 2) It doesn't handle whole months well. Consider Jan 1 - July 1. Instead of "6 months", it returns "5 months and 30 days". 3) Even then, it doesn't work well with leap years. Jan 1 - Mar 1 always returns "1 month and 28 days", even if it was in a leap year - where technically it should be "1 month and 29 days". – Matt Johnson-Pint Sep 26 '14 at 16:56
  • Shouldn't Jan. 1 to Mar. 1 always be "two months"? –  Sep 26 '14 at 17:14
  • Ideally, yes. I was just pointing out that the value your first pass returned wasn't accurate with regard to leap years. – Matt Johnson-Pint Sep 26 '14 at 17:20
  • 1
    The second try code assumes all months are 30 days, which isn't true. Trust me, this problem is more difficult than it seems. :) moment has it covered by good understanding of calendar math. There is indeed a pure JS solution, but it's not simple. – Matt Johnson-Pint Sep 26 '14 at 17:22
  • Actually... As I'm browsing through the moment docs, I am realizing that moment *doesn't* have this operation covered... I will research more and post an answer later. Thx. – Matt Johnson-Pint Sep 26 '14 at 17:32
0

This worked for me. Verified with Age calculator.

function calculateAge(){
    ageText = jQuery("#dob").closest(".form-group").find(".age-text");
    ageText.text("");
    level2.dob = jQuery("#dob").val();
    if(!level2.dob) return;
    level2.mdob= moment(level2.dob, 'DD-MM-YYYY');
    if(!level2.mdob.isValid()){
        alert("Invalid date format");
        return;
    }
    level2.targetDate = moment();//TODO: Fill in the target date
    level2.months = level2.targetDate.diff(level2.mdob, 'months'); // Calculate the months
    let years = parseInt(level2.months/12); // A year has 12 months irrespective or leap year or not
    let balanceMonths = level2.months%12; // The balance gives the number of months 
    let days;
    if(!balanceMonths){ // If no balance months, then the date selected lies in the same month
        months = 0; // so months = 0
        days = level2.targetDate.diff(level2.mdob, 'days'); // only the days difference
    }else{
    months = balanceMonths;
    dob_date = level2.mdob.date();
    target_month = level2.targetDate.month();
    construct_date = moment().month(target_month).date(dob_date);
    days = level2.targetDate.diff(construct_date, 'days')+1; // There might be one day missed out. Not sure on UTC
}

ageText = years +" years " + months+ " months " + days +" days";
}
Alaksandar Jesus Gene
  • 6,523
  • 12
  • 52
  • 83