Time Zone Canonicalization ?
中文标题:时区规范化
- 阶段: 未分阶段
- 状态: 不活跃
- ECMAScript 版本: —
- 同步时间: 2026年11月3日
- English original · 官方仓库
ECMAScript 时区使用 IANA 时区数据库标识符,当某个时区的规范标识符发生变化时,不同实现可能不一致,现有代码也可能被破坏。该提案通过澄清规范、对齐实现,并让 Temporal 对标识符变化不那么敏感来处理这些规范化变更。它推迟在存储标识符时遍历 Link,并新增 Temporal.TimeZone.prototype.equals,用于比较两个时区是否解析为同一个 Zone。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
处理时区规范化变更
ECMAScript 中的时区依赖 IANA 时区数据库(TZDB)标识符,例如 America/Los_Angeles 或 Asia/Tokyo。
本提案改善了当时区在 TZDB 中的规范标识符发生变化时的开发者体验,例如从 Europe/Kiev 变为 Europe/Kyiv。
状态
本提案在 2023 年 7 月的 TC39 会议上达到 Stage 3。 Stage 3 审阅者是:Philip Chimento(@ptomato)、Daniel Minor(@dminor)和 Jordan Harband(@ljharb)。
该会议同意本提案应合并到同样处于 Stage 3 的 Temporal 提案中。 此次合并将通过向 Temporal 仓库提交规范性 PR 来完成,本提案的 champions 将很快提交该 PR。 该 PR 落地后,本仓库中未解决的问题将迁移到 ECMA-402 仓库,并可能通过后续对 ECMA-402 的规范性 PR 来处理。
以下是本提案推进到 Stage 3 的 2023 年 7 月 TC39 全体会议的幻灯片。
以下是本提案推进到 Stage 2 的 2023 年 5 月 TC39 全体会议的幻灯片。
以下是本提案推进到 Stage 1 的 2023 年 3 月 TC39 全体会议的幻灯片。
本提案的规范“堆叠”在 Temporal 提案之上,因为本提案建立在 Temporal 特性之上,尤其是内置对象 Temporal.TimeZone。
规范
本提案的规范文本可在 https://tc39.es/proposal-canonical-tz 找到。 本提案的规范变更很窄:大约 15 行规范文本变更(大多是现有抽象操作中的单行变更),外加一段说明文字。
提案负责人
- Justin Grant(@justingrant)
- Richard Gibson(@gibson042)
测试
覆盖本提案全部表面范围的 Test262 测试可在 https://github.com/tc39/test262/pull/3837 获取。
Polyfill
有一个非生产环境、仅供测试的 polyfill 可用。
本提案的 polyfill 堆叠在 Temporal 提案的非生产、仅供测试的 polyfill 之上。更多信息请参见本提案的 polyfill README。
目录
动机
实现之间的差异 + 规范与 Web 现实不符
模糊的规范文本:"在 IANA 时区数据库中"
用户投诉
- CLDR-9892:‘Asia/Calcutta’、‘Asia/Saigon’和‘Asia/Katmandu’即使自 1993 年起已过时,仍被当作规范标识符
- Chromium 580195:Asia/Calcutta 时区标识符应替换为 Asia/Kolkata
- chrome 中的 moment.tz.guess() 与 IE 中的 moment.tz.guess() 不同 · Issue #453 · moment/moment-timezone
- CLDR-5612:时区非常过时
- CLDR-9718:在 Zone - Tzid 映射和 Windows 映射中将 Asia/Calcutta 重命名为 Asia/Kolkata
- Asia/Kolkata 的规范时区名称不正确 · Issue #1076 · tc39/proposal-temporal
- [tz] Kyiv 不是 Kiev(来自 IANA TZDB 邮件列表)。
- WebKit 218542:Buenos Aires 返回的时区不正确
- Firefox 1796393:Javascript 返回有问题的时区,导致站点损坏
- Firefox 1825512:Europe/Kyiv 不是有效的 IANA 时区标识符
- 还能轻松找到几十个更多例子。
不能依赖静态数据随时间保持相同行为
使用 === 比较持久化标识符不可靠
Temporal 使这些问题更具破坏性
定义
与以下术语相关的更多细节可在 ECMA-262 的 时区标识符以及 Temporal 规范中修订 ECMA-402 的部分里的 IANA 时区数据库的使用中找到。
- 可用命名时区标识符 - TZDB 中的时区名称(并在 ECMAScript 中被接受)。 可用命名时区标识符仅由 ASCII 组成,并且不区分大小写。 目前有 594 个可用命名时区标识符(平均长度 14.3 个字符)。 目前每年新增的标识符不到 5 个。
- 主要时区标识符 - 某个可用命名时区的首选标识符。
- 非主要时区标识符 - 某个可用命名时区的其他标识符。 一个可用命名时区有一个主要标识符以及 0 个或多个非主要标识符。
- Zone - TZDB 术语,指由标识符命名的一组规则(参见 如何阅读 tz 数据库和 理论与实用)。
目前有 461 个 Zone(平均长度 14.9 个字符)。
除
Etc/GMT等罕见例外,TZDB 中的所有 Zone 在 ECMAScript 中都是主要时区标识符。 - Link - TZDB 术语,指没有自己的 Zone 记录,而是指向另一个标识符并使用其规则的标识符。
当 Zone 被重命名时,会向
backward添加一个从旧名称到新名称的 Link。 为更新城市拼写而进行的重命名每隔几年发生一次。 目前有 133 个 Link(平均长度 12.2 个字符)。 TZDB 中的 Link 在 ECMAScript 中通常是非主要时区标识符,但 TZDB 构建选项的差异意味着某些 TZDB 构建中的某些 Link 在 ECMAScript 中是主要时区标识符。 - 大小写规范化 - 将用户提供的 ID 匹配到 IANA TZDB 中的大小写。例如:
america/los_angeles⇨America/Los_Angeles - 规范化 - 返回 IANA Zone,必要时解析 Link。例如:Europe/Kiev ⇨ Europe/Kyiv
- CLDR 规范化 - V8 和 WebKit(通过 ICU 库)使用的 Zone & Link 数据
- IANA 规范化 - Firefox(在自定义 TZDB 构建中)使用的 Zone & Link 数据
提议的解决方案 - 摘要
以下步骤被设计为“可分离的”;如果因实现复杂性和/或缺乏共识而受阻,我们仍可继续推进其他步骤。
减少实现之间以及实现与规范之间的差异
- 已完成 - 简化抽象操作,处理时区标识符。
- 已完成 - 澄清规范,以防止更多分歧
- 帮助 V8 和 WebKit 更新 13 个过时的规范化,例如
Asia/Calcutta、Europe/Kiev和Asia/Saigon,以免在 Temporal 被广泛采用后这变得痛苦。(无需规范文本) - 用于减少实现之间差异的规定性规范文本。 此步骤需要在实现者以及 TG2(ECMA-402 团队)之间就规范化应如何工作找到共同点。
减少规范化变更的影响
- 避免可观察地跟随 Link。 如果规范化变更不影响现有代码,那么未来的规范化变更破坏 Web 的可能性就会小得多。 由于规范化是由实现定义的,此变更可能在 Temporal Stage 4 之后可以安全发布(也可能不可以;需要研究),但最好不要等太久。
- 添加
Temporal.TimeZone.prototype.equals。 因为(5)会停止在创建TimeZone对象时规范化 ID,所以有一种符合人体工程学的方式来知道两个TimeZone对象是否表示同一个 Zone 会很有帮助。
提议的解决方案 - 细节
1. 已完成 - 标识符相关术语和抽象操作的编辑性清理
状态:编辑性 PR 已合并为 tc39/ecma262#3035
最近落地到 ECMA-262 的一个 PR 重构了时区标识符的规范文本。 其中一项变更是新增了 时区标识符 说明性章节,定义与标识符相关的术语和概念。 另一项变更是重构与时区标识符相关的抽象操作,以便 ECMA-262、ECMA-402 和 Temporal 中其他与时区标识符相关的功能(包括本提案中的规范性变更)可以基于这些操作构建,而不必求助于新的、非实现定义的 AO:
AvailableNamedTimeZoneIdentifiers() - 返回一个由实现定义的 Records 列表,每个 Record 由以下部分组成:
[[Identifier]]:IANA TZDB 中的标识符[[PrimaryIdentifier]]:传递性 IANA Link 目标的标识符(跟随尽可能多的 Link 以到达 Zone),并与 ECMA-402 的 CanonicalizeTimeZoneName 中对"UTC"的特殊处理相同。
GetAvailableNamedTimeZoneIdentifier(_identifier_) - 过滤 AvailableNamedTimeZoneIdentifiers 的结果,返回其中 [[Identifier]] 与 _identifier_ 进行 ASCII 大小写不敏感匹配的 record,如果没有匹配则返回 ~empty~。
此 AO:
- 通过使用结果的
[[PrimaryIdentifier]],替换 Temporal 和 ECMA-402 中的CanonicalizeTimeZoneName。 - 通过将结果与
~empty~比较,替换 Temporal 中的IsAvailableTimeZoneName和 ECMA-402 中的IsValidTimeZoneName。 - 通过使用结果的
[[Identifier]],获取大小写规范化后的时区标识符。 此能力将作为本提案的一部分使用。
本提案的规范文本堆叠在这些最近合并的编辑性变更之上。
2. 已完成 - 澄清规范以防止更多分歧
状态:编辑性 PR 已合并为 tc39/proposal-temporal#2573。
Temporal 规范的 ECMA-402 部分现在包含一个新的 时区数据库的使用章节,推荐构建和处理 IANA 时区数据库更新的最佳实践。
这些建议的范围足够宽泛,可以涵盖 Firefox 和 V8/WebKit 中的现有实现,但不会比这更宽泛。
例如,所有 ECMAScript 实现似乎都避免使用执行过度急切规范化的默认 TZDB 构建选项,例如 Atlantic/Reykjavik=>Africa/Abidjan。
我们将建议所有实现遵循此最佳实践。
这些变更的目标是提供一个 Web 现实基线,供进一步的跨实现对齐在此基础上构建。
3. 修复 V8/WebKit 中过时的规范化
状态:在 2023-03-29 的 CLDR 会议上,CLDR 同意制定一项设计提案,以提供 IANA 当前规范化信息。 请关注 https://unicode-org.atlassian.net/browse/CLDR-14453 以了解进展更新。
下面的列表显示了 13 个 Link,它们在 IANA 和 Firefox 中已被取代,但在 CLDR 中仍然规范化为“旧”标识符(因此也影响 ICU,从而影响 V8 和 WebKit)。 下面的数据来自 TZDB 的 2022g 版本,通过一个测试浏览器规范化行为的简单 CodeSandbox 应用获得。
修复这些过时的 Link 有一定紧迫性,因为如上文所述,在 Temporal 被广泛采用后更改规范化将导致更多变动和客户投诉。
为了修复这些过时的 Link,我们将与 V8 和 WebKit 的代表(也许还有 ICU 和 CLDR 的代表)合作,看看是否有快速、低成本的方法让 V8/WebKit 更新这 13 个 Zone 的规范化。
请注意,TZDB 标识符的重命名非常少见。 从对 TZDB NEWS 文件的审查来看,2020-2022 年每年只有一次重命名。 在那之前,最后一次重命名是 2016 年的 Rangoon => Yangon。 理想情况下,ICU 会为这些过时标识符提供及时的解决方案。 但与此同时,如果实现必须在特殊案例列表中硬编码覆盖,那么它不需要经常更新。
4. 用于减少实现之间差异的规定性规范文本
状态:目前看来我们不太可能很快获得这种跨实现者协议。因此,我们可能会从本提案的范围中移除这一步,并另行并行跟进。
此步骤涉及实现者和 TG2 之间就规范化应如何工作达成一致。 它可能需要就 ECMAScript 应依赖哪个外部规范化来源(IANA 或 CLDR)达成一致(或提出建议),以及(如果选择 IANA)应使用哪些 TZDB 构建选项。 在这里取得进展需要理解相关权衡的规范制定者和实现者的输入。 请注意,一个可接受的结果可能是“同意各自保留不同意见”,只要我们能就大部分内容达成一致。 我们不需要完美对齐来减少生态系统差异。
在上述 (3) 取得进展后,就这段规范文本达成共识可能会容易得多,因为那 13 个过时的 Link 是当前 Firefox 与 V8/WebKit 之间规范化行为的最大差异。
5. 推迟遍历 Link 的规范化
这一规范性变更将推迟 Link 遍历,使 Link 标识符能够存储在 ZonedDateTime、TimeZone 和 Intl.DateTimeFormat 的内部槽中,从而可以将其返回给用户。
此变更的理由是规范化本身就有问题,因为它总是至少让一些人感到不满:现有代码的开发者在其代码行为不同时会感到恼火,而其他开发者在使用了过时标识符时也会感到恼火。
为了规避这两个问题,这一提议的变更将使规范化对 Temporal 用户几乎不可见,只有一个地方例外:从 Temporal.Now 返回的时区标识符。
此变更的一个权衡是,比较 TimeZone 对象的字符串表示(或其 id 属性,或 Temporal.ZonedDateTime 的时区槽)将不再是测试相等性的可靠方式。
因此,下面的 (6) 提议新增一个 TimeZone.prototype.equals API。
此变更需要以下规范性编辑:
- a) 在解析和/或存储用户输入标识符的地方,将
GetAvailableNamedTimeZoneIdentifier(id).[[PrimaryIdentifier]]改为GetAvailableNamedTimeZoneIdentifier(id).[[Identifier]]。 - b) 在将标识符用于需要规范化的目的(例如
TimeZoneEquals抽象操作)之前,调用GetAvailableNamedTimeZoneIdentifier(id).[[PrimaryIdentifier]]。
这些变更描述在本提案的规范文本、polyfill 和 测试中。
一些与性能相关的说明:
- 存储用户输入的标识符字符串并不必要,因为标识符在存储前会由
GetCanonicalTimeZoneIdentifier进行大小写规范化。 标识符不到 600 个,因此内置时区标识符可以存储为指向约 9KB ASCII 字符串数组的 2 字节(甚至 10 位)索引。 - 本提案不会要求在每 个
TimeZone、ZonedDateTime和Intl.DateTimeFormat实例中同时存储原始和规范 ID 索引。 实现可以选择这样做以简化实现,但它们也可以通过一个 2.3KB 的映射(将每个标识符的索引映射到其对应 Zone 的标识符索引)进行即时规范化,从而每个实例节省 1-2 字节。
6. 添加 Temporal.TimeZone.prototype.equals
最后一步将把 Temporal 的 TimeZoneEquals 暴露给 ECMAScript 代码,使开发者能够比较两个时区,看看它们是否解析为同一个 Zone。
这个 equals 模式与 ZonedDateTime 和 PlainDate 等其他 Temporal 类型提供相等比较的方式一致。
ZonedDateTime.prototype.equals API 的行为不会改变,因为它(就像除返回标识符的 API 之外的所有其他 API 一样)会在使用时区标识符之前进行规范化。
将此新 API 包含在本提案中而不是等到以后的一个原因是,它可以防止 tz1.id === tz2.id 模式在 ECMAScript 代码中变得流行,因为该模式将被本提案破坏。
没有此 API 时,仍然可以测试规范相等性,只是不那么符合人体工程学:
长期扩展(不在本提案范围内)可以被添加,以在创建时强制规范化:
话虽如此,暴露规范标识符在每个软件平台都一直是令人烦恼的根源。 因此,添加使规范化更可见的 API 可能会招致更多用户投诉。 这是将这些类型的 API 推迟到以后提案的另一个好理由。 :smile:
TZDB 大小计算
上面用于 V8 和 WebKit 的 CLDR Link 和 Zone 与 Firefox 使用的 IANA 数据并不完全相同。 但 CLDR 足够接近,因此上面的数字应在 Firefox 统计数据的 20% 以内。
参考资料
ICU4X
Rust 本地化 API(包括对 Temporal 友好的 时区 API),目前正在实现中。
关于规范化 API 的讨论见 https://github.com/unicode-org/icu4x/issues/2909。
IANA 时区数据库(TZDB)
时区数据的标准仓库,包括 Link 和 Zone 标识符,以及计算任意时区在某一时刻的 UTC 偏移所需的数据。
通过向 eggert/tz 仓库提交 PR 进行维护,并在 TZDB 邮件列表上讨论。
TZDB 仓库中的数据并非设计为直接原始使用。 相反,该仓库的 MAKEFILE 提供各种构建选项,这些选项会生成供应用程序使用的数据文件。 构建选项的变化可能产生非常不同的输出,包括规范化行为的巨大差异。
本提案的目标之一是定义 ECMAScript 实现应使用这些构建选项中的哪些。
global-tz 仓库
提供预构建的 TZDB 数据文件,使用的构建选项比默认 TZDB 构建选项更符合 Java(以及 ECMAScript)的需求。
global-tz 中的文件被声称(由 TZDB News 文件)与使用 make PACKRATDATA=backzone PACKRATLIST=zone.tab 构建 TZDB 的结果相同。
此构建配置消除了(从 ECMAScript 的角度看)不希望的合并,例如 Atlantic/Reykjavik 和 Africa/Abidjan 等不相关时区的合并,这些时区未来可能会分化。
此外,此构建配置与 Firefox 使用的 Zone 和 Link 类似。
此仓库由 JSR-310 的 champion 维护,JSR-310 是当前的 Java 日期/时间 API,他还维护 Joda 日期/时间 API 库,供较旧的 Java 实现使用,JSR-310 也基于该库。
注意,Temporal.ZonedDateTime 的字符串序列化格式(包括 IANA 时区标识符的使用)被设计为可与 java.time.ZonedDateTime 互操作。
此外,ECMAScript 的时区用例与 Java 类似。
因此,这可能是一个有用的标准 TZDB 构建配置,可考虑在 ECMAScript 中推荐。