一尘不染

如何存储重复日期并牢记夏时制

mysql

我将事件存储在数据库中。我有“开始”和“结束”日期时间,“ tickets_start”和“ tickets_end”(用于票务销售实际开始/结束的时间-
而不是实际事件的开始/结束)。

到目前为止,我已经构建了可以完成所有有趣工作的方法,例如在保存之前将日期/时间转换为GMT,然后返回各自的时区进行显示。

我将时区存储在varchar字段中,其值类似于“ America / New_York”。

但是-现在,如果用户希望允许重复事件,则需要开始处理。我以前做过,这没什么大不了的,但是从来没有跨越多个时区。

起初,我认为这没什么大不了的,但后来意识到-
如果初始开始日期是在7月(例如),并且每年重复一个月,一年中的某个时候,夏令时将使它成为现实因此从GMT转换会改变时间。一个月,转换12:00时,它将更改为-5,下个月,由于DST,它将更改为-4。

我目前的想法是,我将存储一个’dst’tinyint(1)来确定是否在DST期间输入了开始/结束日期,然后在必要时创建一种将时间更改一个小时的方法。

但是,我想在这里问,希望这可能是“正常的”,或者是我没有想到的简单的事情。

(cakephp 2.4.x)


阅读 362

收藏
2020-05-17

共1个答案

一尘不染

首先,请注意,在现代术语中,您应该说UTC而不是GMT。它们几乎是等效的,只是更精确地定义了UTC。保留GMT一词,是指英国时区中在冬季有效的部分,其偏移量为UTC
+ 0。

现在到您的问题。UTC不一定是存储所有日期和时间值的最佳方法。它对于 过去的 事件或将来的 绝对 事件特别有效,但对于将来的 本地 事件-
尤其是将来的 重复 事件,效果不佳。

我最近在另一个答案中写到了这一点,这是少数几个例外情况之一,其中本地时间比UTC更有意义。主要论点是“闹钟问题”。如果您通过UTC设置闹钟,那么您将在DST转换的当天早或晚醒来一个小时。这就是为什么大多数人都按当地时间设置闹钟的原因。

当然,如果您要处理来自世界各地的数据,则不能 存储本地时间。您应该存储一些不同的东西:

  • 重复活动的当地时间,例如“ 08:00”
  • 表示当地时间的时区,例如“ America / New_York”
  • 重复模式,无论哪种格式,对于您的应用程序来说都是有意义的,例如每天,每两周或每月的第三个星期四等。
  • 下一个 立即 UTC日期和时间等效,以便可以进行最佳投影。
  • 也许(但不总是)未来时间UTC日期和时间的列表,预示了将来的某个预定义时间段(可能是一周,可能是6个月,可能是一两年,这取决于您的需求)。

对于后两个,请理解,如果负责该时区的政府决定更改任何内容,则可以更改任何本地日期/时间的UTC。由于每年都有多个时区数据库更新,因此您将需要制定一个计划来订阅更新公告并定期更新您的时区数据库。每当您更新时区数据时,都需要重新计算所有未来事件的UTC等效时间。

如果您打算显示跨越多个时区的任何类型的事件列表,那么具有UTC等效项就很重要。这些是您将查询以构建该列表的值。

要考虑的另一点是,如果事件安排为在DST后备过渡期间发生的本地时间进行,则您必须决定该事件是发生在第一个实例(通常)还是第二个实例(有时),或两者(很少),并在您的应用程序中内置一种机制,以确保事件不会触发两次,除非您希望这样做。

如果您正在寻找一个简单的答案-抱歉,没有答案。跨时区安排未来事件是一项复杂的任务。

替代方法

我也曾有过一些人告诉我的技术,他们
使用UTC时间安排,这是他们选择在本地时间起始日期,将其转换成UTC存储,和时区ID存储为好。然后在运行时,他们应用时区将原始UTC时间转换回本地时间,然后使用该本地时间来计算其他递归,就好像它是最初存储的上述那样。

尽管此技术 有效 ,但缺点是:

  • 如果有一个时区更新会在运行第一个实例之前更改本地时间,那么它将使整个计划失效。可以通过为“第一个”实例选择过去的时间来减轻这种情况,以使第二个实例实际上是第一个实例。

  • 如果时间确实是应该跟随用户的“浮动时间”(例如在手机的闹钟中),则您仍然必须存储最初创建时区的时区信息-即使那不是您要在其中运行的区域。

  • 它增加了额外的复杂性而没有任何好处。对于可能要使用时区支持的仅UTC调度程序的情况,我会保留此技术。

2020-05-17