使用IntlDateFormatter时为何出现时差问题?
这个时差问题其实主要跟历史时区规则差异和DateTime的初始化方式有关,咱们一步步拆解:
核心原因
1. DateTime初始化未指定时区,导致基准不一致
你代码里创建DateTime时只传了时间字符串,没指定时区:
$dateTime = new \DateTime($inputDateTimeStr);
这时候DateTime会默认用PHP的全局时区(比如date_default_timezone_get()返回的值,可能是UTC或者服务器本地时区)来解析1890-01-01 00:00:00,之后再调用setTimezone转成Europe/Budapest。而IntlDateFormatter是直接用Europe/Budapest时区来处理原始时间的,两者的基准时区不一样,自然会出现偏移。
2. 历史时区规则的差异
1890年的布达佩斯时区规则和现在完全不同——当时还没有夏令时制度,时区偏移也和现代有区别。IntlDateFormatter依赖ICU的时区数据库,而PHP的DateTime依赖Olson时区数据库,如果服务器上这两个数据库版本不一致,对百年前的时间计算就会出现细微偏差,导致时差。
解决办法
方法一:初始化DateTime时直接指定目标时区
创建DateTime就用Europe/Budapest,避免先默认时区再转换的步骤,保证和IntlDateFormatter的处理基准一致:
<?php // php v7.1 $pattern = 'yyyy-MM-dd HH:mm:ss'; $timezone = "Europe/Budapest"; $inputDateTimeStr = '1890-01-01 00:00:00'; $locale = 'hu_HU'; $intlDateFormatter = new \IntlDateFormatter( $locale, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, \IntlTimeZone::createTimeZone($timezone), \IntlDateFormatter::GREGORIAN, $pattern ); // 初始化DateTime时直接指定时区 $dateTime = new \DateTime($inputDateTimeStr, new \DateTimeZone($timezone)); // 此时格式化结果会和IntlDateFormatter的解析结果一致 echo $intlDateFormatter->format($dateTime);
方法二:全程使用IntlDateFormatter处理
如果需要绝对一致的结果,建议用IntlDateFormatter的parse方法解析时间字符串,再转成DateTime,避免混合使用两种工具:
$timestamp = $intlDateFormatter->parse($inputDateTimeStr); $dateTime = (new \DateTime())->setTimestamp($timestamp);
这样得到的DateTime对象完全基于IntlDateFormatter的解析逻辑,不会有时差问题。
方法三:更新时区数据库
如果服务器上的ICU或PHP时区数据库版本过旧,也可能导致历史时间计算偏差。可以尝试更新服务器的ICU库,或者通过pecl install timezonedb更新PHP的时区数据库。
内容的提问来源于stack exchange,提问作者sod

