Symptom: a signer completes a document at 9:15 AM Pacific, but your audit log, email, or PDF stamp shows a time several hours earlier or later, or shows different times in different places.
Cause
The timestamp was saved as a naive value, meaning it carries no time zone. Python's documentation says a naive datetime does not contain enough information to locate itself relative to other datetimes, and that whether it means UTC, local time, or something else is up to your program. Calling astimezone() on a naive value makes it worse, because the docs say a naive value is presumed to be in the system time zone. A server running in UTC and a laptop running in Pacific will then disagree about the same record.
Fix
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# 1. Capture and store an aware UTC value (ISO 8601 with offset)
signed_at = datetime.now(timezone.utc)
stored = signed_at.isoformat() # e.g. 2026-10-13T16:15:01+00:00
# 2. Parse it back: a string with an offset gives an aware datetime
parsed = datetime.fromisoformat(stored)
# 3. Convert only when displaying, using the viewer's zone
print(parsed.astimezone(ZoneInfo("America/Los_Angeles")).strftime("%b %d, %Y %I:%M %p %Z"))
# A string with no offset parses to a naive datetime (tzinfo is None)
print(datetime.fromisoformat("2026-10-13T09:14:19").tzinfo)Store the UTC value once, never overwrite it with a converted one, and convert at the edge (UI, email, PDF stamp) using the signer's or viewer's zone.
Caveats
zoneinfo was added in Python 3.9. It reads the system time zone database, falling back to the tzdata package from PyPI. Windows notably lacks a system database, so add tzdata as a dependency for cross-platform code, or every ZoneInfo call can raise ZoneInfoNotFoundError. Also check your data source: if an upstream system sends signing times without an offset, ask which zone it uses before you attach one.
Back to All Questions