为何两种时区设置方式会导致PHP DateTime输出不同日期?
首先先把你没写完的第二段代码补全(按常规场景推测完整写法),方便对比:
第一段代码(先创建实例再设置时区)
// 使用setTimezone设置时区 $date1 = DateTime::createFromFormat('Y-m-d H:i:s', '2018-04-04 12:00:00'); $date1->setTimezone(new DateTimeZone('UTC')); $date1->add(new DateInterval('PT7776000S')); echo $date1->format('c') . PHP_EOL; // 输出:2018-07-03T10:00:00+00:00
第二段代码(创建时直接传入时区参数)
// 将时区作为参数传入createFromFormat $date2 = DateTime::createFromFormat('Y-m-d H:i:s', '2018-04-04 12:00:00', new DateTimeZone('UTC')); $date2->add(new DateInterval('PT7776000S')); echo $date2->format('c') . PHP_EOL; // 输出:2018-07-03T12:00:00+00:00
本质原因:解析时间字符串时用的时区不一样!
这两种写法的核心差异,在于PHP解析你的时间字符串2018-04-04 12:00:00时,用的时区完全不同:
第一段代码的逻辑:
当你调用createFromFormat时不传时区参数,PHP会自动用服务器的默认时区(比如你的服务器可能设了Europe/Paris(UTC+2)或者Asia/Shanghai(UTC+8)这类)来解析这个时间字符串。
举个例子,如果默认时区是UTC+2,那么2018-04-04 12:00:00对应的UTC时间其实是2018-04-04 10:00:00——这个底层的时间戳(Unix时间)是固定的。之后你调用setTimezone('UTC')只是改变了这个时间的显示时区,底层的时间戳根本没变。
然后加上90天(PT7776000S就是90天的秒数),最终得到的UTC时间就是2018-04-04 10:00:00 + 90天 = 2018-07-03 10:00:00+00:00,和你看到的输出一致。第二段代码的逻辑:
你直接在createFromFormat的第三个参数传入了UTC时区,PHP会直接把2018-04-04 12:00:00当成UTC时间来解析,对应的时间戳就是这个UTC时间的数值。加上90天后,自然就是2018-07-03 12:00:00+00:00。
一句话总结
- 写法一:先按服务器默认时区转时间戳 → 切换显示时区为UTC → 加时间间隔
- 写法二:直接按UTC时区转时间戳 → 加时间间隔 → 显示为UTC时间
因为初始解析时的时区不同,导致底层时间戳不一样,最终加相同的时间间隔后,结果肯定不同。
怎么验证?
你可以在两段代码里分别输出时间戳来确认:
// 第一段输出时间戳 echo $date1->getTimestamp() . PHP_EOL; // 第二段输出时间戳 echo $date2->getTimestamp() . PHP_EOL;
这两个数值肯定不一样,这就是结果差异的根源。
内容的提问来源于stack exchange,提问作者user1939584

