Plate 68
zoneinfo vs Fixed UTC Offset: Localhost Lab
Hands-on zoneinfo.ZoneInfo vs fixed UTC offset conversions: real conv/s on many datetimes, measured on Linux localhost today in this hands-on lab for SREs.
Aditya Challa4 min read
Intro — what this post promises
Convert many datetimes with zoneinfo.ZoneInfo vs fixed datetime.timezone offsets vs naive timedelta math (stdlib only — no pytz). This lab reports conversions/s on Linux localhost.
Frame: prefer zoneinfo for real TZ rules; fixed offsets only when rules are truly fixed (e.g. always UTC+05:30). Affiliates: 0.
Related links:
- dataclass replace vs manual localhost lab
- heapq merge vs sorted localhost lab
- islice vs list slice localhost lab
- configparser vs json localhost lab
- html escape vs manual localhost lab
- zlib vs gzip compress localhost lab
- difflib vs set ops localhost lab
- mmap write vs write localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. 20000 hourly stamps. Zones: UTC, Asia/Kolkata, America/New_York.
Verdict up front: astimezone(fixed IST) ~3937539 conv/s; astimezone(ZoneInfo Kolkata) ~3718035; astimezone(New_York) ~3627156; naive +05:30 ~25248859 (not a TZ conversion). DST check NY winter offset -18000.0 s vs summer -14400.0 s.
Arms
| Arm | Meaning |
|---|---|
attach timezone.utc / ZoneInfo UTC | stamp tzinfo |
| attach fixed IST / ZoneInfo Kolkata | stamp tzinfo |
astimezone fixed vs ZoneInfo | real convert |
astimezone New_York | DST rules |
naive + timedelta(5:30) | offset math only |
| isoformat after convert | string path |
Lab topology
Script: lab-evidence/111-zoneinfo-vs-utc-offset/results/run_lab.py.
Lead table — conversions (p50 conv/s)
| Arm | conv/s |
|---|---|
| naive +05:30 | 25248859 |
| astimezone timezone.utc | 24853396 |
| attach ZoneInfo Kolkata | 14399906 |
| attach fixed IST | 8072214 |
| astimezone fixed IST | 3937539 |
| astimezone ZoneInfo Kolkata | 3718035 |
| astimezone ZoneInfo NY | 3627156 |
Fixed IST vs ZoneInfo Kolkata astimezone sit in the same band here — Kolkata has no DST, so rules collapse to a constant offset. New_York still needs zoneinfo (winter/summer offsets differ).
Attach & format
| Arm | conv/s |
|---|---|
| attach timezone.utc | 15169247 |
| attach ZoneInfo UTC | 14411963 |
| isoformat ZoneInfo Kolkata | 943588 |
| isoformat fixed IST | 984936 |
String formatting dominates once you leave pure conversion.
Storage recommendation
Store UTC aware (timezone.utc or ZoneInfo UTC), convert to ZoneInfo only at the edges (API, UI, reports). That keeps math simple and still pays zoneinfo only where humans read clocks.
Reading it
- Real civil times →
ZoneInfo("Area/City"). - Truly fixed offsets (storage UTC, display +00:00) →
timezone.utc/ fixedtimezone(timedelta(...)). - Never use naive
datetime + timedeltaas a substitute for TZ rules across DST. - pytz not required on 3.9+ for this pattern — stdlib zoneinfo is enough.
IST special case
Asia/Kolkata is a popular fixed-offset example (+05:30, no DST). Matching ZoneInfo against timezone(timedelta(hours=5, minutes=30)) is expected to be close — and it was here. That agreement must not be generalized to America/New_York or Europe/London. The NY winter/summer offset split in this lab exists specifically to catch that mistake.
Library note
Classic pytz patterns (localize / normalize) are unnecessary for new code on Python 3.9+. Stick to aware datetimes + ZoneInfo + astimezone. If you still maintain pytz call sites, migrate at the boundary rather than mixing both in one hot path.
Pitfalls
- Attaching a fixed offset to local wall times without
astimezone. - Assuming all zones are fixed like IST.
- Shipping naive-plus math into US/EU product clocks.
- Comparing attach speed to astimezone speed without labels.
Reproduce
Evidence: summary.json, summary.txt.
Limits
One Linux box. System tzdata required for ZoneInfo. Not database TIMESTAMP WITH TIME ZONE round-trips.
Takeaway
For Kolkata, ZoneInfo ~3718035 conv/s ≈ fixed offset ~3937539. Still prefer zoneinfo so DST zones (NY winter -18000 s / summer -14400 s) stay correct — fixed offsets only when the contract is truly fixed.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. n=20000: astimezone fixed IST 3937539 conv/s; ZoneInfo Kolkata 3718035; ZoneInfo NY 3627156. stdlib only. Affiliates: 0. Evidence: lab-evidence/111-zoneinfo-vs-utc-offset/.
Related links
Plate 17
platform vs os.uname Inventory: Localhost Lab
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 75
uuid.uuid4 vs uuid.uuid1: Localhost Lab
Hands-on uuid.uuid4 vs uuid.uuid1 ID generation lab: real ops/s plus version/node checks, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 50
signal vs threading.Event Wakeup: Localhost Lab
Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026