不列颠哥伦比亚省的时区调整,给使用 Postgres 存未来预约的系统埋下了一个很具体的坑:从 2026 年 3 月 8 日起,该省春季拨快到 UTC-7 后,不再在秋季拨回 UTC-8,America/Vancouver 将长期按 UTC-7 计算。
这件事的重要性不在于时区新闻本身,而在于它打破了许多后端系统的默认假设。Postgres 的 timestamptz 存的是 UTC 瞬间,不保存用户输入的本地墙钟时间,也不保存时区名。插入和查询都会按当时系统里的 tzdata 规则换算;规则一变,未来本地时间就可能错位。
BC 新规则会让已存未来预约错位
典型场景很简单:用户在温哥华预约 2026 年 11 月 10 日上午 10 点。若系统按旧规则认为那天是 UTC-8,就会把它存成 18:00 UTC。等 tzdata 更新后,America/Vancouver 已按 UTC-7 解释,18:00 UTC 查回本地时间就成了 11 点。
| 数据类型 | 适用场景 | 判断 |
|---|---|---|
| timestamptz | 日志、交易、传感器、已发生事件 | UTC 瞬间权威,继续使用 |
| 本地时间 + 时区名 + 派生 UTC | 预约、日历、配送、法律截止时间 | 用户本地意图权威,应保留 |
| 只存本地时间 | 跨时区调度、提醒、冲突检测 | 信息不足,不建议单独使用 |
这也是行业里常见的分歧:很多系统把“存 UTC”当作银弹。对日志和支付流水,这个习惯是对的;对未来日历,它只保存了系统当时理解的结果,没有保存用户的原始意图。
tzdata 更新不是小运维,可能改变业务数据含义
Crunchy Data 给出的检测 SQL 是一个实用锚点:
SELECT to_char('2026-12-01 10:00:00'::timestamp AT TIME ZONE 'America/Vancouver', 'HH24:MI:SS OF');
如果结果是 17:00:00 +00,说明 tzdata 已按新规则计算;如果是 18:00:00 +00,说明尚未更新。后者不是长期好事,只是暂时没有出现新旧规则混杂。一旦 Ubuntu 等系统的 tzdata 包随常规更新推进,数据库、应用服务器和后台任务若更新时间不一致,排查会更麻烦。
一个容易被忽略的限制是:Postgres 不能把 timestamptz 生成列当作稳定不可变表达式处理,因为时区规则本来就会变。更稳妥的做法,是表里保存 local_time、timezone_name 和 starts_at_utc,用触发器或应用逻辑派生 UTC;tzdata 更新后,再按时区名重算未来记录。
工程团队接下来要查的不是所有时间列
受影响最大的,是面向真人到场、送达和截止日期的系统:诊所预约、上门服务、航旅接送、本地法院或政府申报期限。用户记住的是墙上时钟的 10 点,不是数据库里的 17:00 UTC 或 18:00 UTC。
团队接下来应查三件事:哪些表保存了 2026 年 11 月以后、地点在 BC 的未来事件;tzdata 在生产环境、任务队列和只读副本中的更新时间是否一致;是否有足够的 created_at、updated_at 和审计日志来判断记录是在规则更新前还是更新后生成。
RFC 9557 这类带时区名的时间格式可以改善表达,但它并不解决未来本地时间受政策变化影响的问题。真正的取舍仍在数据库模型里:到底谁是权威,是 UTC 瞬间,还是用户当初选择的本地时间。
