如何存储重复日期,牢记夏令时

How to store repeating dates keeping in mind Daylight Savings Time

提问人:Dave 提问时间:10/28/2013 最后编辑:Dave 更新时间:5/17/2016 访问量:19754

问:

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

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

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

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

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

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

但是 - 我想我会在这里问,希望也许有一个“正常”,或者一个我没有想到的简单事情。

(CakePHP 2.4.x)

php mysql cakephp 日期时间 dst

评论

0赞 vascowhite 10/28/2013
夏令时和时区最佳做法的可能重复

答:

144赞 Matt Johnson-Pint 10/28/2013 #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的调度程序,你正在尝试将时区支持改造到其中。

评论

0赞 Dave 10/28/2013
有没有一种好方法可以处理检查日期是否过去,或其他类似的简单“检查”?以前,如果以 UTC 格式存储所有日期,这很简单,因为我可以检查服务器时间。但现在,它似乎需要额外的步骤......有什么建议吗?
1赞 Matt Johnson-Pint 10/28/2013
如果存储下一个即时预计的 UTC 时间,则可以使用它来查询事件是即将到来还是已经过去。如果通过,则您可能希望在其他表中为过去的事件写入新记录,然后将 UTC 时间更新为下一个定期实例。
0赞 Matt Johnson-Pint 10/28/2013
您可以使用本地时间安排重复事件,并以 UTC 格式安排每次事件的过去实例。但是,请尝试将这些实体视为域中的独立实体。
2赞 musiphil 8/14/2015
在这五个项目中,最后两个总是可以从前三个计算出来,所以如果你决定存储它们,我会将它们视为缓存值。
1赞 Deep 1/16/2020
这是一篇内容丰富且结构良好的帖子。谢谢@MattJohnson品脱