48

我将事件存储在我的数据库中。我有“开始”和“结束”日期时间、“tickets_start”和“tickets_end”(用于售票实际开始/结束的时间 - 与实际活动的开始/结束时间相反)。

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

我将时区存储在具有“America/New_York”之类的值的 varchar 字段中。

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

起初,我认为这没什么大不了的,但后来意识到 - 如果最初的开始日期是 7 月(例如),并且它每个月重复一年,那么在某个时候,夏令时会成功这样从 GMT 的转换就会以不同的方式改变时间。一个月,当转换 12:00 时,它会变成 -5,而下一个月,由于 DST,它会变成 -4。

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

但是 - 我想我会在这里问,希望这可能有一个“正常”或者一个我没有想到的简单的东西。

(cakephp 2.4.x)

4

1 回答 1

135

首先,请认识到,在现代术语中,您应该说 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 的调度程序,而您正试图将时区支持改造成其中。

于 2013-10-28T04:47:39.083 回答