YourWorldTime

那些不是整点小时的 UTC 偏移

· 约 1 分钟

全世界约五分之一的人生活在与 UTC 相差非整数小时的偏移上。假定并非如此的软件,对这些人不是稍微算错,而是错 30 或 45 分钟,这更糟。

「二十四个整点时区」这套心智模型错得代价高昂:它写出的代码会对远超十亿的人口自信地偏出 30 或 45 分钟。整整一小时的错至少看起来像个错。四十五分钟的错看起来像个笔误,然后就随版本发出去了。

完整清单

:30 的偏移

偏移 何处
UTC-09:30 马克萨斯群岛
UTC-03:30 纽芬兰与拉布拉多
UTC+03:30 伊朗
UTC+04:30 阿富汗
UTC+05:30 印度、斯里兰卡
UTC+06:30 缅甸、科科斯群岛
UTC+09:30 澳大利亚中部 —— 北领地、南澳大利亚州
UTC+10:30 豪勋爵岛(标准时间)

:45 的偏移

偏移 何处
UTC+05:45 尼泊尔
UTC+08:45 西澳大利亚州东南部(尤克拉,非官方)
UTC+12:45 查塔姆群岛(标准时间)

单是印度就有 14 亿人。再加上伊朗、阿富汗、缅甸、斯里兰卡和尼泊尔,还没算澳大利亚或加拿大,就已经超过地球人口的五分之一了。

另一类不寻常的偏移

非整数小时的偏移是众所周知的怪异之处。另外两桩同样重要。

跨度是 26 小时,不是 24。 基里巴斯的基里蒂马蒂岛在 UTC+14;贝克岛和豪兰岛在 UTC-12。这是 26 小时的跨度,也就是说,存在这样的时刻:地球上同时有三个不同的日历日期在使用。任何假定「两天的窗口覆盖所有时区」的代码,每天要错两次。

夏令时也不总是一小时。 常住人口约 380 人的澳大利亚小岛豪勋爵岛拨动 30 分钟:冬季 UTC+10:30,夏季 UTC+11:00。它是地球上唯一这么做的地方,也是日期库最好的测试样例——任何把 60 分钟跳变写死的实现,只在那里出错,别处都对,也就是说它会通过你写的其他每一个测试。

什么会坏

四舍五入到最近的整点。 最常见的失败。把偏移按整数小时计算的代码会悄悄把印度截断成 +5,这对该国每一个用户来说都错了 30 分钟。

把偏移存成整数。 一个声明为小时计数的表列根本表示不了加德满都。这通常要等数据都进去了才被发现。

建立在小时列之上的网格界面。 任何把一天铺成 24 个等宽格子、并假定每个时区的当地钟点都落在格子边界上的日历或规划器,都会把半小时和 45 分钟的时区放错位置。本站会议规划器的时间带用「时刻」而不是「当地钟点」作为列,所以在 UTC 那一行显示 04:00 的那一列上,+05:45 那一行显示 09:45——这是实情,而不是一个更整齐的谎。

跨越 30 分钟夏令时切换的时长运算。 假定一次夏令时切换正好是 3,600,000 毫秒,在豪勋爵岛会给出错误答案,而且只在那里。

怎样做对

补救办法和几乎所有时区缺陷的补救办法一样:永远不要自己算偏移。 去问平台。

任何现代运行环境都自带 IANA 数据库,并会以分钟为单位、正确地告诉你某个时区在某一时刻的偏移,加德满都和豪勋爵岛也不例外。在 JavaScript 里,那就是带 timeZoneName: 'longOffset'Intl.DateTimeFormat,或者把该时区的钟面读数与 UTC 相比——本站引擎走的正是后一条路,因为即便面对 1900 年以前数据里精确到秒的地方平太阳时偏移,它也依然精确。

然后守住三条规则:

  1. 偏移的单位是分钟,绝不是小时。如果一个变量叫 offsetHours,它本身就已经是个缺陷。
  2. 拿加德满都和豪勋爵岛来测。 别的一切都会放任你保留的那两个假设,正是被这两处打破的。
  3. 绝不要对时区做四舍五入。 如果显示不得不是近似的,就说清楚;不要为了让排版好做,悄悄把用户的钟挪走 30 分钟。

一个算例:45 分钟对网格做了什么

设想一个会议规划器,把一天铺成 24 列、每列一小时,并给每位参与者的工作时段上色。天真的实现按时区偏移的小时数平移基准行来算出每一行。

伦敦对纽约时这样是行的:纽约那行就是伦敦那行右移五格。伦敦对加德满都时就不行了。加德满都快 5 小时 45 分,也就是 5.75 格,而四分之三格这种东西并不存在。实现于是四舍五入——通常入到 6——此后那一行的每一个读数都早了 15 分钟。

这个误差小到足以通过评审,又大到足以要紧:网格标称加德满都 09:00 的那场 09:00 会议,在当地其实是 08:45,加入的人在自己那一天里迟到了一刻钟。

补救办法是别再把列当成小时。让每一列成为一个_时刻_,然后问平台:在那个时刻,每个时区的当地钟面读数是多少。于是 +05:45 那一行就会在 UTC 行显示 04:00 的地方显示 09:45——明显没有对齐网格,而这才是诚实的呈现。本站的会议规划器正是这么做的,加德满都那一行参差不齐的边缘是一项优点。

历史上的偏移更古怪

在标准化之前,偏移就是地方平太阳时,精确到秒。IANA 数据库把它们都记着。

荷兰在 1909 至 1937 年间处于 UTC+00:19:32.13。利比里亚直到 1972 年都在 UTC-00:44:30。去问运行环境 1920 年某一天 Europe/Amsterdam 的偏移,它会连秒一起给你确切的数值。

这正是本站底下那个偏移函数按秒计算、只在显示层才舍入到分钟的原因。一个按分钟存储的引擎会悄悄丢掉荷兰历史里的 32 秒——没人会察觉,而它依然是错的。