Editor

Age Calculator in Python

Intermediate · Useful tools · about 45 minutes to build · 100 lines

Type a date of birth and get the exact age in years, months and days on any date, with the weekday of birth and the next birthday.

The program

Press Run and answer its questions in the box that appears under the code.

age_calculator.py
"""An age calculator: exact age in years, months and days from a date of birth."""
import calendar
from datetime import date, datetime
DATE_FORMAT = "%d-%m-%Y" # 16-06-2009
# Reading dates
def read_date(prompt, blank_is_today=False):
"""Ask for a date like 16-06-2009 (/ or . also work)."""
while True:
text = input(prompt).strip().replace("/", "-").replace(".", "-")
if text == "" and blank_is_today:
return date.today()
try:
return datetime.strptime(text, DATE_FORMAT).date()
except ValueError:
print(" Please type a real date like 16-06-2009.")
# Date arithmetic
def add_months(day, months):
"""Move a date forward by whole months; 31 Jan + 1 month gives 28 or 29 Feb."""
total = day.month - 1 + months
year, month = day.year + total // 12, total % 12 + 1
last_day = calendar.monthrange(year, month)[1]
return date(year, month, min(day.day, last_day))
def exact_age(born, on):
"""Return the age as (years, months, days) on the date `on`."""
months = (on.year - born.year) * 12 + (on.month - born.month)
if add_months(born, months) > on:
months -= 1 # This month's "birthday day" has not come yet
days = (on - add_months(born, months)).days
return months // 12, months % 12, days
def next_birthday(born, on):
"""Return the date of the next birthday, counting `on` itself."""
years = on.year - born.year
birthday = add_months(born, years * 12)
if birthday < on:
birthday = add_months(born, (years + 1) * 12)
return birthday
def plural(count, word):
"""Return '1 year' or '17 years'."""
return f"{count:,} {word}" + ("" if count == 1 else "s")
# Putting it together
def show_age(born, on):
"""Print the exact age and some facts about the dates."""
if born == on:
print(" That is the day of birth itself: 0 days old.")
return
years, months, days = exact_age(born, on)
print(f" Age: {plural(years, 'year')}, {plural(months, 'month')} "
f"and {plural(days, 'day')}")
total_days = (on - born).days
weeks, extra_days = divmod(total_days, 7)
print(f" That is {plural(total_days, 'day')} "
f"({plural(weeks, 'week')} and {plural(extra_days, 'day')}).")
print(f" Born on a {born.strftime('%A')}.")
birthday = next_birthday(born, on)
turning = birthday.year - born.year
if birthday == on:
print(f" Happy birthday. Turning {turning} today.")
else:
wait = plural((birthday - on).days, "day")
print(f" Next birthday: {birthday.strftime(DATE_FORMAT)}, "
f"a {birthday.strftime('%A')}, in {wait} (turning {turning}).")
def main():
"""Ask for dates and show ages until the user is done."""
print("Age calculator")
while True:
print()
born = read_date("Date of birth (DD-MM-YYYY): ")
on = read_date("Age on which date? (DD-MM-YYYY, Enter for today): ",
blank_is_today=True)
if born > on:
print(" The date of birth must come before the other date.")
else:
show_age(born, on)
again = input("Check another date? (y/n): ").strip().lower()
if again not in ("y", "yes"):
break
print("Goodbye.")
if __name__ == "__main__":
main()

Open it in the full editor to save it, share it and keep building.

Sample run

What the program printed, and what was typed (in yellow).

Age calculator

Date of birth (DD-MM-YYYY): 16-06-2009
Age on which date? (DD-MM-YYYY, Enter for today): 22-10-2026
  Age: 17 years, 4 months and 6 days
  That is 6,337 days (905 weeks and 2 days).
  Born on a Tuesday.
  Next birthday: 16-06-2027, a Wednesday, in 237 days (turning 18).
Check another date? (y/n): y

Date of birth (DD-MM-YYYY): 31-02-2010
  Please type a real date like 16-06-2009.
Date of birth (DD-MM-YYYY): 05/01/2001
Age on which date? (DD-MM-YYYY, Enter for today): 01-04-2026
  Age: 25 years, 2 months and 27 days
  That is 9,217 days (1,316 weeks and 5 days).
  Born on a Friday.
  Next birthday: 05-01-2027, a Tuesday, in 279 days (turning 26).
Check another date? (y/n): y

Date of birth (DD-MM-YYYY): 01-01-2030
Age on which date? (DD-MM-YYYY, Enter for today): 01-04-2026
  The date of birth must come before the other date.
Check another date? (y/n): n
Goodbye.

How it is built

1. Reading a date

datetime.strptime reads the text with the format %d-%m-%Y and raises ValueError for impossible dates such as 31-02-2010. Slashes and dots become dashes first, so 05/01/2001 works too.

def read_date(prompt, blank_is_today=False):
    """Ask for a date like 16-06-2009 (/ or . also work)."""
    while True:
        text = input(prompt).strip().replace("/", "-").replace(".", "-")
        if text == "" and blank_is_today:
            return date.today()
        try:
            return datetime.strptime(text, DATE_FORMAT).date()
        except ValueError:
            print("  Please type a real date like 16-06-2009.")

2. Adding months

Moving 31 January forward one month must land on the last day of February, so the day is capped with calendar.monthrange. This small function is the key to exact ages.

def add_months(day, months):
    """Move a date forward by whole months; 31 Jan + 1 month gives 28 or 29 Feb."""
    total = day.month - 1 + months
    year, month = day.year + total // 12, total % 12 + 1
    last_day = calendar.monthrange(year, month)[1]
    return date(year, month, min(day.day, last_day))

3. The exact age

Count the whole months between the dates, step back one if this month's birthday day has not come yet, then count the days left over. months // 12 and months % 12 split the months into years and months.

def exact_age(born, on):
    """Return the age as (years, months, days) on the date `on`."""
    months = (on.year - born.year) * 12 + (on.month - born.month)
    if add_months(born, months) > on:
        months -= 1  # This month's "birthday day" has not come yet
    days = (on - add_months(born, months)).days
    return months // 12, months % 12, days

4. The next birthday

This year's birthday is used if it has not passed, otherwise next year's. Going through add_months means a 29 February birthday falls on 28 February in other years.

def next_birthday(born, on):
    """Return the date of the next birthday, counting `on` itself."""
    years = on.year - born.year
    birthday = add_months(born, years * 12)
    if birthday < on:
        birthday = add_months(born, (years + 1) * 12)
    return birthday

5. Putting it together

Subtracting the dates gives the total days, and divmod(total_days, 7) splits them into weeks and days. strftime('%A') names the weekday.

def show_age(born, on):
    """Print the exact age and some facts about the dates."""
    if born == on:
        print("  That is the day of birth itself: 0 days old.")
        return
    years, months, days = exact_age(born, on)
    print(f"  Age: {plural(years, 'year')}, {plural(months, 'month')} "
          f"and {plural(days, 'day')}")
    total_days = (on - born).days
    weeks, extra_days = divmod(total_days, 7)
    print(f"  That is {plural(total_days, 'day')} "
          f"({plural(weeks, 'week')} and {plural(extra_days, 'day')}).")
    print(f"  Born on a {born.strftime('%A')}.")
    birthday = next_birthday(born, on)
    turning = birthday.year - born.year
    if birthday == on:
        print(f"  Happy birthday. Turning {turning} today.")
    else:
        wait = plural((birthday - on).days, "day")
        print(f"  Next birthday: {birthday.strftime(DATE_FORMAT)}, "
              f"a {birthday.strftime('%A')}, in {wait} (turning {turning}).")

Make it bigger

  • Compare two dates of birth and say who is older and by how much.
  • Count down the hours and minutes to the next birthday with datetime.now().
  • Read a list of names and birthdays and print them in birthday order.
  • Accept dates written as words, like 5 January 2001, with a second format.

What it practises