PHP 处理夏令时的时区

Posted

技术标签:

【中文标题】PHP 处理夏令时的时区【英文标题】:timezone with DST handling by PHP 【发布时间】:2012-11-21 20:03:01 【问题描述】:

我正在开发一个日历应用程序。在此,来自不同时区的用户创建/查看事件。 我正在考虑按照以下方法将事件时间保存为 UTC 并以用户的本地时间显示。注意:用户有他们喜欢的时区设置。

将事件开始时间保存为 UTC 时间戳:

$timezone = new DateTimeZone( $user_timezone_name );
$utcOffset = $timezone->getOffset( new DateTime( $event_start_time_string_local ) );
$event_start_timestamp_local = maketime( $event_start_time_string_local );
//Now add/subtract $utcOffset to/from $event_start_timestamp_local to get event's start  //time  timestamp in UTC and save this result in DB.

获取用户时区的事件开始时间:

date_default_timezone_set( $user_timezone_name );
$eventTimeLocalTime = date("Y-m-d H:i:s", $event_start_timestamp_in_UTC );

地点:

user_timezone_name is user's timezone setting.
event_start_time_string_local is event's start time string in local/civil time.
event_start_timestamp_in_UTC is event's start time timestamp in UTC.

我的问题:

上面使用的 php API 是否负责所有地区的夏令时? DST 时间本身在不同年份会发生变化,PHP 是如何获取这方面的信息的?我们需要升级 PHP 吗?如果是这样,我们是否需要升级所有或特定的 PHP 库?

参考资料: - does-phps-date-default-timezone-set-adjust-to-daylight-saving - get-timezone-offset-for-a-given-location

【问题讨论】:

【参考方案1】:

你太复杂了。要使用DateTime 在两个时区之间进行转换,请执行以下操作:

date_default_timezone_set('Asia/Kolkata'); // YOUR timezone, of the server

$date = new DateTime($input, new DateTimeZone('Asia/Tokyo')); // USER's timezone
$date->setTimezone(new DateTimeZone('UTC'));
echo $date->format('Y-m-d H:i:s');

这会将用户的本地时区转换为 UTC。反过来,要以用户的本地时间显示时间,请交换两个时区。

是的,PHP 负责 DST。必要的转换规则是 PHP 安装的一部分。您可以通过更新 PHP 或更新 timezonedb 来使它们保持最新状态。

【讨论】:

更新 timezonedb 是一项常规任务,每年需要几次,因为每次任何国家/地区进行更改时都会得到更新 - 这种情况经常发生。 为什么我们需要在开始时调用'date_default_timezone_set('Asia/Kolkata')'? 您可以使用'@12345...' 作为 UNIX 时间戳 @Krishna UNIX 时间戳定义为 自 1970 UTC 以来的秒数。时间戳是无时区的,但如果有的话,它是 UTC。如上所示,您可以格式化为“本地”时间。 @deceze UNIX 时间戳定义为自纪元以来的“UNIX 秒数”数。 UNIX 秒 != s.i.秒,因为 unix 秒包括闰秒并导致所有天的长度都是 86400 秒,而不是平均 86400.002 秒。一个后果是闰秒的时刻不能在 unix 时间戳中表示,因为它们不存在于 unix 时间戳中。【参考方案2】:

是的 php api 考虑了夏令时。大多数情况下,您可以按照 deceze 所示的方式安全地执行时间转换。问题是它并不总是可能/可行或有意义。考虑下面的例子 - 在波兰(2013 年),10 月 27 日凌晨 3 点,您的时钟向后移动一小时以切换到标准时间(偏移 UTC+01:00),在 3 月 31 日凌晨 2 点时钟向前调整 1 小时以切换到夏季时间(偏移 UTC+02:00)。现在让我们看看 Grenwich/UTC 中的“实时”是如何变化的,以及 PHP 从欧洲/华沙到 UTC 的转换是如何工作的:

Europe/Warsaw time   offset  UTC                    php2utc conversion   php offset
2013-10-27 01:00:00    +2    2013-10-26 23:00:00    2013-10-26 23:00:00  +2
2013-10-27 01:30:00    +2    2013-10-26 23:30:00    2013-10-26 23:30:00  +2
2013-10-27 02:00:00    +2    2013-10-27 00:00:00    2013-10-27 01:00:00  +1  *
2013-10-27 02:30:00    +2    2013-10-27 00:30:00    2013-10-27 01:30:00  +1  *
2013-10-27 02:59:00    +2    2013-10-27 00:59:00    2013-10-27 01:59:00  +1  *
     3am -> 2am .....................................summer time changes to standard(winter) time @3am we subtract 1h so 3am becomes 2am
2013-10-27 02:00:00    +1    2013-10-27 01:00:00    2013-10-27 01:00:00  +1
2013-10-27 02:30:00    +1    2013-10-27 01:30:00    2013-10-27 01:30:00  +1
2013-10-27 03:00:00    +1    2013-10-27 02:00:00    2013-10-27 02:00:00  +1  
2013-10-27 03:30:00    +1    2013-10-27 02:30:00    2013-10-27 02:30:00  +1  

正如您在凌晨 2 点至凌晨 3 点之间看到的那样,PHP 无法知道“哪个”小时(从调整之前/之后),因此无法可靠地转换。 希望这对某人的审议有所帮助;-)

【讨论】:

这正是我试图为其编写单元测试的问题。大概 DateTimeZone 对象包括 DST 更改。我怎样才能以一种或另一种方式强制它?是否有一些一致性(例如,如果我在一小时内指定一个时间,重叠,它将始终选择 DST 时间),以及缺少的时间。在上面的示例中,没有当地时间,波兰的 3 月 31 日凌晨 2 点 30 分(即 UTC+2 凌晨 3 点 30 分)。据推测,某处对此有详细的规范。知道在哪里吗? 感谢那张桌子。在这个有问题的一小时内,不能输入 CEST 值,因为它们总是作为 CET 处理。即使明确给出了 CEST 时区。我通过检测 DST 更改(使用format('I'))解决了这个问题,在这种情况下,我提前一小时创建了一个 DateTime,将其转换为 UTC,然后添加缺失的一小时。现在我的 UTC 数据库的每小时值没有漏洞或重复的时间戳。

以上是关于PHP 处理夏令时的时区的主要内容,如果未能解决你的问题,请参考以下文章

如何在android中处理时区和夏令时?

在用户的时区显示当前时间,不要忘记夏令时

如何为 cron 触发器处理石英夏令时的用户时区?

程序里的国际时区和夏令时

如何在 R 中找到给定日期和时区的夏令时调整?

在使用利用夏令时的 Python 时区时,应该传递啥 tzinfo?