I need to check if some number of years have been since some date. Currently I've got timedelta
from datetime
module and I don't know how to convert it to years.
-
5see this answer: http://stackoverflow.com/a/9754466/65387 – Adam Aug 13 '14 at 13:07
-
1related: [How to convert years to second](http://stackoverflow.com/a/32658742/4279) – jfs Dec 11 '15 at 15:29
21 Answers
You need more than a timedelta
to tell how many years have passed; you also need to know the beginning (or ending) date. (It's a leap year thing.)
Your best bet is to use the dateutil.relativedelta
object, but that's a 3rd party module. If you want to know the datetime
that was n
years from some date (defaulting to right now), you can do the following::
from dateutil.relativedelta import relativedelta
def yearsago(years, from_date=None):
if from_date is None:
from_date = datetime.now()
return from_date - relativedelta(years=years)
If you'd rather stick with the standard library, the answer is a little more complex::
from datetime import datetime
def yearsago(years, from_date=None):
if from_date is None:
from_date = datetime.now()
try:
return from_date.replace(year=from_date.year - years)
except ValueError:
# Must be 2/29!
assert from_date.month == 2 and from_date.day == 29 # can be removed
return from_date.replace(month=2, day=28,
year=from_date.year-years)
If it's 2/29, and 18 years ago there was no 2/29, this function will return 2/28. If you'd rather return 3/1, just change the last return
statement to read::
return from_date.replace(month=3, day=1,
year=from_date.year-years)
Your question originally said you wanted to know how many years it's been since some date. Assuming you want an integer number of years, you can guess based on 365.2425 days per year and then check using either of the yearsago
functions defined above::
def num_years(begin, end=None):
if end is None:
end = datetime.now()
num_years = int((end - begin).days / 365.2425)
if begin > yearsago(num_years, end):
return num_years - 1
else:
return num_years

- 1
- 11
- 47
- 78

- 11,672
- 5
- 39
- 38
-
37You can be fully accurate with 365.2425 (instead of 365.25), which takes the 400 year exception into account for the Gregorian calendar. – brianary Dec 24 '09 at 21:09
-
3Your function legally breaks for countries such as the UK and Hong Kong, because they "round up" to the 1st of March for leap years. – antihero Sep 04 '13 at 14:02
-
4@brianary: it won't be "fully accurate" e.g., [the difference introduced by the local timezone (`datetime.now()`) is greater than the difference between *average* Julian (`365.25`) and Gregorian (`365.2425`) years.](http://stackoverflow.com/a/32658742/4279). The correct approach is suggested in [@Adam Rosenfield's answer](http://stackoverflow.com/a/765862/4279) – jfs Dec 11 '15 at 15:38
-
3see also [this](http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time) and [this](http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time) Both are excellent lists of things that are not true about time. – gvoysey Aug 19 '16 at 18:28
-
`dateutil` seems to be a built-in package now (at least for my Python 3.9.6 from Miniconda) – Addison Klinke Jun 26 '22 at 03:54
-
just giving some thought about why I am not sure why timedelta can't do that tho, since datetime is indeed leap-year aware... `date = datetime.strptime("28.02.1981, 23:00:00","%d.%m.%Y, %H:%M:%S") print(date+timedelta(hours=1))` produces March 1st, while `date = datetime.strptime("28.02.1980, 23:00:00","%d.%m.%Y, %H:%M:%S") print(date+timedelta(hours=1))` produces Feb 29th --> correctly – EvilSmurf Nov 11 '22 at 11:04
If you're trying to check if someone is 18 years of age, using timedelta
will not work correctly on some edge cases because of leap years. For example, someone born on January 1, 2000, will turn 18 exactly 6575 days later on January 1, 2018 (5 leap years included), but someone born on January 1, 2001, will turn 18 exactly 6574 days later on January 1, 2019 (4 leap years included). Thus, you if someone is exactly 6574 days old, you can't determine if they are 17 or 18 without knowing a little more information about their birthdate.
The correct way to do this is to calculate the age directly from the dates, by subtracting the two years, and then subtracting one if the current month/day precedes the birth month/day.

- 390,455
- 97
- 512
- 589
-
2This is the way... unless you work on any sector that likes guesstimates: health, geology, etc. – Braiam May 31 '21 at 20:43
-
Subtracting just the years is such an elegant solution yet not very obvious. – xyres Jun 28 '21 at 08:12
-
First off, at the most detailed level, the problem can't be solved exactly. Years vary in length, and there isn't a clear "right choice" for year length.
That said, get the difference in whatever units are "natural" (probably seconds) and divide by the ratio between that and years. E.g.
delta_in_days / (365.25)
delta_in_seconds / (365.25*24*60*60)
...or whatever. Stay away from months, since they are even less well defined than years.

- 21,814
- 3
- 56
- 68
-
4That is NOT what anybody means or uses when it's a question of how many years service or has a person attained a particular age. – John Machin Nov 30 '09 at 11:57
-
5Your 365.25 should be 365.2425 to take the 400 year exception of the Gregorian calendar into account. – brianary Dec 24 '09 at 21:11
-
2Well, the problem *can* be solved correctly - you can tell ahead of time which years have leap days and leap seconds and all that. It's just that there's no extremely elegant way of doing it without subtracting the years, then the months, then the days, etc... in the two formatted dates – Litherum Jan 14 '11 at 19:00
Here's a updated DOB function, which calculates birthdays the same way humans do:
import datetime
import locale
# Source: https://en.wikipedia.org/wiki/February_29
PRE = [
'US',
'TW',
]
POST = [
'GB',
'HK',
]
def get_country():
code, _ = locale.getlocale()
try:
return code.split('_')[1]
except IndexError:
raise Exception('Country cannot be ascertained from locale.')
def get_leap_birthday(year):
country = get_country()
if country in PRE:
return datetime.date(year, 2, 28)
elif country in POST:
return datetime.date(year, 3, 1)
else:
raise Exception('It is unknown whether your country treats leap year '
+ 'birthdays as being on the 28th of February or '
+ 'the 1st of March. Please consult your country\'s '
+ 'legal code for in order to ascertain an answer.')
def age(dob):
today = datetime.date.today()
years = today.year - dob.year
try:
birthday = datetime.date(today.year, dob.month, dob.day)
except ValueError as e:
if dob.month == 2 and dob.day == 29:
birthday = get_leap_birthday(today.year)
else:
raise e
if today < birthday:
years -= 1
return years
print(age(datetime.date(1988, 2, 29)))

- 423
- 5
- 10
-
This breaks when dob is Feb. 29 and the current year is not a leap year. – Trey Hunner Mar 10 '13 at 03:26
Get the number of days, then divide by 365.2425 (the mean Gregorian year) for years. Divide by 30.436875 (the mean Gregorian month) for months.

- 8,996
- 2
- 35
- 29
def age(dob):
import datetime
today = datetime.date.today()
if today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
return today.year - dob.year - 1
else:
return today.year - dob.year
>>> import datetime
>>> datetime.date.today()
datetime.date(2009, 12, 1)
>>> age(datetime.date(2008, 11, 30))
1
>>> age(datetime.date(2008, 12, 1))
1
>>> age(datetime.date(2008, 12, 2))
0

- 50,179
- 34
- 152
- 186
-
A person born on 29 February will be treated as having attained age 1 on the following 28 February. – John Machin Nov 30 '09 at 12:09
-
Ok. Corrected to accommodate the 0.08% of the population born on the 29th by inverting the test from "is birthday after today" to "is birthday before today". Does that solve it? – John Mee Dec 01 '09 at 04:35
-
It does work correctly for your example!?! If I set "today" to 28th February 2009, and the date-of-birth to 29th February 2008 it returns Zero- at least for me; not 1 as you suggest (Python 2.5.2). There's no need to rude Mr Machin. Exactly what two dates are you having trouble with? – John Mee Feb 08 '10 at 06:16
-
I'll try again: A person born on 29 February will be treated by most people for most legal purposes as having attained age 1 on the following 28 February. Your code produces 0, both before and after your "correction". In fact, the two versions of your code produce EXACTLY the same output for ALL 9 input possibilities (month < == > X day < == >). BTW, 100.0/(4*365+1) produces 0.068, not 0.08. – John Machin Feb 08 '10 at 22:39
-
Oh, well there you go. I didn't know the legalities and you failed to make them unambiguous. Sounds like it was better off the first time around. Unfortunately I'm not able to determine what your 9 scenarios are from the comment. But don't bother to explain; it is certainly time for you to share with us your solution; one that works the way you like. – John Mee Feb 08 '10 at 23:00
-
2Sigh. (0) Addressing 29 February issues is essential in any date arithmetic; you just ignored it. (1) Your code wasn't better off first time; what don't you understand in "produce EXACTLY the same output"? (2) Three possible atomic results (<, ==, >) comparing today.month and dob.month; three possible atomic results comparing today.day and dob.day; 3 * 3 == 9 (3) http://stackoverflow.com/questions/2217488/age-from-birthdate-in-python/2218654 – John Machin Feb 08 '10 at 23:54
` Simple solution!
import datetime as dt
from dateutil.relativedelta import relativedelta
dt1 = dt.datetime(1990,2,1)
dt2 = dt.datetime(2021,5,16)
out = relativedelta(dt2, dt1)
print(f'Complete: {out}')
print(f'years:{out.years}, months:{out.months}, days:{out.days}') `
Complete: relativedelta(years=+31, months=+3, days=+15)
years:31, months:3, days:15

- 51
- 1
- 1
How exact do you need it to be? td.days / 365.25
will get you pretty close, if you're worried about leap years.

- 39,140
- 13
- 95
- 92
-
I'm really worried about leap years. It should check if person is over 18 years old. – Migol Apr 19 '09 at 18:08
-
Then there's no easy one-liner, you're going to have to parse the two dates and figured out if the person has passed their 18th birthday or not. – eduffy Apr 19 '09 at 18:13
Late to the party, but this will give you the age (in years) accurately and easily:
b = birthday
today = datetime.datetime.today()
age = today.year - b.year + (today.month - b.month > 0 or
(today.month == b.month > 0 and
today.day - b.day > 0))

- 323
- 3
- 8
This method is a hack of the datetime library:
def calculate_age(birthday_date, fmt="%Y-%m-%d"):
birthday = datetime.datetime.strptime(birthday_date, fmt)
age = datetime.datetime.now() - birthday
# first datime valid is (1, 1, 1), I use (1, 12, 31) => (2, 0, 0) to hack the lib
age = (datetime.datetime(1, 12, 31) + age)
return age.year - 2
This solution is odd, so I shared it with you. It's probably not the most elegant function.
now : 2019-09-21
print(calculate_age("2019-09-21")) => 2 (Done)
-> age + (1,12,31) = 0004-01-01 23:42:17.767031
print(calculate_age("2020-09-21")) => 0 (Undone)
-> age + (1,12,31) = 0002-12-31 23:46:39.144091
The difference is one day. I understand the reason for the difference is due to the bisextile (leap) year.
To correct this undesired behaviour, you need to use the birthday year to add the result.
Yet another 3rd party lib not mentioned here is mxDateTime (predecessor of both python datetime
and 3rd party timeutil
) could be used for this task.
The aforementioned yearsago
would be:
from mx.DateTime import now, RelativeDateTime
def years_ago(years, from_date=None):
if from_date == None:
from_date = now()
return from_date-RelativeDateTime(years=years)
First parameter is expected to be a DateTime
instance.
To convert ordinary datetime
to DateTime
you could use this for 1 second precision):
def DT_from_dt_s(t):
return DT.DateTimeFromTicks(time.mktime(t.timetuple()))
or this for 1 microsecond precision:
def DT_from_dt_u(t):
return DT.DateTime(t.year, t.month, t.day, t.hour,
t.minute, t.second + t.microsecond * 1e-6)
And yes, adding the dependency for this single task in question would definitely be an overkill compared even with using timeutil (suggested by Rick Copeland).

- 31,947
- 10
- 111
- 111
In the end what you have is a maths issue. If every 4 years we have an extra day lets then dived the timedelta in days, not by 365 but 365*4 + 1, that would give you the amount of 4 years. Then divide it again by 4. timedelta / ((365*4) +1) / 4 = timedelta * 4 / (365*4 +1)

- 185
- 1
- 2
- 7
-
1The leap year thing does not apply when the years are divisible by 100, except when they are divisible by 400. So, for year 2000: - it is divisible by four, so it should be leap but... - it is also divisible by a hundred, so it should not be leap but... - it is divisible by 400, so it was actually a leap year. For 1900: - it is divisible by 4 so it should be leap. - it is divisible by 100, so it should not be leap. - it is NOT divisible by 400, so this rule does not apply. 1900 was not a leap year. – Jblasco Sep 24 '13 at 10:44
This is the solution I worked out, I hope can help ;-)
def menor_edad_legal(birthday):
""" returns true if aged<18 in days """
try:
today = time.localtime()
fa_divuit_anys=date(year=today.tm_year-18, month=today.tm_mon, day=today.tm_mday)
if birthday>fa_divuit_anys:
return True
else:
return False
except Exception, ex_edad:
logging.error('Error menor de edad: %s' % ex_edad)
return True

- 812
- 6
- 7
For my purposes since subtraction of two datetime
objects (at least in Python 3.8) converts to a timedelta
with only a days
attribute:
>>> admit = datetime.strptime('20130530', '%Y%m%d')
>>> birth = datetime.strptime('20010621', '%Y%m%d')
>>> age = (admit - birth).days/365.2425
>>> age
11.940012457476882

- 1,651
- 2
- 15
- 28
Even though this thread is already dead, might i suggest a working solution for this very same problem i was facing. Here it is (date is a string in the format dd-mm-yyyy):
def validatedate(date):
parts = date.strip().split('-')
if len(parts) == 3 and False not in [x.isdigit() for x in parts]:
birth = datetime.date(int(parts[2]), int(parts[1]), int(parts[0]))
today = datetime.date.today()
b = (birth.year * 10000) + (birth.month * 100) + (birth.day)
t = (today.year * 10000) + (today.month * 100) + (today.day)
if (t - 18 * 10000) >= b:
return True
return False
this function returns the difference in years between two dates (taken as strings in ISO format, but it can easily modified to take in any format)
import time
def years(earlydateiso, laterdateiso):
"""difference in years between two dates in ISO format"""
ed = time.strptime(earlydateiso, "%Y-%m-%d")
ld = time.strptime(laterdateiso, "%Y-%m-%d")
#switch dates if needed
if ld < ed:
ld, ed = ed, ld
res = ld[0] - ed [0]
if res > 0:
if ld[1]< ed[1]:
res -= 1
elif ld[1] == ed[1]:
if ld[2]< ed[2]:
res -= 1
return res

- 683
- 1
- 8
- 17
I'll suggest Pyfdate
What is pyfdate?
Given Python's goal to be a powerful and easy-to-use scripting language, its features for working with dates and times are not as user-friendly as they should be. The purpose of pyfdate is to remedy that situation by providing features for working with dates and times that are as powerful and easy-to-use as the rest of Python.
the tutorial

- 2,664
- 3
- 18
- 7
import datetime
def check_if_old_enough(years_needed, old_date):
limit_date = datetime.date(old_date.year + years_needed, old_date.month, old_date.day)
today = datetime.datetime.now().date()
old_enough = False
if limit_date <= today:
old_enough = True
return old_enough
def test_ages():
years_needed = 30
born_date_Logan = datetime.datetime(1988, 3, 5)
if check_if_old_enough(years_needed, born_date_Logan):
print("Logan is old enough")
else:
print("Logan is not old enough")
born_date_Jessica = datetime.datetime(1997, 3, 6)
if check_if_old_enough(years_needed, born_date_Jessica):
print("Jessica is old enough")
else:
print("Jessica is not old enough")
test_ages()
This is the code that the Carrousel operator was running in Logan's Run film ;)

- 255
- 3
- 6
I came across this question and found Adams answer the most helpful https://stackoverflow.com/a/765862/2964689
But there was no python example of his method but here's what I ended up using.
input: datetime object
output: integer age in whole years
def age(birthday):
birthday = birthday.date()
today = date.today()
years = today.year - birthday.year
if (today.month < birthday.month or
(today.month == birthday.month and today.day < birthday.day)):
years = years - 1
return years

- 454
- 4
- 18
I liked John Mee's solution for its simplicity, and I am not that concerned about how, on Feb 28 or March 1 when it is not a leap year, to determine age of people born on Feb 29. But here is a tweak of his code which I think addresses the complaints:
def age(dob):
import datetime
today = datetime.date.today()
age = today.year - dob.year
if ( today.month == dob.month == 2 and
today.day == 28 and dob.day == 29 ):
pass
elif today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
age -= 1
return age

- 517
- 5
- 11
I would use datetime.date
data type instead, as it is simpler when it comes to checking how many years, months and days have passed:
now = date.today()
birthday = date(1993, 4, 4)
print("you are", now.year - birthday.year, "years,", now.month - birthday.month, "months and",
now.day - birthday.day, "days old")
Output:
you are 27 years, 7 months and 11 days old
I use timedelta
when I need to perform arithmetic on a specific date:
age = now - birthday
print("addition of days to a date: ", birthday + timedelta(days=age.days))
Output:
addition of days to a date: 2020-11-15

- 167
- 4
- 15