zoneinfo Module Complexity¶
The zoneinfo module provides IANA time zone support for datetime,
including daylight saving time. A ZoneInfo object is parsed once from a
TZif file and cached. A lookup inside its recorded transitions is a binary
search; one before the first is constant, and so is one after the last when
the zone's footer rule has seasonal transitions to evaluate.
Complexity Reference¶
Let t be the number of transitions in a zone's TZif file (a few hundred for
a zone with daylight saving time, none for UTC), s the same for the zone a
datetime is converted from, f the number of files and directories under the
TZPATH trees, z the number of zones found there or listed by the packaged
database, c the number of cached zones, and p the number of search-path
entries.
| Operation | Time | Space | Notes |
|---|---|---|---|
ZoneInfo(key), first load |
O(p + t) | O(t) | Probes the search path for the file, then reads and parses it, one entry per transition, and caches the instance |
ZoneInfo(key), cached |
O(1) | O(1) | Returns the same object. It lives while referenced, and the eight most recently used zones stay alive regardless |
ZoneInfo.no_cache(key) |
O(p + t) | O(t) | Probes and parses every time; neither reads nor fills the cache |
ZoneInfo.from_file(f, key=None) |
O(t) | O(t) | Never cached; the result cannot be pickled |
ZoneInfo.clear_cache(only_keys=None) |
O(c) | O(1) | Drops every cached zone, or O(len(only_keys)) for the keys given; the next load parses again |
utcoffset(dt), dst(dt), tzname(dt) |
O(log t) | O(1) | Binary search over the transitions in local time. O(1) before the first transition and after the last, where the zone's footer rule is evaluated for the year of dt |
fromutc(dt) |
O(log t) | O(1) | The same search over the UTC transitions, plus the fold check. O(1) before the first transition, and after the last only when the footer rule has seasonal transitions; a fixed-offset footer still searches |
dt.astimezone(zone) |
O(log s + log t) | O(1) | The source zone's utcoffset(), then zone.fromutc() |
ZoneInfo.key |
O(1) | O(1) | None for a from_file() zone unless one was passed |
available_timezones() |
O(p + f + z) | O(z + f) | Reads the packaged zone list if one is installed, then walks the TZPATH trees, skipping right and posix, and opens each file whose key is not yet a known zone to check for the TZif magic; the walk's listings are the f term. Nothing is cached: every call walks again and returns a new set |
reset_tzpath(to=None) |
O(p) | O(p) | to's entries, or with None those of PYTHONTZPATH or the build's default. An entry of to must be absolute, ValueError otherwise; a relative PYTHONTZPATH entry is dropped with InvalidTZPathWarning. Zones already cached stay usable |
TZPATH |
O(1) | — | The tuple of search directories |
ZoneInfoNotFoundError, InvalidTZPathWarning |
— | — | Raised when no file matches key; warned when PYTHONTZPATH holds a relative entry |
Working with Time Zones¶
Using Timezones¶
from zoneinfo import ZoneInfo
from datetime import datetime
# Create timezone - O(p + t) on first load, O(1) from the cache afterwards
est = ZoneInfo("America/New_York")
pst = ZoneInfo("America/Los_Angeles")
# Create datetime with timezone - O(1)
dt = datetime(2024, 1, 15, 12, 0, tzinfo=est)
print(dt) # 2024-01-15 12:00:00-05:00
# Convert timezone - O(log s + log t), one lookup in each zone
dt_pst = dt.astimezone(pst)
print(dt_pst) # 2024-01-15 09:00:00-08:00
# Get timezone name - O(log t)
print(dt.tzname()) # EST
The Cache¶
from zoneinfo import ZoneInfo
# O(1) - the second call returns the very same object
a = ZoneInfo("Europe/London")
b = ZoneInfo("Europe/London")
a is b # True
# O(p + t) - a fresh probe and parse that leaves the cache alone
c = ZoneInfo.no_cache("Europe/London")
c is a # False
# O(1) - one key to drop; the next lookup parses again
ZoneInfo.clear_cache(only_keys=["Europe/London"])
ZoneInfo("Europe/London") is a # False
Beyond the Transition Table¶
from zoneinfo import ZoneInfo
from datetime import datetime
zone = ZoneInfo("America/New_York")
# O(log t) - inside the table of recorded transitions
datetime(1990, 7, 1, tzinfo=zone).tzname() # 'EDT'
# O(1) - after the last recorded transition the footer rule decides
datetime(2100, 7, 1, tzinfo=zone).tzname() # 'EDT'
datetime(2100, 1, 1, tzinfo=zone).tzname() # 'EST'
Listing Zones¶
from zoneinfo import available_timezones
# O(p + f + z) - walks the search path, opening files as it goes; call it once and keep the set
zones = available_timezones()
"America/New_York" in zones # True
Best Practices¶
✅ Do:
- Look zones up by key and let the cache hold them
- Call
available_timezones()once and keep the set - Keep a reference to a zone you use often, so it survives beyond the eight the cache holds on its own
❌ Avoid:
no_cache()orfrom_file()in a loop - every call parses the file again- Calling
available_timezones()per request - it walks the search path every time