PHP中DateTime类在夏令时场景下的日期时间比较问题
PHP 夏令时场景下 DateTime 日期时间比较问题解决方案
问题复现
测试代码如下:
$timezone = "America/New_York"; $date1 = new DateTime('2021-11-07 01:55:00.000000', new DateTimeZone($timezone)); $date1->add(new DateInterval("PT5M")); $date2 = new DateTime('2021-11-07 01:55:00.000000', new DateTimeZone($timezone)); /* $date1 = 2021-11-07 01:00:00.000000 $date2 = 2021-11-07 01:55:00.000000 */ var_dump($date1 > $date2); /* result: bool(true) */
原因说明
2021年11月7日是美国纽约时区的夏令时结束日,当日凌晨2点会回拨1小时到1点,因此当天存在两个重叠的1点区间:
- 第一个1点属于夏令时,对应UTC时间为UTC-4
- 第二个1点属于冬令时,对应UTC时间为UTC-5
代码中$date1加5分钟后触发夏令时切换,落入冬令时的1点区间,实际内部存储的UTC时间戳比$date2大。PHP中DateTime对象比较默认使用内部存储的UTC时间戳,而非表面显示的本地时间字符串,因此返回true属于符合底层逻辑的正常结果。
处理方案
- 统一使用UTC时间进行所有日期计算、比较操作,仅在需要给用户展示的时候转换为本地时区。
示例:// 所有计算都用UTC时区,输出时再转本地 $date1 = new DateTime('2021-11-07 01:55:00.000000', new DateTimeZone($timezone)); $date1->setTimezone(new DateTimeZone('UTC')); $date1->add(new DateInterval("PT5M")); $date2 = new DateTime('2021-11-07 01:55:00.000000', new DateTimeZone($timezone)); $date2->setTimezone(new DateTimeZone('UTC')); var_dump($date1 > $date2); // 结果依然为true,符合实际时间先后逻辑 - 如果业务需要按本地显示的字面时间比较,可以提取两个DateTime的本地时间字符串,重新以UTC时区创建对象后再比较:
$date1Str = $date1->format('Y-m-d H:i:s'); $date2Str = $date2->format('Y-m-d H:i:s'); $date1Literal = new DateTime($date1Str, new DateTimeZone('UTC')); $date2Literal = new DateTime($date2Str, new DateTimeZone('UTC')); var_dump($date1Literal > $date2Literal); // 返回false,符合字面时间的比较预期 - 涉及时间增减操作优先使用
DateTimeImmutable类,避免原对象被意外修改,同时可以明确控制每一步的时间转换逻辑。
内容的提问来源于stack exchange,提问作者O. Batista
相关产品推荐
相关产品推荐

