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.
2
Answers
Your
diffDuration
is 30 years, 11 month and 29 days;.years()
gets 30 indeed, but to get the full duration, one has to also check.months()
-> 11,.days()
-> 29,.hours()
-> 0 ,.minutes()
-> 0,.seconds()
-> 0,.milliseconds()
-> 0.The following snippet shows that, using the properties of the object
diffDuration._data
, which are those returned by the functions mentioned above:If you add one day, i.e.,
currDate = moment([currYear, 1, 2])
, you’ll get the full 31 years:Now, why is a day missing? Note that when you compute
currDate.diff(birthDate)
, that’s just a number of milliseconds; you loose the actual start and end moments by constructing aDuration
object. It boils down to the count of leap years.There are 7 leap years in your interval of 31 years; but it could have been 8 leap years, e.g., 1994->2025. Since you don’t have the start and end dates, the system doesn’t know which one it is. Actually, the average number of leap years in any interval of 31 years is slightly less than 7.75 (31/4), so the library rounds it
up to the closest value, 8 (since it has to be either 7 or 8) and since it expects 8 leap years, it’s 31 years minus one day.
This is implemented in the source code in the function daysToMonths. For the interval you have, there are 11322 days converted to 371.9829975974866 months, slightly less than the required 372 months for 31 years.
The solution, if you want to just use
.years()
as you already have,is to just move the starting moment of the interval at least one day later, or several days later, you may even go to:
you can’t get more than the correct number of years.
The problem
From the moment.js docs:
Your issue stems from passing the result of
diff()
toduration()
, which doesn’t behave reliably for the reason given in the docs. The duration object itself is awfully close to a year:But when you extract the
'years'
key it just gives you the30
value in the duration object without rounding up.The solution
Just pass
'years'
as a second argument todiff()
, which sets the unit of measurement, and don’t bother withduration()
at all, like this:Which sets
age
to the desired31
.