DateTimeImmutable::createFromFormat传入时区无效致日期对比异常
PHP 8.2下CI环境中Composer包的日期对比时区问题
在PHP 8.2环境的CI流程中,使用lcobucci/jwt 5.1.x版本时遇到日期对比失败的问题,核心原因是生成的DateTimeImmutable对象时区不一致,导致所有对比逻辑失效。
问题复现代码
$timezone = new \DateTimeZone(\date_default_timezone_get()); $now = new \DateTimeImmutable('now', $timezone); $date = \DateTimeImmutable::createFromFormat('U.u', $now->format('U.u'), $timezone); var_dump($now, $date, $now === $date, $now > $date, $now < $date);
问题现象
输出结果显示两个DateTimeImmutable对象的时区完全不同:
- 直接创建的
$now使用当前默认时区(如Europe/Berlin) - 通过
createFromFormat生成的$date强制使用UTC时区(+00:00)
最终所有对比结果均为false:
...BearerTokenValidatorTest.php:61: class DateTimeImmutable#445 (3) { public $date => string(26) "2023-09-28 12:20:51.615050" public $timezone_type => int(3) public $timezone => string(13) "Europe/Berlin" } ...BearerTokenValidatorTest.php:61: class DateTimeImmutable#444 (3) { public $date => string(26) "2023-09-28 10:20:51.615050" public $timezone_type => int(1) public $timezone => string(6) "+00:00" } ...BearerTokenValidatorTest.php:61: bool(false) ...BearerTokenValidatorTest.php:61: bool(false) ...BearerTokenValidatorTest.php:61: bool(false)
问题根源
PHP的DateTimeImmutable::createFromFormat在处理U格式(UNIX时间戳)时,会强制将生成的时间对象设置为UTC时区,忽略传入的第三个时区参数。这是因为UNIX时间戳本身就是基于UTC的时间表示,PHP在解析该格式时会直接使用UTC,不会应用外部时区。
解决方案
方案1:转换时区后对比
生成DateTimeImmutable对象后,调用setTimezone方法将其转换为目标时区,再进行对比:
$timezone = new \DateTimeZone(\date_default_timezone_get()); $now = new \DateTimeImmutable('now', $timezone); // 先解析再转换时区 $date = \DateTimeImmutable::createFromFormat('U.u', $now->format('U.u'))->setTimezone($timezone); var_dump($now === $date); // 返回true
方案2:基于时间戳数值对比
跳过对象对比,直接获取两个时间的UNIX时间戳数值进行对比,数值对比不受时区影响:
var_dump($now->getTimestamp() === $date->getTimestamp()); // 返回true // 如需精确到微秒,可使用format('U.u')转换为字符串对比 var_dump($now->format('U.u') === $date->format('U.u'));
内容的提问来源于stack exchange,提问作者Wolf-Tech
相关产品推荐
相关产品推荐

