ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 68

  1. Blog

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 Challa·1 October 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — conversions (p50 conv/s)
  5. Attach & format
  6. Storage recommendation
  7. Reading it
  8. IST special case
  9. Library note
  10. Pitfalls
  11. Reproduce
  12. Limits
  13. Takeaway

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

ArmMeaning
attach timezone.utc / ZoneInfo UTCstamp tzinfo
attach fixed IST / ZoneInfo Kolkatastamp tzinfo
astimezone fixed vs ZoneInforeal convert
astimezone New_YorkDST rules
naive + timedelta(5:30)offset math only
isoformat after convertstring path

Lab topology

n=20000 timestamps · 7 rounds · p50
metric: conv/s = n / p50_s
stdlib: zoneinfo + datetime.timezone only

Script: lab-evidence/111-zoneinfo-vs-utc-offset/results/run_lab.py.


Lead table — conversions (p50 conv/s)

Armconv/s
naive +05:3025248859
astimezone timezone.utc24853396
attach ZoneInfo Kolkata14399906
attach fixed IST8072214
astimezone fixed IST3937539
astimezone ZoneInfo Kolkata3718035
astimezone ZoneInfo NY3627156

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

Armconv/s
attach timezone.utc15169247
attach ZoneInfo UTC14411963
isoformat ZoneInfo Kolkata943588
isoformat fixed IST984936

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 / fixed timezone(timedelta(...)).
  • Never use naive datetime + timedelta as 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

python3 lab-evidence/111-zoneinfo-vs-utc-offset/results/run_lab.py

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.

zoneinfodatetime.timezoneutc offsetdstlocalhost labsreconversions/s

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/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — conversions (p50 conv/s)
  5. Attach & format
  6. Storage recommendation
  7. Reading it
  8. IST special case
  9. Library note
  10. Pitfalls
  11. Reproduce
  12. Limits
  13. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove