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:
| Code | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2026 |
%m | 2-digit month | 05 |
%d | 2-digit day | 11 |
%H | 24-hour hour | 14 |
%I | 12-hour hour | 02 |
%M | minute | 30 |
%S | second | 45 |
%A | weekday name | Monday |
%B | month name | May |
%p | AM/PM | PM |
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.