javascriptmomentjs

year calculation issue using moment library when setting age as a number


I am asking end user to put numerical age of the user. For example user inputs 31.

Behind the scene I convert this to the year and store it. The logic is below to convert:

 (moment().subtract(age, 'years')).year();

Which returns the value 1993

Good so far. However, in the other part of the UI where i show this age as number it shows 30 rather 31. The code that converts it back is like below:

 let birthYear =  this.userData.profile.birthYear
     let currYear = moment().year()
     let currDate = moment([currYear, 1, 1])
     let birthDate = moment([birthYear, 1, 1])
     var diffDuration = moment.duration(currDate.diff(birthDate));
     age = diffDuration.years()

I am lost why the age year becomes 30 rather 31.


Solution

  • The problem

    From the moment.js docs:

    Durations do not have a defined beginning and end date. They are contextless.
    A duration is conceptually more similar to '2 hours' than to 'between 2 and 4 pm today'. As such, they are not a good solution to converting between units that depend on context.
    For example, a year can be defined as 366 days, 365 days, 365.25 days, 12 months, or 52 weeks.

    Your issue stems from passing the result of diff() to duration(), which doesn't behave reliably for the reason given in the docs. The duration object itself is awfully close to a year:

      _data: {
        milliseconds: 0,
        seconds: 0,
        minutes: 0,
        hours: 0,
        days: 29,
        months: 11,
        years: 30
      },
    

    But when you extract the 'years' key it just gives you the 30 value in the duration object without rounding up.

    The solution

    Just pass 'years' as a second argument to diff(), which sets the unit of measurement, and don't bother with duration() at all, like this:

    let currYear = moment().year()
    let currDate = moment([currYear, 1, 1])
    let birthDate = moment([birthYear, 1, 1])
    
    age = currDate.diff(birthDate, 'years');
    

    Which sets age to the desired 31.