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

时间:2013-10-28 02:07:31

标签: php mysql cakephp datetime dst

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

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

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

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

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

我目前的想法是,我会存储'dst'minitint(1),以确定是否在DST期间输入了开始/结束日期,然后在必要时制作一个方法将时间改变一小时。< / p>

但是 - 想想我会在这里问这里或许这是一个“正常”,或者是一些我没想过的容易的东西。

(cakephp 2.4.x)

1 个答案:

答案 0 :(得分:100)

首先,请注意在现代术语中,您应该说UTC而不是GMT。除了更准确地定义UTC之外,它们大多是等效的。保留GMT一词是指英国的时区部分,该部分在冬季月份生效,其偏移量为UTC + 0.

现在回答你的问题。 UTC不一定是存储所有日期和时间值的最佳方式。它对于过去的事件或未来的绝对事件特别有效,但对于未来的本地事件来说效果不是很好 - 尤其是未来重复事件。

我最近写了这个on another answer,并且是当地时间比UTC更有意义的少数例外情况之一。主要论点是“闹钟问题”。如果您按闹钟设置闹钟,那么您将在DST过渡的那天提前或延迟一小时醒来。这就是大多数人按当地时间设置闹钟的原因。

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

  • 定期活动的当地时间,例如“08:00”
  • 表示当地时间的时区,例如“America / New_York”
  • 重复发生模式,无论以何种格式对您的应用程序有意义,例如每日,每两周或每月的第三个星期四等。
  • 下一个立即 UTC日期和时间相当于您可以预测的最佳日期和时间。
  • 也许(但并非总是),未来事件UTC日期和时间的列表,预测到未来的某个预定义时间段(可能是一周,也许是6个月,也许是一年或两年,具体取决于您的需求)。

对于最后两个,请了解如果负责该时区的政府决定更改任何内容,则任何本地日期/时间的UTC等效值都会发生变化。由于每年都有多个时区数据库更新,因此您需要定期制定subscribe to announcements of updatesupdate your timezone database计划。每当您更新时区数据时,您都需要重新计算所有未来事件的UTC等效时间。

如果您计划显示跨越多个时区的任何类型的事件列表,那么拥有UTC等效项非常重要。这些是您要查询以构建该列表的值。

要考虑的另一点是,如果在DST回退转换期间为本地时间安排事件,则必须确定事件是发生在第一个实例(通常是)还是第二个实例(有时),或两者(很少),并在你的应用程序中构建一个机制,以确保事件不会发生两次,除非你想要它。

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

替代方法

我有几个人向我展示了一种技术,他们使用UTC时间进行调度,即他们在当地时间选择一个开始日期,将其转换为UTC进行存储,并存储时区ID。然后在运行时,他们应用时区将原始UTC时间转换回本地时间,然后使用该本地时间计算其他重复次数,就好像它是最初存储的那样。

虽然这种技术将工作,但缺点是:

  • 如果时区更新在第一个实例运行之前更改了本地时间,则会关闭整个计划。这可以通过为“第一个”实例选择过去的时间来缓解,这样第二个实例就是第一个实例。

  • 如果时间真的是应该跟随用户的“浮动时间”(例如手机上的闹钟),你仍然需要存储该区域的时区信息。最初创建的 - 即使这不是您想要运行的区域。

  • 它增加了额外的复杂性而没有任何好处。我会保留这种技术,用于您可能只有一个UTC调度程序,您正试图将时区支持改进。