8

这是一个很难的问题。事实上,它似乎很难,SQL 标准和大多数主要数据库在它们的实现中都没有线索。

将所有日期时间转换为 UTC 可以轻松比较记录,但会丢弃时区信息,这意味着您不能对它们进行计算(例如,将 8 个月添加到存储的日期时间),也不能在它们存储的时区中检索它们。所以幼稚的方法就出来了。

除了时间戳之外存储与 UTC 的时区偏移量(例如,在 postgres 中带有时区的时间戳)似乎就足够了,但是由于 DST,不同的时区可以在一年中的某个时间点具有相同的偏移量,并且在 6 个月后具有不同的偏移量. 例如,您现在(8 月)可以在 UTC-4 拥有纽约和智利,但 11 月 4 日之后,纽约将是 UTC-5,智利(9 月 2 日之后)将是 UTC-3。因此,仅存储偏移量也不允许您进行准确的计算。像上面的幼稚方法一样,它也丢弃了信息。

如果您将时区标​​识符(例如 America/Santiago)与时间戳一起存储会怎样?这将允许您区分智利日期时间和纽约日期时间。但这还不够。如果您要存储到期日期,例如 6 个月后的午夜,并且 DST 规则发生变化(不幸的是政客们喜欢这样做),那么您的时间戳将是错误的,并且到期可能发生在晚上 11 点或凌晨 1 点。这对您的应用程序可能或可能不是什么大问题。所以使用时间戳也会丢弃信息。

似乎要真正准确,您需要使用时区标识符存储本地日期时间(例如,使用非时区感知时间戳类型)。为了支持更快的比较,您可以缓存它的 utc 版本,直到您使用的时区数据库被更新,然后如果缓存值已更改,则更新它。因此,这将是 2 个简单的时间戳类型加上一个时区标识符和某种外部 cron 作业,用于检查时区数据库是否已更改并为缓存的时间戳运行适当的更新查询。

这是一个准确的解决方案吗?还是我还缺少什么?能不能做得更好?

我对 MySQL、SQL Server、Oracle、PostgreSQL 和其他处理 TIMESTAMP WITH TIME ZONE 的 DBMS 的解决方案感兴趣。

4

4 回答 4

1

你已经很好地总结了这个问题。可悲的是,答案是做你所描述的。

使用的正确格式确实取决于时间戳应该代表什么的语用学。它通常可以分为过去和未来的事件(尽管有例外):

  • 过去的事件可以而且通常应该被存储为永远不会被重新解释的东西。(例如:带有数字时区的 UTC 时间戳)。如果应保留指定的时区(以向用户提供信息),则应将其分开。

  • 未来的事件需要您描述的解决方案。本地时间戳和命名时区。这是因为您想在时区规则更改时更改该事件的“实际”(UTC) 时间。

我会质疑时区转换是否是这样的开销?它通常很快。如果您看到非常显着的性能影响,我只会经历缓存的痛苦。有(正如您所指出的)一些需要缓存的大操作(例如根据实际(UTC)时间对数十亿行进行排序。

如果出于性能原因需要在 UTC 中缓存未来的事件,那么可以,您需要制定一个流程来更新缓存的值。根据数据库的类型,这可能由系统管理员完成,因为 TZ 规则很少更改。

于 2012-08-25T15:52:43.367 回答
0

如果您关心偏移量,则应存储实际偏移量。存储时区标识符与时区可以并且确实随时间变化不同。通过存储时区偏移量,您可以计算出事件发生时正确的本地时间,而不是根据当前偏移量计算本地时间。如果了解实际发生的时区事件很重要,您可能仍希望存储时区标识符。

请记住,时间是一种物理属性,但时区是一种政治属性。

于 2012-08-25T15:59:57.587 回答
0

如果您转换为 UTC,您可以订购和比较记录如果您添加时区的名称,您可以在其原始 tz 中表示它,并能够添加/减去时间段,如周、月等(而不是经过的时间) .

在您的问题中,您说这还不够,因为 DST 可能会更改。DST 使计算日期(除了经过的时间)变得复杂且代码密集。就像您需要代码来处理闰年一样,您需要考虑对于给定的数据/期间是否需要应用 DST 校正。几年来,答案是肯定的,而其他年份的答案是否定的。请参阅此 wiki 页面,了解这些规则变得多么复杂。

存储偏移量基本上是存储这些计算的结果。该计算出的偏移量仅对给定的时间点有效,并且不能像您在问题中建议的那样按原样应用于稍后或更早的时间点。您对 UTC 时间进行计算,然后根据当时在该时区中处于活动状态的规则将结果时间转换为所需的时区。

请注意,在第一次世界大战之前,任何地方都没有任何 DST,并且数据库中的日期/时间系统可以完美地处理这些情况。

于 2012-08-25T17:11:49.357 回答
0

我对 MySQL、SQL Server、Oracle、PostgreSQL 和其他处理 TIMESTAMP WITH TIME ZONE 的 DBMS 的解决方案感兴趣。

Oracle 即时转换为 UTC,但根据您传递的内容保持时区或 UTC 偏移量。Oracle(正确地)在时区和 UTC 偏移之间产生差异,并返回您传递给您的内容。这只需要额外的两个字节。

Oracle 使用 UTC 进行所有计算TIMESTAMP WITH TIME ZONE。这对于添加月份没有影响,但对于添加天数会有所不同,因为没有夏令时。请注意,计算的结果必须始终是有效的时间戳,例如,将一个月添加到 1 月 31 日将在 Oracle 中引发异常,因为 2 月 31 日不存在。

于 2017-04-24T06:21:44.217 回答