Dates, Times & Time Zones
Advanced · 20 min read · ▶ live playground · ✦ checkpoint
A regex can prove 2026-99-99 looks like a date. It can't prove that month 99 exists. Python's datetime tools give you real date and time objects, so your program can parse text, add time, format clean output, and handle time zones without guessing.
Use real date and time objects
The datetime module is Python's standard-library toolbox for calendar and clock work. A date is a calendar day with year, month, and day. A time is a clock reading with hour, minute, second, and smaller parts. A datetime is one value that combines a date and a time.
Think of these objects as stamped appointment cards. A string like "2026-07-15" is only ink on the page. A date(2026, 7, 15) is a card Python can compare, add to, and inspect.
from datetime import date, time, datetime, timedelta
practice_day = date(2026, 7, 15)
start_time = time(18, 30)
session = datetime(2026, 7, 15, 18, 30)
reminder = session + timedelta(days=7, minutes=15)
print(practice_day)
print(start_time)
print(session)
print(reminder)
print(reminder - session)2026-07-1518:30:002026-07-15 18:30:002026-07-22 18:45:007 days, 0:15:00
A timedelta is a duration, a measured amount of time such as 3 days or 20 minutes. You use it for questions like "one week from now" or "how long between these two datetimes?"
datetime objects understand calendar rules. Adding seven days to July 15 lands on July 22. Subtracting two datetimes gives you a timedelta, not a loose number.
Parse text, then format it
Most dates enter your program as strings: a CSV row, a form field, a JSON value, or text found by the regex tools from the last lesson. Parsing means turning text into a real object. Formatting means turning a real object back into text for people.
strptime() parses text using a format code. strftime() formats an object using a format code. The name looks odd: read the p as parse and the f as format.
from datetime import datetime
raw_times = ["2026-07-15 18:30", "2026-99-99 18:30"]
pattern = "%Y-%m-%d %H:%M"
for raw_time in raw_times:
try:
starts_at = datetime.strptime(raw_time, pattern)
print(starts_at.strftime("%d/%m/%Y %H:%M"))
except ValueError as error:
print(f"{raw_time}: rejected ({error})")15/07/2026 18:302026-99-99 18:30: rejected (time data '2026-99-99 18:30' does not match format '%Y-%m-%d %H:%M')
Common format codes:
%Ymeans a four-digit year, like2026.%mmeans a two-digit month, like07.%dmeans a two-digit day, like15.%Hmeans a 24-hour clock hour, like18.%Mmeans minutes, like30.
The useful habit is strictness. If your app says dates must look like YYYY-MM-DD HH:MM, then parse exactly that. Bad data should be rejected while it is still small.
Try it - build a schedule
Run this, then change one date to 2026-99-99 18:30. You should see the bad row rejected before it reaches the schedule.
Put a time zone on the card
A time zone is the rulebook for one place's clocks, including how many hours it sits ahead of or behind UTC and any daylight-saving clock changes. UTC is the reference clock programmers use when comparing moments across the world.
A naive datetime is a datetime with no time zone attached. An aware datetime is a datetime that knows its time zone. For real-world events, prefer aware datetimes. Without a zone, 2026-01-15 09:30 is not one moment. It is 9:30 somewhere.
Python's zoneinfo module gives you real time-zone rules by name.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
meeting = datetime(2026, 1, 15, 9, 30, tzinfo=ZoneInfo("America/New_York"))
print(meeting.isoformat())
print(meeting.astimezone(ZoneInfo("Asia/Kolkata")).isoformat())
print(meeting.astimezone(timezone.utc).isoformat())2026-01-15T09:30:00-05:002026-01-15T20:00:00+05:302026-01-15T14:30:00+00:00
Use names like "America/New_York" and "Asia/Kolkata", not hand-written offsets like -5 or +5.5. A fixed offset is only a number. A zone name carries the rulebook.
Use timestamps for storage
A timestamp is a number of seconds since the Unix epoch, the fixed starting point 1970-01-01 00:00:00 UTC. Timestamps are useful for storage, sorting, and communication between systems because they describe one instant without a local clock label.
For human display, convert the timestamp back into the user's time zone.
from datetime import datetime
from zoneinfo import ZoneInfo
event = datetime(2026, 7, 15, 18, 30, tzinfo=ZoneInfo("Asia/Kolkata"))
saved_stamp = event.timestamp()
restored = datetime.fromtimestamp(saved_stamp, tz=ZoneInfo("Asia/Kolkata"))
print(saved_stamp)
print(restored.isoformat())1784120400.02026-07-15T18:30:00+05:30
That round trip is the point: store the instant, then display it through the right local rulebook. If you store only "18:30", you lose the place. If you store only "2026-07-15", you lose the clock.
Checkpoint
Answer all three to mark this lesson complete
Now your programs can tell the difference between text that looks like a date and a real moment on a real clock. The next project asks you to bring that same precision back to objects: clear classes, clear behavior, and a system that holds together.