Python datetime 的陷阱,以及各类库对此(未)做了什么
软件中的日期和时间处理远比“直接用 UTC”复杂得多,尤其当涉及夏令时、不断变化的时区法律,以及面向未来的人类事件(如会议或合同)时。评论者讨论了 Python 的 `datetime` 模块及其替代方案应该如何表示时间——UTC 时刻、本地“墙上时钟”时间、固定偏移,还是完整的 IANA 时区——以及库是否应允许模糊或不存在的时间。许多人认为没有一种单一模型能适配所有场景,因此健壮的库需要多个明确的类型和清晰的取舍,而不是某种神奇的时间戳抽象。
UTC vs 本地时间 & 未来日期时间
- 许多人认为“把所有东西都存成 UTC”对过去事件和面向机器的时间戳效果很好。
- 但也有人强调,这对未来由人安排的时间(会议、约会、合同)并不适用。
- 关键点是:未来的墙上时钟时间通常应存为(本地日期时间 + IANA 时区),而不是预先转换成 UTC,因为时区/DST 规则可能变化。
- 这没有统一规则:有时用户想要的是一个固定的 UTC 时刻,有时则是某地的“本地墙上时间”。
DST、时区与人类预期
- DST 和时区政策具有政治性且会变化;你无法预测未来规则。
- 周期性事件(“每周一 10:00”、“每两周一次 1:1,下午 1 点”)不应在 DST 变化时平移一小时;这需要显式建模本地时间和时区。
- 跨洲会议在 DST 切换日期不同的时候会出现暂时性的错位;日历通常会选择一个参考时区。
数据格式与纪元(ISO 8601、Unix 时间等)
- ISO 8601 / RFC 3339 适合字符串格式化,但并不能解决时区/DST 语义或地点问题。
- 美式 MM/DD/YYYY 以及像 Excel 这样的工具被视为主要错误来源。
- Unix 纪元秒数(或整数微秒/纳秒)在内部表示中很流行,但对 1970 年之前的支持以及闰秒都很棘手。
闰秒
- 一些库(例如通用库,包括这里讨论的那些)会忽略闰秒;专业库(例如面向天文学的库)会处理它们。
- 看法不一:有人认为对大多数应用来说忽略闰秒是可以接受的;也有人指出这会破坏精确的时长/时刻计算,以及对“23:59:60”的解析。
库设计:类型与行为
- 强烈支持使用不同类型:
- Instant/仅 UTC,
- ZonedDateTime(IANA 时区),
- OffsetDateTime(固定偏移),
- 用于墙上时钟或周期性“闹钟”场景的本地/naive 日期时间。
- 将 naive 和 zoned 数据混在同一种类型里(如某些 JS 和 Python 库)受到广泛批评。
- 对于 DST 缺口中的不存在时间,如何表示存在争议:
- 有人倾向于禁止或对无效本地时间报错。
- 也有人更喜欢有文档、可确定的“尽力而为”映射,即使结果令人惊讶。
“本地时间”概念
- 有人认为系统“本地时间”除了 UI 和 libc 兼容性之外几乎是历史错误;更好的做法是始终指定明确的时区或地点。
- 也有人指出,普通用户和设备仍然需要“本地墙上时间”的概念,用于闹钟、类 cron 任务,以及“咖啡馆下午 4 点”这类语义。