定时任务遇到夏令时:识别重复与不存在的本地时间
仓库在纽约安排凌晨执行任务。春季切换时,02:30 可能根本不存在。秋季切换时,01:30 可能出现两次。解析用户输入时先枚举可能的 UTC 时刻,再按产品约定处理零个或两个候选。
1. 用三条输入验证候选数
保存为 dst_lab.py,执行 python dst_lab.py。运行环境需要可用的 IANA 时区数据。缺失时,可以给部署环境添加 tzdata 依赖。
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
def utc_candidates(wall, zone):
if wall.tzinfo is not None:
raise ValueError("expected a naive local time")
found = set()
for fold in (0, 1):
aware = wall.replace(tzinfo=zone, fold=fold)
utc = aware.astimezone(timezone.utc)
restored = utc.astimezone(zone).replace(tzinfo=None)
if restored == wall:
found.add(utc)
return sorted(found)
ny = ZoneInfo("America/New_York")
examples = [
("2024-03-10T02:30", []),
("2024-11-03T01:30", ["05:30", "06:30"]),
("2024-11-03T03:30", ["08:30"]),
]
for text, expected in examples:
candidates = utc_candidates(datetime.fromisoformat(text), ny)
actual = [value.strftime("%H:%M") for value in candidates]
assert actual == expected
print(text, "UTC candidates:", actual)
本地实测输出如下。
2024-03-10T02:30 UTC candidates: []
2024-11-03T01:30 UTC candidates: ['05:30', '06:30']
2024-11-03T03:30 UTC candidates: ['08:30']
2. 在创建任务时定义边界行为
| 候选数 | 含义 | 可以采用的契约 |
|---|---|---|
| 0 | 该本地时刻落在时钟向前跳过的区间 | 请用户改时间,或明确顺延规则 |
| 1 | 对应唯一执行时间 | 保存 UTC 时刻 |
| 2 | 回拨造成重复 | 由用户选较早或较晚时刻,并显示 UTC 偏移 |
直接绑定 tzinfo 可以构造带时区对象,但构造成功未验证这个本地时刻真实存在。实验对两个 fold 都做 UTC 往返,剔除回到不同墙上时间的候选,并在 UTC 上去重。
一次预约可以落成 UTC 时刻。对于「每天当地 09:00」,还要持久化当地时区和重复规则,再为每个日期计算执行时间。按上次 UTC 时间加 24 小时,跨夏令时后可能改变当地时刻。
小结
界面接受本地时间时,把时区和重复时刻的选择一并纳入输入。测试至少包含春季缺口、秋季回拨和一个普通时刻。
数据来源与检索时间
检索时间 2026-11-13。
| 来源 | URL | 对应内容 |
|---|---|---|
| Python zoneinfo | https://docs.python.org/3.11/library/zoneinfo.html | IANA 数据来源与 fold |
| PEP 495 | https://peps.python.org/pep-0495/ | 本地时间歧义及缺失时间 |
核验说明
- Python 3.11.9 实测纽约三个历史时刻。业务的顺延和重复触发规则需单独约定。