I want to calculate the age of a person given the date of birth and the current date in years, months and days relative to the current date.
For example:
<
Here's a way to do it if you're willing to violate the principle of "I should be an integer number of years old on my birthday". Note that that principle isn't strictly true, due to leap years, so the following is actually more accurate, though perhaps less intuitive. If you want to know the actual exact number of years old someone is, this is the way I would do it.
First convert both the birthdate and the current time to epoch time (number of seconds since the dawn of time, ie, 1970 in unix land) and subtract them. See, eg, this question for how to do that: How do I convert a date/time to epoch time (aka unix time -- seconds since 1970) in Perl?. You now have the person's exact age in seconds and need to convert that to a string like "36 years, 3 months, 8 days".
Here's pseudocode to do it. It should be straightforward to remove the weeks or hours or whatever parts you don't want...
daysInYear = 365.2425; # Average lengh of a year in the gregorian calendar.
# Another candidate for len of a year is a sidereal year,
# 365.2564 days. cf. http://en.wikipedia.org/wiki/Year
weeksInYear = daysInYear / 7;
daysInMonth = daysInYear / 12;
weeksInMonth = daysInMonth / 7;
sS = 1;
mS = 60*sS;
hS = 60*mS;
dS = 24*hS;
wS = 7*dS;
oS = daysInMonth*dS;
yS = daysInYear*dS;
# Convert a number of seconds to years,months,weeks,days,hrs,mins,secs.
seconds2str[secs]
{
local variables:
y, yQ= False, o, oQ= False, w, wQ= False,
d, dQ= False, h, hQ= False, m, mQ= False, s= secs;
if(secs<0) return "-" + seconds2str(-secs); # "+" is string concatenation.
y = floor(s/yS);
if(y>0) yQ = oQ = wQ = dQ = hQ = mQ = True;
s -= y * yS;
o = floor(s/oS);
if(o>0) oQ = wQ = dQ = hQ = mQ = True;
s -= o * oS;
w = floor(s/wS);
if(w>0) wQ = dQ = hQ = mQ = True;
s -= w * wS;
d = floor(s/dS);
if(d>0) dQ = hQ = mQ = True;
s -= d * dS;
h = floor(s/hS);
if(h>0) hQ = mQ = True;
s -= h * hS;
m = floor(s/mS);
if(m>0) mQ = True;
s -= m * mS;
return
(yQ ? y + " year" + maybeS(y) + " " : "") +
(oQ ? o + " month" + maybeS(o) + " " : "") +
(wQ ? w + " week" + maybeS(w) + " " : "") +
(dQ ? d + " day" + maybeS(d) + " " : "") +
(hQ ? dd(h) + ":" : "") +
(mQ ? dd(m) + ":" + dd(round(s)) + "s" : s + "s");
}
# Returns an "s" if n!=1 and "" otherwise.
maybeS(n) { return (n==1 ? "" : "s"); }
# Double-digit: takes a number from 0-99 and returns it as a 2-character string.
dd(n) { return (n<10 ? "0" + tostring(n) : tostring(n)); }
Since years, months and even days can have uneven lengths, you can't start by subtracting the two dates to get a simple duration. If you want the result to match up with how human beings treat dates, you instead have to work with real dates, using your language's built in functions that understand dates better than you ever will.
How that algorithm is written depends on the language you use, because each language has a different set of functions. My own implementation in Java, using the awkward but technically correct GregorianCalendar:
Term difference (Date from, Date to) {
GregorianCalendar cal1 = new GregorianCalendar();
cal1.setTimeInMillis(from.getTime());
GregorianCalendar cal2 = new GregorianCalendar();
cal2.setTimeInMillis(to.getTime());
int years = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR);
int months = cal2.get(Calendar.MONTH) - cal1.get(Calendar.MONTH);
int days = cal2.get(Calendar.DAY_OF_MONTH) - cal1.get(Calendar.DAY_OF_MONTH);
if (days < 0) {
months--;
days += cal1.getActualMaximum(Calendar.DAY_OF_MONTH);
}
if (months < 0) {
years--;
months += 12;
}
return new Term(years, months, days);
}
This may not be perfect, but it delivers human-readable results. Term is a class of my own for storing human-style periods, since Java's date libraries don't have one.
For future projects I plan to move to the much better Joda date/time library, which does have a Period class in it, and will probably have to rewrite this function.
Here is this using the borrowing rule of mathematics.
DateTime dateOfBirth = new DateTime(2000, 4, 18);
DateTime currentDate = DateTime.Now;
int ageInYears = 0;
int ageInMonths = 0;
int ageInDays = 0;
ageInDays = currentDate.Day - dateOfBirth.Day;
ageInMonths = currentDate.Month - dateOfBirth.Month;
ageInYears = currentDate.Year - dateOfBirth.Year;
if (ageInDays < 0)
{
ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
ageInMonths = ageInMonths--;
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
}
if (ageInMonths < 0)
{
ageInMonths += 12;
ageInYears--;
}
Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays);
That is not an easy question, since above days (if we don't take into account leap-seconds) there are not easy formulas.
Months can consist of 28, 29, 30, or 31 days; years can be 365 or 366 days. Therefore, problems arise when you try to calculate full units of time for months and years.
Here is an interesting article that takes into account all the complex aspects to solve your question in Java.
I assume the questions is broader than only one programming language. If you are using C#, you can use DateTimeOffset to calculate this.
var offset = DateTimeOffset.MinValue;
var lastDate = DateTime.Today;
var firstDate = new DateTime(2006,11,19);
var diff = (lastDate- firstDate);
var resultWithOffset = DateTimeOffset.MinValue.AddDays(diff.TotalDays);
var result = resultWithOffset.AddDays(-offset.Day).AddMonths(-offset.Month).AddYears(-offset.Year);
result.Day
, result.Month
, result.Year
will hold the information you need.