Convert timedelta to years?

PythonDatetimeTimedelta

Python Problem Overview


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.

Python Solutions


Solution 1 - Python

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

Solution 2 - Python

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.

Solution 3 - Python

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.

Solution 4 - Python

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)))

Solution 5 - Python

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.

Solution 6 - Python

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

Solution 7 - Python

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

Solution 8 - Python

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).

Solution 9 - Python

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 3654 + 1, that would give you the amount of 4 years. Then divide it again by 4. timedelta / ((3654) +1) / 4 = timedelta * 4 / (365*4 +1)

Solution 10 - Python

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

Solution 11 - Python

` 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

Solution 12 - Python

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.

Solution 13 - Python

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

Solution 14 - Python

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

Solution 15 - Python

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

Solution 16 - Python

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 ;)

https://en.wikipedia.org/wiki/Logan%27s_Run_(film)

Solution 17 - Python

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

Solution 18 - Python

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

Solution 19 - Python

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

Solution 20 - Python

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))

Solution 21 - Python

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMigolView Question on Stackoverflow
Solution 1 - PythonRick CopelandView Answer on Stackoverflow
Solution 2 - PythonAdam RosenfieldView Answer on Stackoverflow
Solution 3 - PythonMarkusQView Answer on Stackoverflow
Solution 4 - PythonantiheroView Answer on Stackoverflow
Solution 5 - PythonbrianaryView Answer on Stackoverflow
Solution 6 - PythoneduffyView Answer on Stackoverflow
Solution 7 - PythonJohn MeeView Answer on Stackoverflow
Solution 8 - PythonAntony HatchkinsView Answer on Stackoverflow
Solution 9 - PythonNorbertoView Answer on Stackoverflow
Solution 10 - PythonpvilasView Answer on Stackoverflow
Solution 11 - PythonSajidView Answer on Stackoverflow
Solution 12 - PythonlbontronView Answer on Stackoverflow
Solution 13 - PythonView Answer on Stackoverflow
Solution 14 - PythonMauro BianchiView Answer on Stackoverflow
Solution 15 - PythontwilsView Answer on Stackoverflow
Solution 16 - PythonAndres HurtisView Answer on Stackoverflow
Solution 17 - PythonSamView Answer on Stackoverflow
Solution 18 - PythonRick GravesView Answer on Stackoverflow
Solution 19 - PythonSebastian MeckovskiView Answer on Stackoverflow
Solution 20 - PythonHebe HilhorstView Answer on Stackoverflow
Solution 21 - PythonjimhView Answer on Stackoverflow