Datetime and timezone correctness
Model instants, local times, durations, and daylight-saving transitions correctly.
Time bugs usually come from confusing an instant on the global timeline with a local wall
time. Python calls a datetime without tzinfo naive and one with tzinfo aware.
Use aware values for instants
from datetime import datetime, timezone
created_at = datetime.now(timezone.utc)
serialized = created_at.isoformat()
restored = datetime.fromisoformat(serialized)
assert restored == created_at
assert restored.tzinfo is not Nonedatetime.utcnow() returns a naive value and is deprecated in modern Python. Use
datetime.now(timezone.utc). Store instants in UTC, but retain a user's timezone identifier when
future local scheduling matters.
Convert with zoneinfo
zoneinfo.ZoneInfo uses IANA timezone data and applies historical daylight-saving rules.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
instant = datetime(2026, 11, 1, 5, 30, tzinfo=timezone.utc)
new_york = instant.astimezone(ZoneInfo("America/New_York"))
print(new_york.isoformat())Use geographic names such as America/New_York, not abbreviations such as EST: abbreviations
are ambiguous and fixed offsets do not model daylight-saving changes.
On systems without timezone data, applications may need the separately installed tzdata
package. That package is not part of the standard library.
Ambiguous and nonexistent wall times
When clocks move backward, one local time occurs twice. PEP 495's fold distinguishes the two:
from datetime import datetime
from zoneinfo import ZoneInfo
zone = ZoneInfo("America/New_York")
first = datetime(2026, 11, 1, 1, 30, tzinfo=zone, fold=0)
second = datetime(2026, 11, 1, 1, 30, tzinfo=zone, fold=1)
assert first.timestamp() != second.timestamp()When clocks move forward, some local times never occur. Constructing a datetime does not
guarantee that a wall time exists. User-facing schedulers need an explicit policy: reject,
shift forward, or ask the user. Convert through UTC and round-trip to detect problematic values.
Parse and format
Prefer ISO 8601 for machine exchange:
from datetime import datetime
value = datetime.fromisoformat("2026-07-19T17:30:00+00:00")
text = value.isoformat(timespec="seconds")Use strptime() only when an external format requires it. Parsing %Z timezone abbreviations is
not portable. A trailing Z denotes UTC and is accepted by current fromisoformat() versions.
Durations and calendar arithmetic
timedelta represents a fixed elapsed duration:
from datetime import datetime, timedelta, timezone
expires_at = datetime.now(timezone.utc) + timedelta(minutes=15)It does not represent "one calendar month." Month arithmetic needs a domain rule because months have different lengths. Also distinguish:
- elapsed-time deadlines, best measured with
time.monotonic(); - civil timestamps, represented with aware
datetime; - CPU time, available from
time.process_time().
System clock adjustments can make time.time() jump. Use monotonic clocks for timeout math:
import time
deadline = time.monotonic() + 2.0
remaining = max(0.0, deadline - time.monotonic())Timestamps and precision
datetime.timestamp() returns POSIX seconds as a float and may lose sub-microsecond precision.
datetime.fromtimestamp(value, tz=timezone.utc) creates an aware value. Avoid converting naive
local values to timestamps because the host's timezone silently affects the result.
Common mistakes
- Comparing naive and aware values (Python rejects ordering).
- Replacing
tzinfoto convert zones.dt.replace(tzinfo=...)labels wall fields;dt.astimezone(...)converts an instant. - Storing only a UTC offset for a recurring local event.
- Using local wall-clock time to measure elapsed work.
- Assuming every day is exactly 24 local hours.
Practice
Implement a meeting formatter that accepts an aware UTC instant and an IANA timezone name. Add tests around both daylight-saving transitions. Then design validation for a user-entered future local time and document your policy for ambiguous and nonexistent values.
LEARNING RECORD
Finish this lesson
Mark it complete when you can explain the main decision without looking.
KNOWLEDGE CHECK