นากกายหมุนเข็มนาฬิกาข้ามช่องว่างฤดูใบไม้ผลิบนโต๊ะมิ้นต์

zoneinfo: เก็บเวลาแบบ DST-safe โดยไม่เดาออฟเซ็ตเอง

น้องกายEngineering

เคยบวกชั่วโมงในโซนที่มี DST แล้วได้เวลาติดผนังผิดไหม ผมเคย จนเลิกเดาออฟเซ็ตเอง

ตามเอกสาร zoneinfo คลาส ZoneInfo อ่านฐานข้อมูล IANA เช่น America/New_York ส่วนเอกสาร datetime อธิบายว่าแอตทริบิวต์ fold ใช้แยกเวลาที่ซ้ำกันตอนนาฬิกาถอยหลัง

กติกาที่ผมใช้

  • เก็บช่วงเวลาสำคัญเป็น UTC แล้วค่อย astimezone ไปโซนท้องถิ่นตอนแสดงผล
  • อย่าบวก timedelta แล้วคาดหวังว่านาฬิกาติดผนังจะข้ามช่องว่าง DST ให้เองเสมอ
  • ตอนนาฬิกาถอยหลัง ใช้ fold=0 / fold=1 ให้ชัดว่าหมายถึงออฟเซ็ตไหน
from datetime import datetime
from zoneinfo import ZoneInfo

ny = ZoneInfo("America/New_York")
utc = ZoneInfo("UTC")

# Spring-forward 2026-03-08: 02:00 local is skipped.
local_before = datetime(2026, 3, 8, 6, 30, tzinfo=utc).astimezone(ny)
local_after = datetime(2026, 3, 8, 7, 30, tzinfo=utc).astimezone(ny)
assert local_before.hour == 1 and local_before.minute == 30
assert local_after.hour == 3 and local_after.minute == 30
assert local_before.utcoffset() != local_after.utcoffset()

# Fall-back 2026-11-01: 01:30 occurs twice; fold picks which offset.
first = datetime(2026, 11, 1, 1, 30, tzinfo=ny, fold=0)
second = datetime(2026, 11, 1, 1, 30, tzinfo=ny, fold=1)
assert first.utcoffset() != second.utcoffset()
assert (second.astimezone(utc) - first.astimezone(utc)).total_seconds() == 3600
print("ok")

Recap

  1. ใช้ ZoneInfo("Area/City") จาก IANA ไม่ฮาร์ดโค้ด +07 แบบตายตัวถ้าโซนมี DST
  2. เก็บแกนเวลาเป็น UTC แล้วแปลงตอนแสดง
  3. ใช้ fold เมื่อเวลาติดผนังเกิดซ้ำตอนนาฬิกาถอย
  4. เขียนเทสต์ข้ามวันเปลี่ยนฤดูในโซนที่ทีมคุณสนใจจริง