PHP Daylight savings conundrum
我有一个关于处理夏令时的一般问题。我想它并不是真正特定于PHP的,但我是用PHP编写的,所以我认为包含它不会有什么坏处。
我有一个使用jqueryfullcalendar构建的日历应用程序。用户在其本地时区中查看事件,我的服务器将其存储为MySQL中的UTC日期时间。(StackOverflow上的其他问题表明这是处理时区的最佳方法。)因此每次用户保存或查看日历上的事件时都会进行转换。这工作很好,但我对如何最好地处理夏令时感到困惑。
例如,假设时区EST(东部标准时间)中的用户创建的事件不是每天下午3点重复的夏令时。我的PHP代码使用标准的datetime(和datetimezone)类在UTC和EST之间按UTC-5:00进行转换。当夏令时,时钟提前一小时,php将在UTC-4:00之前在UTC和EST之间转换。从用户的角度来看,事件从原来的下午3点移动到下午4点。这不是我的用户和我想要的。下午3点的活动应保持下午3点的活动,而不考虑日光节约。最好的方法是什么?在PHP中有没有一种方法可以忽略夏令时?
我的代码:
1 2 3 | $getDate = new DateTime($storedDate, new DateTimeZone('UTC')); $getDate->setTimezone(new DateTimeZone('America/New_York')); $getDateString = $getDate->format('Y-m-d H:i:s'); |
更多信息:(从我下面的评论中复制)
-只存储重复事件的第一次出现。根据用户请求的日历视图(月、周或日视图)动态创建所有其他事件。按照我对其进行编码的方式,它只创建将在视图上可见的引用。
-问题是,我还需要在其他时区保持它不变。要继续原来的示例,东部标准时间下午3点(不考虑夏令时),但如果在中部时间观看该事件,则该事件也应保持下午2点(不考虑夏令时)。
本质上,我需要忽略夏令时。
您是否尝试过查看datetimezone::getTransitions()?
http://www.php.net/manual/en/datetimezone.gettransitions.php
特别是使用[offset]和[isdst]属性。
- 当他们节省时间时,找到当前日期之前的第一个非DST转换。(通常是过去一年中的两个值之一)。使用非DST期间的偏移量进行转换
- 检索值时,如果您当前处于DST期间,请使用非DST期间的偏移量来转换时间,而不是当前偏移量。
以您的EST为例,在8月份,即使您在EDT中,也可以使用-5的EST转换保存值。
如果他们在1月份看到这个值,那么当你把这个值拉出来的时候,你加5,如果你在8月份,那么你加4。
这将适用于95%的情况,我假设开关是一致的。如果Eastern决定与Central合并,您可能会有运行–5/–4/–5/–4/–5/–5/–6/–5/–6/–6/–6的转换,这会把事情搞砸。
这个没有魔法子弹。我不知道你的应用程序结构的细节,你可能只需要尝试在你所处的任何一天的午夜增加3个小时,这样任何定期的每日约会都只存储为一个时间。
这是一个复杂的问题。我学到了一个困难的方法,不是每天都有86400秒长。
在处理各种日历应用程序时,我在早期做了一个设计决策,这省去了很多麻烦。也就是说,事件的每个实例在数据库中都有一个条目。
一个表用于所有事件信息(标题、说明等)。另一个表为该事件的每个实例保存了一个时间戳。当有人计划了一个重复事件(比如每周三下午3点),我会在每周三下午3点在他们的时区(实际上存储为UTC)中插入一个实例。现在,理论上的事件可以永远重复。我决定,对重复设置一个合理的限制(比如50年或100年)要比计算一个事件的所有时间要简单得多。对于用户来说,事件似乎永远都在发生,但在数据库中却不是这样。即使100年来每天都安排一个事件,在一个非常窄的表中,这也只有36500条记录。
使用这种方法,您必须考虑例外情况。有时,人们会更改一个事件实例的详细信息。在这些情况下,我只是创建了另一个事件并复制了相关的细节…因为它实际上是一个单独的事件。如果你想用一个组ID把它们都绑在一起,你可以。更改事件的单个计划很容易,因为每个实例都有一个单独的行。
我建议在大多数情况下使用这种方法。它为我节省了大量的麻烦,使我能够依靠一个数据库来完成所有的繁重工作,同时也解决了你的时区问题。