处理UTC日期和未来



我刚刚发现,如果我们将来也要处理日期,那么在utc中存储日期是不理想的。这种情况似乎是因为,时区似乎比我们想象的更频繁地变化。幸运的是,我们似乎有IANA tzdb,它似乎会定期更新,但令人困惑的是,postgres似乎使用了数据库的特定版本,它似乎在构建时使用了该版本。。

所以,我的问题是,如果时区正在变化,夏令时正在进行,政治和地理调整正在发生,而我们的数据库没有最新的tzdb,我们如何能够跟踪系统中日期的准确性?此外,像date-fns-tz这样的库是否基本上不准确,无法解释新的时区变化?

理想情况下,我认为图书馆会对中央服务器进行网络调用,以维护最新的更改,但事实并非如此。最近的日期/时区更改通常是如何处理的?

IANA时区数据库收集关于世界各地何时生效的时区的全球知识。这些信息自然是不完整的,特别是当涉及到未来时。(IANA)时区不是与UTC的偏移量,而是一条规则,说明与UTC的哪个偏移量何时处于活动状态。EST不是那种意义上的时区,它是某个UTC偏移量的缩写。如果你住在纽约,你有时会有EST,有时会有EDT,这取决于美国/纽约时区的规则。当然,您应该更新时区数据库,但不是因为时间戳会更改(它们是不可变的),而是因为时间戳在某个时区中的显示方式可能会更改。

存储在数据库中的始终是UTC时间戳,因此时间戳本身是不可变的。改变的是表现。因此,如果你预测世界将于明年7月15日奥地利时间中午结束,而奥地利政府取消了夏令时,你的预测将推迟一个小时(除非你预计灾难将遵循奥地利立法)。如果您对此感到担忧,请使用UTC进行预测,或者至少将UTC偏移量添加到时间戳中。

如果您将timestamp with time zone存储在数据库中,并在timezone设置为Europe/Vienna的情况下查询它,您将得到一定的结果。如果您更新时区数据库,并且更新中反映了新的立法,那么同一查询明天将返回不同的结果。然而,它仍然是相同的时间戳,只有使用中的UTC偏移量会有所不同:

SELECT TIMESTAMP WITH TIME ZONE '2023-07-15 12:00:00+02'
= TIMESTAMP WITH TIME ZONE '2023-07-15 11:00:00+01';
?column? 
══════════
t
(1 row)

为了用一个例子进一步澄清@Laurenz在评论中的说法,让我们以萨摩亚为例,他们从GMT-11时区切换到GMT+13,跳过一整天。

虽然忽略了时区的实际含义(注释中有不同的类似意见),但出于以下计算的目的,我们只将其视为标准UTC的偏移值。此外,请注意,我使用自己的符号方式进行计算,但是,这是可以理解的,希望如此;-)

那么,萨摩亚在2011年12月29日跳过了一天,怎么了?根据我的发现,当午夜钟声敲响时,他们实际上跳过了周五。但是,unix时间戳保持等效/不变:

GMT-11
(-)GMT+13
__________
= 24hrs

Let, WST=GMT-11
2011-12-29 T 24:00:00 - 11 (clock strikes midnight)
= 2011-12-30 T 00:00:00 - 11 (WST)
= 2011-12-30 T 11:00:00      (UTC)  

now the switch occurs, WST=GMT+13
2011-12-31 T 00:00:00  + 13 (WST)
= 2011-12-31 T-13:00:00       (UTC)
= 2011-12-30 T 11:00:00       (UTC)

因此,在我看来,存储未来的日期并不会真正影响日期本身的价值。但是,它确实影响了日期的显示方式,例如,如果时区信息没有更新,人们仍然会将29日后的萨摩亚视为30日星期五。但是,在这种情况下,将是周五,格林尼治标准时间30日-11,而如果信息更新,则将是周六,格林尼治时间31日+13。所以,一切都很好。

更多详细信息,请参阅@Laurenz回答的评论部分

此外,正如@Adrian在上面提到的,处理时区的软件,如果支持转换,就会附带tzdb版本。postgres中的情况似乎也是如此,尽管您似乎可以将其配置为使用系统版本。对于这种情况,您必须更新软件或系统的数据库本身。

我知道您想要存储一个未来的时间点,比如"2078年7月5日上午10:00在澳大利亚/悉尼时区";,无论您再次检索时间点时该时区与UTC相比有多大偏移量。当时间到来时,这个时间点甚至可能不存在,因为它被跳过以引入夏令时(或者它可能存在不止一次)。

说XML模式,您想要存储的信息由组成

  • 不带时区偏移的dateTime,在给定的示例中为2078-07-05T10:00:00(没有尾随的Z)
  • 加上时区,在给定的示例Australia/Sydney中,时区以来自IANA数据库的字符串形式给出

我不知道如何最好地将其存储在PostgreSQL数据库中,无论是作为两个单独的字符串,还是作为一种特殊的数据类型。PostgreSQL文档中写道:

所有可识别时区的日期和时间都存储在UTC内部。在显示给客户端之前,它们将转换为TimeZone配置参数指定的区域中的本地时间。

在我看来,UTC值是固定的,如果在给定时区引入或取消夏令时,该时区的本地时间值可能会发生变化。(我说得对吗?)你想换一种方式:当地时间保持不变,在引入/取消夏令时后UTC值可能会改变。

例如,假设下次大选的投票站在我所在时区的2025-09-21T08:00:00+02:00开放。但如果我的国家在此之前取消夏令时,它们将在2025-09-21T08:00:00+01:00开放,没有明确的重新安排。换句话说:UTC时间发生了变化,但当地时间没有变化。

或者考虑一个航班,它存储了当地起飞时间和时区,持续时间为10小时,到达另一个时区。当出发时区的偏移量发生变化时,其本地到达时间就会发生变化,例如,因为该国在第X天引入或取消了夏令时,但到达时区的偏移不会发生变化。计算本地到达时间的应用程序在第X天或更晚执行时,必须显示更改后的到达时间,尽管存储的数据(本地出发时间、出发时区、到达时区和航班持续时间)没有更改。如果应用程序使用基于IANA时区数据库的库,并在第X天到来之前收到包括夏令时引入/废除在内的升级,则所需的更改可以自动发生。

有关此类库的示例,请参见https://day.js.org/docs/en/timezone/parsing-in-zone.

最新更新