CHAPTER 29 · INTERMEDIATE4 min read

Datetime and Time Zones

Represent dates and instants accurately, and keep time-zone assumptions visible at system boundaries.

Python 3.12+Free to readModule 7 of 13
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Choose date, datetime, and timedelta for distinct meanings.
  • Use aware UTC datetimes for instants.
  • Distinguish time-zone conversion from attaching an interpretation.

01Choose the type from the meaning

A birthday or an invoice due date may be a calendar date without a time zone. Use date for that meaning. An event recorded at a particular moment needs datetime, while an elapsed amount such as ninety minutes uses timedelta. Mixing these meanings encourages accidental midnight assumptions and off-by-one-day displays.

Construct explicit values for examples and tests rather than depending on the current clock. date.fromisoformat parses an agreed ISO date representation; isoformat returns a suitable interchange string. Display formats belong near the interface, while calculations should continue using typed date and time values.

calendar_dates.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

02An instant needs an offset interpretation

A naive datetime has no usable UTC offset information. The value 09:00 alone cannot identify one global instant because many locations have that wall time. An aware datetime carries enough offset information for comparisons and conversions. For recorded events, use datetime.now(UTC) in real code or an explicit tzinfo=UTC in a deterministic test.

Python 3.12 deprecates utcnow(), which returns a naive value despite its name. Prefer an aware UTC value. Comparing naive and aware datetimes for ordering raises TypeError; do not fix that by blindly dropping tzinfo. Decide what the original value meant and normalize data at the boundary.

aware_times.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

03Convert an instant rather than relabeling it

astimezone converts the display of an existing instant into another zone. replace(tzinfo=...) attaches or replaces an interpretation without adjusting the wall-clock fields. Attaching UTC to a naive value is correct only when you already know that value represents UTC. Applying it to an unknown local time invents information.

A fixed offset is sufficient for a known +05:30 representation but does not model changing regional rules. zoneinfo.ZoneInfo uses named IANA zones and their historical and daylight-saving rules. It relies on available time-zone data; some platforms need a separately installed data package. The deterministic example uses a fixed offset so it requires only the standard library.

04Measure durations with the right clock

Subtract UTC-normalized aware instants for an elapsed timeline duration, especially around daylight-saving transitions. A timedelta has days and seconds components; use total_seconds() for the complete duration rather than seconds, which excludes whole days. Calendar scheduling such as "every day at nine locally" has different requirements from "every twenty-four elapsed hours".

For benchmarking or timeout elapsed time, use time.perf_counter rather than wall-clock datetimes. Wall time can be adjusted, while a performance counter is intended for measuring intervals. Inject a clock into business functions that ask whether a deadline has passed so tests can supply a fixed now instead of sleeping. Include the exact deadline boundary in those tests.

elapsed_duration.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
LET IT CLICK

The ideas worth keeping.

  • Model dates, instants, and durations as different concepts.
  • Keep event timestamps aware and normalize instants to UTC.
  • Use conversion for instants and explicit policies for ambiguous local input.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Normalize a timestamp

Parse 2026-10-20T14:30:00+05:30 and convert it to UTC. Reject a timestamp that lacks an offset instead of assuming a local zone.

your_turn_1.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
from datetime import UTC, datetimedef parse_instant(text):    value = datetime.fromisoformat(text)    if value.tzinfo is None or value.utcoffset() is None:        raise ValueError("timestamp must include an offset")    return value.astimezone(UTC)result = parse_instant("2026-10-20T14:30:00+05:30")assert result.isoformat() == "2026-10-20T09:00:00+00:00"try:    parse_instant("2026-10-20T14:30:00")except ValueError:    print("offset required")

The parser establishes a datetime value. The application then enforces awareness and normalizes the same instant into UTC.

02

Test overdue logic without a live clock

Write is_overdue(deadline, now) for aware values. A task is overdue only when now is strictly later. Test equality and one second later.

your_turn_2.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
from datetime import UTC, datetime, timedeltadef is_overdue(deadline, now):    for value in (deadline, now):        if value.tzinfo is None or value.utcoffset() is None:            raise ValueError("aware datetime required")    return now.astimezone(UTC) > deadline.astimezone(UTC)deadline = datetime(2026, 10, 20, 9, tzinfo=UTC)assert not is_overdue(deadline, deadline)assert is_overdue(deadline, deadline + timedelta(seconds=1))

Injecting now makes the boundary deterministic. Explicit UTC conversion states that the comparison concerns instants on the timeline.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01Which operation preserves an instant while changing its displayed offset?
02A timedelta spans one day and thirty minutes. Which gives its entire duration in seconds?
03Why prefer datetime.now(UTC) to datetime.utcnow() in Python 3.12?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Normalize a timestamp

Parse 2026-10-20T14:30:00+05:30 and convert it to UTC. Reject a timestamp that lacks an offset instead of assuming a local zone.

EXPECTED OUTPUT
offset required
challenge.pyPython 3
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this chapter, when you want a second angle or the full details.

One chapter closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock chapter completion.