数据质量

夏令时切换以及在切换前后出错的请求

在实行夏令时的时区,每年有两次时钟会发生一些奇怪的事情,而大多数软件从未考虑过。当时钟拨快时,有一小时的当地时间根本不存在,因此那天像“凌晨 2:30 点”这样的时间戳实际上从未出现过。当时钟拨回时,有一小时会出现两次,因此“凌晨 1:30 点”在切换前出现一次、切换后又出现一次,而一个没有其他信息的单纯当地时间戳无法告诉您指的是哪一个。

这并不是只在特殊用例中才重要的罕见边缘情况。它是每年在每个实行夏令时的时区都会重复发生的日常日历事件,并且会悄无声息地破坏任何把当地时间视为始终定义明确的代码。一个排期系统如果只存储“预约凌晨 1:45 点”,而不同时存储预约时刻的 UTC 偏移量,那么当预约恰逢切换之夜时,几个月后它就无法知道原本指的是两个凌晨 1:45 点中的哪一个。

最稳妥的做法是在内部所有地方都以 UTC 存储时间戳,只在显示时转换为当地时间。UTC 没有夏令时,也没有歧义,因此一个时刻一旦以这种方式记录下来,无论之后当地时钟规则如何变化,它都保持正确。只在向用户展示时才转换为用户所在的当地时区,并使用该时区在那个特定时刻的现行规则。这正是为什么值得使用能接受特定时间点(而不仅仅是位置)的时区查询,而不是缓存一次偏移量后反复使用。

如果您构建任何安排或记录事件的系统,值得明确测试的边缘情况包括:在春季拨快切换期间为不存在的那一小时所做的预约,秋季拨回切换中重复那一小时内的时间戳,以及从创建到发生之间日期跨越了一次切换的已排期事件。这些都不是假设。它们每年都按固定、可预测的时间表发生,在开发阶段测试一次,远比排查一张关于会议似乎挪动了一小时的支持工单要省事得多。

如果您的系统需要的是某个特定时刻的正确偏移量,而不仅仅是时区名称,请直接请求它,而不是从缓存的值推算。My Geocode 的时区查询接受一个特定时刻,并应用该确切日期的夏令时规则,因此即使恰好处在切换的边缘,返回的偏移量也是正确的。