Know some Python? Write your own interactive tutorial, with runnable examples and exercises, and share it with a link. Write and share your own tutorial.

Start writing

Learn Python / Dates and Times

Dates and Times

Birthdays, deadlines, timestamps, opening hours: dates and times are everywhere. Python's built-in datetime module handles them, including the tricky parts like month lengths and leap years.

The main types

The datetime module has a few classes you'll use all the time:

Type Holds Example
date year, month, day 2026-10-05
time hour, minute, second 14:30:00
datetime date and time 2026-10-05 14:30:00
timedelta a duration 3 days, 4 hours

One naming trap: the module is called datetime, and so is one of the types inside it. That's why you'll see from datetime import datetime, which means "from the datetime module, import the datetime type". If you write import datetime instead, you need the longer datetime.datetime.now() and datetime.date.today(). This lesson uses the from ... import style throughout.

Today and now

from datetime import date, datetime

today = date.today()
now = datetime.now()

print(today)
print(now)
print(today.year, today.month, today.day)
print(now.hour, now.minute)
Output

Your output depends on when, and in which time zone, you run it. Notice that today() belongs to date and now() belongs to datetime. .year, .month, .day, .hour and .minute are plain numbers (no parentheses), so you can use them in calculations.

Going deeper: Time zones optional

datetime.now() gives a naive datetime: it has no idea which time zone it's in (tzinfo is None). That's fine for a program that only runs in one place. Once you deal with users or servers in different places, use aware datetimes, which carry their time zone with them:

from datetime import datetime, timezone, timedelta

# None: naive
local = datetime.now()
print(local.tzinfo)

# ends in +00:00
utc_now = datetime.now(timezone.utc)
print(utc_now)

# the same moment, Indian time
india = timezone(timedelta(hours=5, minutes=30))
print(utc_now.astimezone(india))
Output

A common rule is to store times in UTC and convert to the user's time zone only when you display them.

Fixed offsets like +05:30 don't know about daylight saving time. For real place names such as "Europe/London", use ZoneInfo from the zoneinfo module on your own computer: datetime.now(ZoneInfo("Europe/London")).

Creating a specific date

from datetime import date, datetime

moon_landing = date(1969, 7, 20)
meeting = datetime(2026, 3, 14, 15, 30)

print(moon_landing)
print(meeting)

# Monday is 0, Sunday is 6
print(moon_landing.weekday())

# Monday is 1, Sunday is 7
print(moon_landing.isoweekday())
Output

The arguments always go from biggest to smallest: year, month, day, then (for a datetime) hour and minute. datetime(2026, 3, 14, 15, 30) is 14 March 2026 at 3:30 pm. Write the numbers without leading zeros: date(2026, 03, 14) is a SyntaxError. The moon landing was on a Sunday, so weekday() gives 6.

Python checks that dates are real:

from datetime import date

date(2026, 2, 30)
Output

There's no 30 February, so this is a ValueError: "day is out of range for month".

Formatting dates with strftime

strftime() turns a date into text using format codes:

from datetime import datetime

moment = datetime(2026, 3, 14, 15, 9, 26)

print(moment.strftime("%d/%m/%Y"))
print(moment.strftime("%A, %d %B %Y"))
print(moment.strftime("%I:%M %p"))
print(moment.strftime("%Y-%m-%d %H:%M:%S"))
# format codes work in f-strings too
print(f"{moment:%b %d, %Y}")
Output

strftime() gives back a string. The format is a template: each % code is replaced by part of the date, and everything else (/, ,, spaces, words) is copied as it is. So "%d/%m/%Y" gives 14/03/2026.

The most useful codes:

Code Meaning Example
%Y 4-digit year 2026
%m month number 03
%d day of month 14
%B / %b month name / short March / Mar
%A / %a weekday name / short Saturday / Sat
%H hour (24-hour) 15
%I hour (12-hour) 03
%M minute 09
%S second 26
%p AM or PM PM

Watch the capitals: %m is the month and %M is the minute; %d is the day and %H is the hour. Mixing up %m and %M is the most common formatting bug.

Parsing text into dates with strptime

strptime() does the opposite: it reads a date from text, using the same codes to describe the layout:

from datetime import datetime

text = "25/12/2026 18:30"
christmas_dinner = datetime.strptime(text, "%d/%m/%Y %H:%M")

print(christmas_dinner)
print(christmas_dinner.strftime("%A"))
Output

The format must describe the text exactly, separators included: / where the text has /, a space where it has a space. A handy way to remember which is which:

Method Think Direction
strftime() f for format date → text
strptime() p for parse text → date

If the text doesn't match the format, you get a ValueError. The ISO format YYYY-MM-DD has a shortcut:

from datetime import date

print(date.fromisoformat("2026-07-04"))
Output

Date arithmetic with timedelta

A timedelta is a length of time. Add it to or subtract it from dates:

from datetime import date, timedelta

start = date(2026, 1, 28)

print(start + timedelta(days=7))
print(start + timedelta(weeks=2))
print(start - timedelta(days=30))
Output

Month lengths and leap years are handled for you. Notice how adding a week to January 28 lands on February 4.

timedelta accepts weeks, days, hours, minutes and seconds, but not months or years, because they don't have a fixed length (a month can be 28 to 31 days). To move by months, build a new date, for example start.replace(month=3).

Subtracting two dates gives a timedelta:

from datetime import date

start = date(2026, 1, 1)
end = date(2026, 12, 25)

gap = end - start
print(gap)
print(gap.days, "days")
Output

Printing a timedelta shows days plus hours:minutes:seconds, here 358 days, 0:00:00. .days gives just the number of days, 358, as a plain number you can calculate with.

With datetime values, durations can include hours and minutes:

from datetime import datetime

check_in = datetime(2026, 5, 1, 14, 0)
check_out = datetime(2026, 5, 4, 11, 30)

stay = check_out - check_in
print(stay)
print(stay.total_seconds() / 3600, "hours")
Output

The stay prints as 2 days, 21:30:00. To get it in hours, total_seconds() converts the whole duration to seconds, and dividing by 3600 (seconds in an hour) gives 69.5. Don't use stay.seconds for this: it holds only the leftover part after the whole days (the 21:30:00), not the total.

Comparing dates

Dates compare naturally with <, > and ==, and they sort correctly:

from datetime import date

deadline = date(2026, 6, 30)
submitted = date(2026, 7, 2)

if submitted > deadline:
    late = (submitted - deadline).days
    print(f"Late by {late} days")

dates = [date(2026, 5, 1), date(2025, 12, 31), date(2026, 1, 15)]
print(sorted(dates))
Output

Timing your code

To measure how long something takes, subtract two datetime.now() values, or use time.perf_counter():

import time

start = time.perf_counter()
total = sum(i * i for i in range(1_000_000))
elapsed = time.perf_counter() - start

print(f"Result {total:,} in {elapsed:.3f} seconds")
Output

Exercises

Exercise 1: Day of the week

Print the day of the week you were born, or of any date you like, as a full name such as Thursday.

from datetime import date

birthday = date(2000, 1, 1)
Output

Exercise 2: Due date

A library book is borrowed on 2026-03-25 and is due 21 days later. Print the due date as Due: Wednesday 15 April 2026.

from datetime import date, timedelta

borrowed = date(2026, 3, 25)
Output

Exercise 3: Days until an event

Parse the event date from the string and print how many days there are between today and the event: Days to go: 81.

from datetime import date, datetime

today = date(2026, 10, 5)
event_text = "December 25, 2026"
Output

Exercise 4: Age calculator

Write age_on(birth, on_date) that returns a person's age in whole years on a given date. Remember: if their birthday hasn't happened yet that year, subtract one.

from datetime import date

def age_on(birth, on_date):
    pass

print(age_on(date(1990, 8, 15), date(2026, 8, 14)))   # 35
print(age_on(date(1990, 8, 15), date(2026, 8, 15)))   # 36
Output

Date and time tools at a glance

A quick reference for later. The examples use d = date(2026, 3, 15) and from datetime import date, datetime, timedelta. Results marked "e.g." depend on when you run them. The strftime codes, such as %d and %B, are in the table earlier in this lesson.

Tool What it does Example Result
date.today() Today's date date.today() e.g. 2026-10-06
datetime.now() The current date and time datetime.now() e.g. 2026-10-06 14:30:05.123456
date(y, m, d) A specific date date(2026, 3, 15) 2026-03-15
datetime(y, m, d, h, min) A specific date and time datetime(2026, 3, 15, 9, 30) 2026-03-15 09:30:00
.year, .month, .day The parts of a date d.month 3
.weekday() Day of the week: Monday is 0, Sunday is 6 d.weekday() 6
.strftime(format) Formats a date as text d.strftime("%d %b %Y") '15 Mar 2026'
datetime.strptime(text, format) Reads a date from text datetime.strptime("15/03/2026", "%d/%m/%Y") 2026-03-15 00:00:00
.isoformat() The standard YYYY-MM-DD text form d.isoformat() '2026-03-15'
date.fromisoformat(text) Reads a YYYY-MM-DD date date.fromisoformat("2026-03-15") 2026-03-15
timedelta(...) A length of time: days, hours, minutes, seconds, weeks d + timedelta(days=30) 2026-04-14
date2 - date1 The time between two dates, as a timedelta (date(2026, 12, 25) - d).days 285
.total_seconds() A timedelta in seconds timedelta(hours=1, minutes=30).total_seconds() 5400.0
.replace(...) A copy with some parts changed d.replace(year=2027) 2027-03-15
time.perf_counter() A precise clock for timing code (import time) time.perf_counter() e.g. 1234.5678

Summary

  • Import what you need: from datetime import date, datetime, timedelta.
  • date.today() and datetime.now() give the current date and time.
  • strftime() formats a date as text, and strptime() parses text into a date. Both use codes like %Y, %m and %d.
  • Add or subtract a timedelta to move dates. Subtracting two dates gives a timedelta.
  • Dates compare and sort naturally.

Share this tutorial

Know something worth teaching? Write your own tutorial with runnable examples and share it.