Working with dates and times is one of those tasks that looks simple but isn’t. Time zones, leap years, daylight saving — they all complicate the picture. The datetime module gives you the right tools.

The three main classes

  • date — a calendar date (year, month, day). No time, no timezone.
  • time — a wall-clock time (hour, minute, second). No date.
  • datetime — both together.
from datetime import date, time, datetime

today: date = date.today()
print(today)             # 2026-05-11

now: datetime = datetime.now()
print(now)               # 2026-05-11 09:23:45.123456

morning: time = time(9, 30, 0)
print(morning)           # 09:30:00

Creating specific dates and times

d: date = date(2026, 1, 15)
t: time = time(14, 30, 0)
dt: datetime = datetime(2026, 1, 15, 14, 30, 0)

You can pull pieces out:

dt = datetime(2026, 5, 11, 9, 23, 45)
print(dt.year, dt.month, dt.day)         # 2026 5 11
print(dt.hour, dt.minute, dt.second)     # 9 23 45
print(dt.weekday())                       # 0=Mon ... 6=Sun

Durations — timedelta

A timedelta represents a span of time. You get one by subtracting two datetimes, or by creating one directly:

from datetime import timedelta

a: datetime = datetime(2026, 1, 1)
b: datetime = datetime(2026, 5, 11)

diff: timedelta = b - a
print(diff)                # 130 days, 0:00:00
print(diff.days)           # 130
print(diff.total_seconds())   # 11232000.0

You can create timedeltas explicitly:

one_week: timedelta = timedelta(days=7)
one_hour: timedelta = timedelta(hours=1)
fifteen_min: timedelta = timedelta(minutes=15)

next_week: datetime = datetime.now() + one_week

Adding a timedelta to a datetime gives you a new datetime. Subtracting two datetimes gives you a timedelta. That’s the algebra.

Formatting — strftime

To turn a datetime into a string:

now: datetime = datetime.now()

print(now.strftime("%Y-%m-%d"))           # 2026-05-11
print(now.strftime("%Y-%m-%d %H:%M:%S"))  # 2026-05-11 09:23:45
print(now.strftime("%A, %B %d %Y"))       # Monday, May 11 2026
print(now.strftime("%I:%M %p"))           # 09:23 AM

A few of the most-used codes:

CodeMeaningExample
%Y4-digit year2026
%m2-digit month05
%d2-digit day11
%H24-hour hour14
%I12-hour hour02
%Mminute30
%Ssecond45
%Aweekday nameMonday
%Bmonth nameMay
%pAM/PMPM

You don’t have to memorise these — look them up when needed. The strftime mini-language is the same in many languages.

Parsing — strptime

The reverse — turn a string into a datetime:

text: str = "2026-05-11 09:23:45"
dt: datetime = datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
print(dt)        # 2026-05-11 09:23:45
print(type(dt))   # <class 'datetime.datetime'>

The format string tells strptime how to interpret each part. If the format doesn’t match, you get a ValueError.

ISO 8601 — the international standard

ISO 8601 is the universal format: 2026-05-11T09:23:45. Python has shortcuts:

# write
dt: datetime = datetime.now()
print(dt.isoformat())         # '2026-05-11T09:23:45.123456'

# read
dt: datetime = datetime.fromisoformat("2026-05-11T09:23:45")
print(dt)

When storing timestamps in databases, logs, or JSON, always use ISO 8601. It’s unambiguous and sorts correctly as a string.

Time zones

Naive datetimes (without timezone info) can lead to bugs when your code runs in different parts of the world. Python 3.9+ has the zoneinfo module:

from datetime import datetime
from zoneinfo import ZoneInfo

now_utc: datetime = datetime.now(ZoneInfo("UTC"))
now_kolkata: datetime = datetime.now(ZoneInfo("Asia/Kolkata"))
now_ny: datetime = datetime.now(ZoneInfo("America/New_York"))

print(now_utc)
print(now_kolkata)
print(now_ny)

Best practice for stored timestamps: store everything in UTC, convert to the user’s local time only for display:

utc_time: datetime = datetime.now(ZoneInfo("UTC"))
local_time: datetime = utc_time.astimezone(ZoneInfo("Asia/Kolkata"))

A practical example — age from a birthdate

from datetime import date


def age(birthday: date) -> int:
    today: date = date.today()
    years: int = today.year - birthday.year
    # subtract 1 if we haven't hit this year's birthday yet
    if (today.month, today.day) < (birthday.month, birthday.day):
        years -= 1
    return years


print(age(date(1991, 2, 20)))    # 35  (as of 2026-05-11)

A small reminder that “subtract years” is not enough — you also need to check the day-of-year. Easy to forget; the tuple comparison is a clean fix.

What’s next

Dates and times handled. Next — collections, the box of specialised containers.

Toggle theme (T)