Skip to content

필사 모드: tzdata 2026c — 加拿大不再拨表,摩洛哥重回GMT

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 — 7月8日,一处没人读发布说明的基础设施

2026年7月8日,IANA 时区数据库发布了今年第三个版本 tzdata 2026c。这个数据库是一堆文本文件,记录着地球上每个时区的 UTC 偏移量、过渡规则以及历史变更记录,无论你的技术栈是什么样子,它必然藏在某个角落里 — glibc 读取的 /usr/share/zoneinfo、JDK 自带的一份拷贝、Go 标准库内嵌的一份拷贝,再经由 ICU 一路到浏览器、Node、Android。

这份数据被更新的理由永远是同一个:某个政府修改了关于时间的法律。今年的输入格外厚重 — 加拿大的两个省(很快可能还有第三个地区)废除了季节性拨表,摩洛哥决定时隔8年重回 GMT。在这个过程中,还发生了一件罕见的事:tzdb 因为受 CLDR 的约束,刻意写入了一个与实际法定生效日不同的过渡日期

本文以一手资料(NEWS 文件与 zone 数据)为准,梳理今年三个版本(2026a/2026b/2026c)实际改动了什么,并整理秋季两个截止日期前应该检查的事项。时区与时间戳的一般性讨论已经在时间戳与时区,正确的做法一文中处理过,这里只看2026年实际发生的变化。

今年的三个版本 — 一览

版本日期关键变更
2026a2026-03-01摩尔多瓦历史数据的追溯修正、right(计入闰秒)文件从默认安装中移除、TZif 缩写空间 50→256 字节
2026b2026-04-22不列颠哥伦比亚省永久 -07、zic 溢出修复
2026c2026-07-08艾伯塔省永久 -06、摩洛哥自9月20日起永久 +00、zic 溢出追加修复

tzdata 不是按周期发布的,而是"需要时才发"。最近三年(2023-2025)分别发布了4次、2次、3次,而今年到7月已经是第三次了。而且正如 NEWS 文件所预告的,因为西北地区的事,"很可能不久后还需要再发一版"。

加拿大西部不再拨表了

不列颠哥伦比亚省(BC)是第一个。根据BC省政府的公告(2026AG0013),2026年3月8日的春季拨快(spring forward)是最后一次拨表,此后 BC 全年停留在 -07。法律条文很有意思,按 tzdb 注释里引用的 Paul Eggert 的整理,新的标准时间 Pacific Time"在3月8日02:00通常的 PST→PDT 过渡之后21小时的次日(3月9日00:00)开始"。也就是说,时钟只拨了一次,但名称却相隔21小时变化了两次。

艾伯塔省紧随其后。其经过在官僚程序上颇为有趣 — Bill 31(《减少繁文缛节法规修订法案》,2026)第3节废除了原有的《夏令时法》,代之以《官方时间法》(第 O-5.7 章),将 UTC-6 定为标准时间。该法案于2026年5月14日获得御准,并于6月18日通过204/2026号枢密院令,在同一天生效。同日发布的206/2026号枢密院令将这一时间的正式名称定为"Alberta Time",但该规定附有2031年6月30日的失效条款 — 意味着届时将重新审视该名称是否有效。tzdb 的注释干巴巴地补充道,这项命名规定"既不影响时间计算,也不影响 TZDB 数据"。

艾伯塔省全年 -06 其实并不是什么陌生的时间。这正是邻近的萨斯喀彻温省几十年来一直在用的时间,tzdb 的 America/Regina 早就以 -6:00 - CST 收尾。艾伯塔省不过是加入了这个行列。

西北地区(NWT)还没跟上。该地区总理已表示"艾伯塔省若实行,我们就跟进",并确实宣布了废除意向,但由于法律程序尚未完成,2026c 中只把这件事作为注释放了进去,而不是数据。America/Inuvik 的 zone 定义下方,附着这样一段注释:

Zone America/Inuvik	0	-	-00	1953 # Inuvik founded
			-8:00	NT_YK	P%sT	1979 Apr lastSun  2:00
			-7:00	NT_YK	M%sT	1980
			-7:00	Canada	M%sT
# Assuming Northwest Territories follows Alberta in abolishing seasonal time
# changes, replace the above line with something like:
#			-7:00	Canada	M%sT	2026 Nov  1  2:00
#			-6:00	-	CST

这是预先埋进数据库里的一条 TODO — 法律一旦通过,就把注释解开发布。这也是为什么已经有人预告了 2026d。

温哥华的新缩写是 MST — 挑选缩写这件难事

BC 新标准时间的法定名称是 Pacific Time。自然而然的缩写会是 PT,但问题就出在这里 — tzdb 和 POSIX 要求时区缩写为3到6个字符的字母数字(或符号),而 PT 差了一个字符。Eggert 在注释里写道,他曾向 BC 省政府询问缩写方案,但没有得到回复。于是 tzdb 的注释里原样留下了逐个候选方案的点评。

候选tzdb 注释的评价
MST与既有软件和惯例最兼容。BC 部分地区和育空地区已经在 -07 上使用这个缩写
PDT大体没问题,但会让人误以为这实际上是夏令时,而它其实是标准时
PST直观,但更容易造成混淆,也可能破坏那些假定 PST 就是 -08 的软件
-07就其本身而言是准确的,但和相邻地区放在一起会显得别扭
PacT直观,但是个前所未有的新缩写

结论是 MST。所以 2026c 里的 zone 定义是这样的:

Zone America/Vancouver	-8:12:28 -	LMT	1884
			-8:00	Vanc	P%sT	1987
			-8:00	Canada	P%sT	2026 Mar  9
			# Temporary hack; see above.
			-8:00	1:00	PDT	2026 Nov  1  2:00
			# End of temporary hack.
			-7:00	-	MST

Zone America/Edmonton	-7:33:52 -	LMT	1906 Sep
			-7:00	Edm	M%sT	1987
			-7:00	Canada	M%sT	2026 Jun 18
			# Temporary hack; see above.
			-7:00	1:00	MDT	2026 Nov  1  2:00
			# End of temporary hack.
			-6:00	-	CST

温哥华变成 MST,埃德蒙顿变成 CST。这里能学到的实务教训只有一条 — 时区缩写不是标识符。 PST 就是 -08、MST 就是山地标准时的这种假设,如今在加拿大西海岸失效了。如果你的代码解析缩写、或者按缩写分支处理逻辑,现在正是该改用 IANA zone ID(America/Vancouver)的时候。

tzdb 为何刻意写入"不准确"的过渡日期 — CLDR 48.1 的限制

细心的话,你会发现上面的 zone 定义里有个奇怪之处。BC 的法定生效日是3月9日,艾伯塔省是6月18日,但数据里两处都写成了在11月1日02:00过渡。这正是注释里标着"Temporary hack"的部分。

原因在 tzdb 的注释里写得很明白。如果按法定日期,从3月9日起就把这段建模为"-07 标准时",CLDR 48.1(2026-01-08)会把这段区间的显示名称生成为"Pacific Standard Time" — 一个直接暴露给用户的错误:偏移量是 -07,名称却写成 -08 的标准时。所以 tzdb 暂时把3月9日到11月1日这段仍旧留作 PDT(夏令时),和以前一样。用注释里的话说,这段区间实际上是标准时,所以严格来说这个 hack 是错的,但 UT 偏移量是准确的,而且绕开了 CLDR 的限制。注释里也一并写下了计划:到11月,继续维持这个 hack 的理由本身就不存在了,届时可以将其移除。

艾伯塔省这边的注释还多了一层 — 艾伯塔省的 hack 在 CLDR 48.2(2026-03-17)及更早版本上同样是必要的,"因为跟进 CLDR 次要版本的平台很少,两个 hack 的实际效果相差不大"。

这件事有意思的地方在于方向。数据的源头(tzdb)为了迁就消费者(CLDR/ICU)的发布周期与限制而弯曲了数据。显示名称的供应链实际上是这样流动的 — tzdb 定义偏移量和过渡,CLDR 附加各语言的显示名称,ICU 把它打包进库,在此之上浏览器的 Intl API、Java、Android 才把时区名称呈现出来。按 ICU 的发布说明,ICU 78.2(2026-01-09)搭载的是 CLDR 48.1 + tzdata 2025c,ICU 78.3(2026-03-17)搭载的是 CLDR 48.2 + tzdata 2026a,预计10月发布的 ICU 79 计划搭载 CLDR 49 + Unicode 18。考虑到这个周期,就能理解"暂时弯曲到11月为止"这个选择是怎么来的了。

当然,这不是免费的。在 hack 还生效期间,温哥华和埃德蒙顿的夏天是法律上属于标准时,但数据里 isdst=1。按 tm_isdst 值分支处理的代码,以及显示缩写(PDT/MDT)的界面,在这段时间里看到的值都会和法律不一致。而在11月1日02:00,还会发生一次偏移量不变的"幽灵过渡" — PDT→MST、MDT→CST,只有名称和 isdst 发生变化。如果你的代码要统计过渡事件、或者假定每次过渡都会伴随偏移量变化,值得留意这种过渡是可能存在的。

摩洛哥,时隔8年重回 +00 — 到2087年的预测也被清除了

自2018年10月起,摩洛哥一直常年使用 +01,只在斋月期间降为 +00,每年重复两次过渡。按 2026c 的记录,这一体制结束了 — 政府以2026年6月25日的法令(第2.26.530号法令)废除了2018年的法令,从2026年9月20日02:00起永久回到 +00,不再有夏令时。西撒哈拉(Africa/El_Aaiun)同步变更。tzdb 引用的报道来源是 Morocco World News 的确认报道以及官方公报(第7521号 Bulletin Officiel)。

Zone Africa/Casablanca	-0:30:20 -	LMT	1913 Oct 26
			 0:00	Morocco	%z	1984 Mar 16
			 1:00	-	%z	1986
			 0:00	Morocco	%z	2018 Oct 28  3:00
			 1:00	Morocco	%z	2026 Sep 20  2:00
			 0:00	-	%z

而这次变更还附带了一次不太起眼的大扫除。因为斋月遵循伊斯兰历,每年在公历上的日期都会漂移,tzdb 此前一直靠天文计算,把到2087年为止的斋月过渡预先算好写成规则。2026b 的 africa 文件里 Morocco 规则有183行,到2026c 只剩57行 — 2027年到2087年的126行预测规则被整段删除。一个时区数据库居然装着长达60年的未来预测,这件事本身就说明这份数据有多特别。

这些预测规则的残留,还让陈旧数据的症状变得诡异。使用2026b 及更早数据、越过9月20日的系统,会把卡萨布兰卡算作 +01,快1小时。但被删掉的旧规则里有一条斋月预测是"2027-02-07降到+00,2027-03-14再回到+01",所以在那5周里,它会碰巧又变回正确,然后从3月中旬起再次出错。陈旧 tzdata 的 bug,就可能这样断断续续地出现。

2026a 那些悄无声息的默认值变化

如果说加拿大和摩洛哥是头条新闻,那么3月的2026a 就是一次改动默认值的发布。以 NEWS 为准,只挑出对工程师有意义的部分:

  • right(计入闰秒)的 TZif 文件从默认安装中移除了。 这是为系统时钟计入闰秒(不符合 POSIX)的环境准备的一套替代文件,由于主流发行版几乎不用,被从默认中排除。旧的默认值可以用 make REDO=posix_right 恢复,如果确实是计入闰秒的时钟,推荐使用 make REDO=right_only。闰秒本身走向何方,可以参考2026年依然没有闰秒 — 以及无人经历过的负闰秒一文。
  • 新增了编译选项 TZ_RUNTIME_LEAPS=0。 这是关闭运行时闰秒支持的选项,符合 POSIX,能减少攻击面和成本,但放弃了 RFC 9636(TZif 格式标准)的闰秒功能。
  • TZif 缩写空间从50字节扩大到256字节。 部分文件已经用到了40字节,可见余量本来就不多。如果你有一个老旧的自定义 TZif 解析器,现在有理由去检查它是否假定了50字节的上限。
  • 摩尔多瓦的历史数据被追溯修正了。 摩尔多瓦自2022年起按欧盟方式过渡(03:00提前、04:00推迟),但数据错误地一直停留在02:00/03:00。这是那种会改变过去时间戳在本地时间下解读方式的修正 — 很好地说明了 tzdata 的更新不只改动未来。
  • 自2019b起就已过时的 posixrules 机制被彻底移除。

此外,还有一条贯穿今年三个版本的线索 — zic(zone 编译器)的加固。 针对恶意输入的整数、缓冲区溢出修复,在2026a/b/c 中持续落地。NEWS 里列出的复现输入,比如 Zone Ouch 0 - LMT 9223372036854775807,明显带着模糊测试(fuzzing)的痕迹。从信任边界之外接收时区源文件、再用 zic 编译的系统应该不多,但这提醒我们 tzcode 同样是供应链的一部分。也有反方向的决定 — 2025c 里加固过的"非特权进程的 TZif 路径限制(强制常规文件、拒绝 .. 路径)"在 FreeBSD 上没能站稳,2026c 里把它撤销了。安全加固也要生态系统接受才能维持下去,这是基础设施维护中一个现实的侧面。

你的技术栈什么时候能拿到 2026c — 两个截止日期

版本发布出来并不是终点。我核对了实际的传播速度(以2026-07-17为准,只记录了从各项目的提交、包历史里直接核实到的内容)。

路径状态
Go(golang/go lib/time)发布后次日、也就是7月9日就合并了 2026c
Debian(tzdata 包)2026c-1 打包完成
Fedora最近一次 stable 更新还是 2026b-1(5月20日)
OpenJDK 主线合并到了 2026b(4月27日,JDK-8383175)。截至本文撰写,2026c 尚未合并
ICU最新的维护版本78.3(3月17日)搭载的是 tzdata 2026a

可以看出一个模式。发行版和 Go 以天为单位跟进;JDK 要等主线合并之后再搭上按季度发布的更新版本,才能真正抵达 JVM;而通过 ICU 拿到时区数据的平台(浏览器、部分嵌入式设备)则被绑定在 ICU 的发布周期上。再叠加上容器基础镜像的构建时点、JVM 自带的 tzdata 副本、pip 的 tzdata 包这类按运行时各自持有的副本 — 同一台服务器上,不同进程看到不同的时区数据,一点也不罕见。

以这次发布为准,值得写进日历的截止日期有两个。

  1. 2026-09-20 02:00 — 摩洛哥、西撒哈拉。 在此之前没有更新的系统,之后会把时间算作 +01,快1小时。
  2. 2026-11-01 02:00 — BC、艾伯塔省。 陈旧数据会执行一次并不存在的秋季拨回(fall back),把温哥华降到 -08、把埃德蒙顿降到 -07。之后就会慢1小时。等 NWT 的立法完成,2026d 可能会带来第三个条目。

自己的系统属于哪一边,用 zdump 就能立刻确认。

# 若已应用 2026c: 会看到9月20日 +01 -> +00 的过渡
zdump -v Africa/Casablanca | grep 2026

# 若已应用 2026c: 11月1日的过渡中 gmtoff 保持在 -21600 不变 (MDT -> CST)
# 若数据陈旧: gmtoff 会从 -21600 掉到 -25200 (MDT -> MST)
zdump -v America/Edmonton | grep 2026

反过来,这次也明显有可以不用理会的情况。如果你的服务只面向韩国用户,KST 与这次发布无关;只把过去的时间戳以 UTC 存储和运算的后端,光是偏移量的变化本身也不会导致它出问题。真正有风险的是三件事 — 相关地区未来的本地时间日程(把9月20日之后"卡萨布兰卡09:00的会议"用陈旧数据换算成 UTC 存下来,会在偏差1小时的那一刻触发)、展示给用户看的本地时间显示,以及依赖 isdst 或缩写的逻辑。

结语

tzdb 是一种奇特的基础设施 — "政治是输入,数据是输出"。今年的输入是加拿大两个省的立法和摩洛哥的一纸法令,输出则是三次(很快是四次)发布,以及为了 CLDR 生态系统而主动维持到11月的那一个"不准确的日期"。归纳一下:

  • BC(3月9日)和艾伯塔省(6月18日)在法律上已经是永久时间了,但 tzdb 里为了绕开 CLDR 48.x,写成了11月1日过渡,偏移量则始终是准确的。
  • 摩洛哥、西撒哈拉从9月20日02:00起永久 +00。在此之前,tzdata 2026c 需要铺到整个机队上。
  • 依赖时区缩写(比如假定 PST=-08)和 tm_isdst 的代码,在这个周期里会真的出问题。用 zone ID 和偏移量去计算,缩写只用来展示。

越是没人读发布说明的基础设施,往往越是要等丢失了一个小时之后,它的存在才会显现出来。一行 zdump,就足以提前知道。

参考资料

현재 단락 (1/93)

2026年7月8日,[IANA 时区数据库](https://www.iana.org/time-zones)发布了今年第三个版本 tzdata 2026c。这个数据库是一堆文本文件,记录着地球上每个...

작성 글자: 0원문 글자: 8,676작성 단락: 0/93