Install & Compatibility
Where this runs
tested against v2026.3.post1 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.910 runs
installs and imports cleanly · install 0.0s · import 0.010s · 68.7MB
glibcpy 3.10–3.910 runs
installs and imports cleanly · install 1.8s · import 0.010s · 21MB
43MB installed
● package 43MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
timezone
✓ from pytz import timezone
Returns a pytz tzinfo object; must be used with .localize() — NOT with datetime(..., tzinfo=tz)
utc / UTC
✓ import pytz; tz = pytz.utc
pytz.utc, pytz.UTC, and pytz.timezone('UTC') are all the same singleton; 'GMT' is a different object and will not compare equal
all_timezones / common_timezones
✓ from pytz import all_timezones, common_timezones
all_timezones is the exhaustive IANA list; common_timezones omits deprecated/historical zones
UnknownTimeZoneError
✓ from pytz.exceptions import UnknownTimeZoneError
Raised by timezone() for unrecognised IANA keys; also importable directly as pytz.exceptions.UnknownTimeZoneError
Canonical pytz usage: always store in UTC, convert to local only for display. Use localize() to attach a tz to a naive datetime; use normalize() after any arithmetic on a local datetime to fix DST offsets.
from datetime import datetime, timedelta
import pytz
# --- Correct: build a UTC-aware datetime ---
utc = pytz.utc
utc_dt = datetime(2024, 3, 10, 7, 0, 0, tzinfo=utc)
print("UTC:", utc_dt)
# --- Correct: convert UTC -> local via astimezone ---
eastern = pytz.timezone('US/Eastern')
loc_dt = utc_dt.astimezone(eastern)
print("Eastern:", loc_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
# --- Correct: localize a naive datetime ---
naive = datetime(2024, 11, 3, 1, 30) # ambiguous wall-clock time
aware = eastern.localize(naive, is_dst=True) # explicitly choose EDT side
print("Localized (EDT):", aware)
# --- Correct: arithmetic on local times MUST be followed by normalize ---
before_dst_end = eastern.localize(datetime(2024, 11, 3, 1, 50))
result = eastern.normalize(before_dst_end + timedelta(minutes=20))
print("After normalize:", result.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
# --- WRONG (do not do this) ---
# wrong = datetime(2024, 11, 3, 1, 30, tzinfo=eastern) # returns LMT, not EST/EDT!
# --- Preferred modern alternative (Python 3.9+) ---
# from zoneinfo import ZoneInfo
# aware_modern = datetime(2024, 11, 3, 1, 30, tzinfo=ZoneInfo('US/Eastern'))
Debug
Known issues
breakingDo NOT pass a pytz timezone directly to the datetime constructor via tzinfo=. For non-UTC zones this silently attaches the zone's historical LMT (Local Mean Time) offset, not the correct current offset, producing wrong results with a 50% chance of being off by an hour around DST transitions.fixAlways use tz.localize(naive_dt) to attach a pytz timezone to a naive datetime. Only pytz.utc is safe to pass directly to the constructor.
affects: all
breakingAfter any timedelta arithmetic on a pytz-aware local datetime, the UTC offset can become stale if the operation crossed a DST boundary. The offset is not automatically recalculated.fixWrap every arithmetic result with tz.normalize(result) to correct the offset. Or work exclusively in UTC and convert only for display.
affects: all
deprecatedThe pytz maintainer and PyPI page explicitly state that projects on Python 3.9+ should use stdlib zoneinfo (PEP 615) + tzdata instead. pytz receives no new features; only IANA DB updates are applied.fixNew code: use 'from zoneinfo import ZoneInfo' with 'pip install tzdata'. For gradual migration use the pytz-deprecation-shim package.
affects: all (Python 3.9+)
gotchapytz.utc / pytz.UTC is NOT the same object as pytz.timezone('GMT'), pytz.timezone('Greenwich'), or datetime.timezone.utc. Identity/equality checks between these will return False.fixStandardise on pytz.utc throughout pytz code, or use datetime.timezone.utc when mixing with stdlib. Never assume UTC aliases are interchangeable.
affects: all
gotchalocalize() raises ValueError if the datetime already has tzinfo set. Calling localize() on an already-aware datetime is a common mistake when re-processing data.fixCheck dt.tzinfo is None before calling localize(). For already-aware datetimes use astimezone() to convert zones.
affects: all
gotchaDST disambiguation via is_dst=None raises pytz.exceptions.AmbiguousTimeError or NonExistentTimeError instead of silently picking one, but only if you opt in. The default (is_dst=False) silently chooses standard time, which may surprise callers.fixPass is_dst=None to localize() in production scheduling code so ambiguous or non-existent wall-clock times raise an exception rather than silently producing a wrong result.
affects: all
gotchapytz timezone objects are not compatible with the standard Python tzinfo interface expected by PEP 495 consumers (fold attribute). Passing pytz zones to libraries expecting stdlib-compatible tzinfo (e.g. Django 4.0+ defaults, zoneinfo consumers) can produce incorrect offsets.fixWhen interfacing with PEP 495-aware code, convert: use zoneinfo.ZoneInfo(str(pytz_zone)) to obtain an equivalent stdlib zone object.
affects: all
Upgrade
Version history
2026.3.post1latest on PyPI · released Jul 25, 2026
Audit
Dependencies
No dependency data recorded yet.