Modules and the standard libraryChapter 72 of 114
Dates
Points in time, differences between them, and turning them into text.
The three types
datetime gives you date (a calendar day), time (a clock time), and datetime (both):
from datetime import date, time, datetime
print(date(2026, 3, 14))
print(time(9, 30))
print(datetime(2026, 3, 14, 9, 30))Output
2026-03-14 09:30:00 2026-03-14 09:30:00
Printing gives ISO format, which sorts correctly as text — a genuinely useful property.
Now
from datetime import datetime, date
now = datetime.now()
print(type(now).__name__)
print(date.today().year > 2000)Output
datetime True
Reading the parts
from datetime import datetime
moment = datetime(2026, 3, 14, 9, 30, 45)
print(moment.year, moment.month, moment.day)
print(moment.hour, moment.minute)
print(moment.weekday())
print(moment.date())Output
2026 3 14 9 30 5 2026-03-14
weekday() counts from Monday as 0, so 5 is Saturday. isoweekday() counts from Monday as 1, which is the other convention you will meet.
Formatting with strftime
from datetime import datetime
moment = datetime(2026, 3, 14, 9, 5)
print(moment.strftime("%d/%m/%Y"))
print(moment.strftime("%A %d %B %Y"))
print(moment.strftime("%H:%M"))
print(moment.strftime("%Y-%m-%d %H:%M:%S"))Output
14/03/2026 Saturday 14 March 2026 09:05 2026-03-14 09:05:00
| Code | Means |
|---|---|
%Y | four-digit year |
%m | month as 01 to 12 |
%d | day as 01 to 31 |
%H | hour, 24-hour |
%M | minute |
%B | month name |
%A | weekday name |
Parsing with strptime
The same codes, in reverse:
from datetime import datetime
moment = datetime.strptime("14/03/2026", "%d/%m/%Y")
print(moment)
print(moment.year)Output
2026-03-14 00:00:00 2026
The format string has to match the text exactly, character for character. A mismatch raises ValueError rather than guessing.
For ISO text, there is a shortcut that needs no format string:
from datetime import datetime, date
print(datetime.fromisoformat("2026-03-14T09:30:00"))
print(date.fromisoformat("2026-03-14"))
print(date(2026, 3, 14).isoformat())Output
2026-03-14 09:30:00 2026-03-14 2026-03-14
Differences
Subtracting two dates gives a timedelta:
from datetime import date, timedelta
start = date(2026, 3, 1)
end = date(2026, 3, 14)
gap = end - start
print(gap)
print(gap.days)
print(start + timedelta(days=30))
print(start - timedelta(weeks=2))Output
13 days, 0:00:00 13 2026-03-31 2026-02-15
timedelta takes days, weeks, hours, minutes and seconds — but not months or years, because those are not fixed lengths. For "one month later", use the dateutil package.
Comparing
from datetime import date
print(date(2026, 1, 1) < date(2026, 6, 1))
print(sorted([date(2026, 5, 1), date(2026, 2, 1)]))Output
True [datetime.date(2026, 2, 1), datetime.date(2026, 5, 1)]
Note the list shows the repr, which is why it looks different from a printed date.
Time zones
A datetime with no zone is naive: it does not know where it is. Comparing a naive one with an aware one raises:
from datetime import datetime, timezone, timedelta
aware = datetime(2026, 3, 14, 9, 0, tzinfo=timezone.utc)
print(aware)
print(aware.astimezone(timezone(timedelta(hours=2))))
naive = datetime(2026, 3, 14, 9, 0)
try:
print(naive < aware)
except TypeError as problem:
print("TypeError:", problem)Output
2026-03-14 09:00:00+00:00 2026-03-14 11:00:00+02:00 TypeError: can't compare offset-naive and offset-aware datetimes
Test yourself
2 questionsWhy can a timedelta not take months or years?
Show the answer
They are not fixed lengths — A month is 28 to 31 days. For calendar arithmetic use the dateutil package.
What happens when you compare a naive datetime with an aware one?
Show the answer
TypeError — Store and compute in UTC and convert only for display, and this stops coming up.
Math
The math module, plus statistics and random.