0

I have a string representing a birth date I want to know how old the person is in years and days.

e.g: birth = 07/04/1994 (month/days/years)

So the person is 25y 78d old

But so far, using datetime when I do substraction between dates the result it as number of days. And because of leap years I can never be sure of the exact number of days.


In [1]:
from datetime import date, datetime
today = date.today()

birth = '10/21/1996'
birth_date = datetime.strptime(datetime.strptime(birth, '%m/%d/%Y').strftime('%Y-%m-%d'),'%Y-%m-%d').date()
delta = today - birth_date
print(delta.days)

Out [1]:
8369
Community
  • 1
  • 1
Benoit Drogou
  • 969
  • 1
  • 5
  • 15
  • 1
    No. A year is not a well defined time period. – user2390182 Sep 20 '19 at 13:57
  • 2
    I think, maybe [pendulum](https://pendulum.eustace.io/) will be able to do that. Not too sure – han solo Sep 20 '19 at 13:58
  • yours is already quite simple! – Evhz Sep 20 '19 at 14:01
  • @schwobaseggl, a year is *very* well defined (at least on the Gregorian calendar). It may not be easy to turn a day count into an exact `y/d` set but that's only because you don't know the start (or end) date. Once you know that, you can get the exact `y/d` values. – paxdiablo Sep 20 '19 at 14:49
  • @paxdiablo a year may be well defined, its general length is not. Of those 54 years you count in your solution, some are longer than the others. There is a reason why `timedelta` does not accept keywords `months` or `years`. The second-exact meaning of the phrase "I am exactly 54 years old" depends on the moment of the utterance. – user2390182 Sep 20 '19 at 15:01
  • I will happily agree that the length of an arbitrary year ("a year") is not well defined (I *think* that's what you're trying to get across) but the length of a specific year (like "2007") is exactly defined. That's why I mentioned that the dates were important. Provided you specify the ground rules (like your age-in-years increases at the start of your birthday and remains consistent until start of your next birthday, and your extra-age-in-days resets to zero on birthday and ticks over each midnight), it's quite easy to do what the OP wants. – paxdiablo Sep 20 '19 at 15:11

3 Answers3

1

Not as simple as a built-in Python function, but certainly doable using the following logic:

  1. Construct a date based on the current year minus one, but the birth month and day. Call this lastYearBirthday. Also construct a date based on the current year but the birth month and day. Call this currYearBirthday.

  2. If currYearBirthday is after today (the birthday is yet to occur this year), the number of full years can be obtained by subtracting year(birthdate) from year(lastYearBirthday). The number of days over and above (days since last birthday) that is obtained with (today - lastYearBirthday).days.

  3. Otherwise this years birthday has already happened (or is happening today) and the number of full years can therefore be obtained by subtracting year(birthdate) from year(currYearBirthday) - the number of days over that is obtained with (today - currYearBirthday).days.

Turning that into a Python function you can use easily, we get:

from datetime import date

# Functions to return tuple of (fullYears, extraDays) for
# a given birth date.

def ageInYearsAndDays(birthDate):
    # Create relevant dates to ease task.

    today = date.today()
    lastYearBirthday = date(today.year - 1, birthDate.month, birthDate.day)
    currYearBirthday = date(today.year, birthDate.month, birthDate.day)

    # Work out years and days based on whether this years
    # birthday has happened. Basic idea is that years can
    # be calculated as difference between birth year and
    # year of most recent birthday. Days is the number of
    # days between most recent birthday and today.

    if currYearBirthday > today:
        years = lastYearBirthday.year - birthDate.year
        days = (today - lastYearBirthday).days
    else:
        years = currYearBirthday.year - birthDate.year
        days = (today - currYearBirthday).days

    return (years, days)

And some test code shows my own precarious position on this mortal coil:

(years, days) = ageInYearsAndDays(date(1965, 2, 2))
print(years, "years and", days, "days")

The output being (on the day this answer was posted) a rather depressing:

54 years and 230 days

:-)

Note that I just constructed my birthdate from year, month and day directly. Since you already know how to turn a string into one of those (as per your question), I didn't bother using that method.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
0

A user built this following code for another answer on stack that captures the leap year logic. With that said, you will need to refactor to fit your needs...

#Calculate the Days between Two Date

daysOfMonths = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

def isLeapYear(year):

    # Pseudo code for this algorithm is found at
    # http://en.wikipedia.org/wiki/Leap_year#Algorithm
    ## if (year is not divisible by 4) then (it is a common Year)
    #else if (year is not divisable by 100) then (ut us a leap year)
    #else if (year is not disible by 400) then (it is a common year)
    #else(it is aleap year)
    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0

def Count_Days(year1, month1, day1):
    if month1 ==2:
        if isLeapYear(year1):
            if day1 < daysOfMonths[month1-1]+1:
                return year1, month1, day1+1
            else:
                if month1 ==12:
                    return year1+1,1,1
                else:
                    return year1, month1 +1 , 1
        else: 
            if day1 < daysOfMonths[month1-1]:
                return year1, month1, day1+1
            else:
                if month1 ==12:
                    return year1+1,1,1
                else:
                    return year1, month1 +1 , 1
    else:
        if day1 < daysOfMonths[month1-1]:
             return year1, month1, day1+1
        else:
            if month1 ==12:
                return year1+1,1,1
            else:
                    return year1, month1 +1 , 1


def daysBetweenDates(y1, m1, d1, y2, m2, d2,end_day):

    if y1 > y2:
        m1,m2 = m2,m1
        y1,y2 = y2,y1
        d1,d2 = d2,d1
    days=0
    while(not(m1==m2 and y1==y2 and d1==d2)):
        y1,m1,d1 = Count_Days(y1,m1,d1)
        days+=1
    if end_day:
        days+=1
    return days


# Test Case

def test():
    test_cases = [((2012,1,1,2012,2,28,False), 58), 
                  ((2012,1,1,2012,3,1,False), 60),
                  ((2011,6,30,2012,6,30,False), 366),
                  ((2011,1,1,2012,8,8,False), 585 ),
                  ((1994,5,15,2019,8,31,False), 9239),
                  ((1999,3,24,2018,2,4,False), 6892),
                  ((1999,6,24,2018,8,4,False),6981),
                  ((1995,5,24,2018,12,15,False),8606),
                  ((1994,8,24,2019,12,15,True),9245),
                  ((2019,12,15,1994,8,24,True),9245),
                  ((2019,5,15,1994,10,24,True),8970),
                  ((1994,11,24,2019,8,15,True),9031)]

    for (args, answer) in test_cases:
        result = daysBetweenDates(*args)
        if result != answer:
            print "Test with data:", args, "failed"
        else:
            print "Test case passed!"

test()

How to calculate number of days between two given dates?

ParalysisByAnalysis
  • 703
  • 1
  • 4
  • 16
  • Doesn't this simply provide what you get from `(date - date).days` as per the OP's question? I think they wanted an accurate way to turn that into years and days. – paxdiablo Sep 20 '19 at 15:16
0

A lot of exercises from the good old days on stack overflow in September. This should illustrate you the logic. Note that a year is leap only when is perfectly divided by 4, 100 or 400. You can then make use of datetime attributes to have fun.

from datetime import date, datetime
today = date.today()

birth = '10/21/1996'
birth_date = datetime.strptime(datetime.strptime(
    birth, '%m/%d/%Y').strftime('%Y-%m-%d'), '%Y-%m-%d').date()
delta = today - birth_date

days = delta.days
year_counter = 0
if today.day >= birth_date.day and today.month >= birth_date.month:
    full_years = today.year
else:
    full_years = today.year - 1

for year in range(1996, full_years):
    if (year % 4) == 0 or (year % 100) == 0 or (year % 400) == 0:
        days -= 366
        year_counter += 1
    else:
        days -= 365
        year_counter += 1

print("years: " + str(year_counter) + "\ndays: " + str(days))

Obviously there are more Pythonic way to write it, but I suppose you wanted some readability.

sparaflAsh
  • 646
  • 1
  • 9
  • 26
  • Thanks for your answer, but it is not a logic test for university as you think. Just a personnal project where I don't want to "waste time" on this. I was looking for a package with built in functions. I think `Pendulum` might be the good one – Benoit Drogou Sep 20 '19 at 14:40
  • 1
    My bad. Probably [dateutil](https://pypi.org/project/python-dateutil/) is more useful then. Look into [relative delta](https://stackoverflow.com/questions/49071561/python-difference-in-years-between-a-datetime-now-and-a-series-filled-up-with) – sparaflAsh Sep 20 '19 at 14:59