PHP IntlDateFormatter 转换错误 date/time

PHP IntlDateFormatter wrong date/time conversion

我最近偶然发现了 PHP v7.0.4 的一个问题。尝试格式化过去的某些日期时。

我在一个项目中工作,其中有一个叫做 "empty date" 的东西,基本上是一个“1800-01-01”(用来代替 NULL 值)。我正在使用 GMT+1,"Europe/Berlin".

在处理它的过程中,并且涉及到日期本地化,IntlDateFormatter 开始出现一些问题,如果日期 <= 1893-04-01(四月初的傻事? ).

您可以在下面看到一些有趣的示例。有人可以确认他们在他们的系统上遇到了同样的问题吗?它应该可以重现:

$formatter = new \IntlDateFormatter('en_US', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::LONG);
echo $formatter->format(new \DateTime("1893-04-01 00:00:00")) . '<br />';
echo $formatter->format(new \DateTime("1893-04-02 00:00:00")) . '<br />';

哪个应该 return:

"Mar 31, 1893, 11:53:28 PM GMT+0:53:28" and 
"Apr 2, 1893, 12:00:00 AM GMT+1"

或者更明显的行为"change":

echo $formatter->format(new \DateTime("1893-04-01 00:06:31")) . '<br />';
echo $formatter->format(new \DateTime("1893-04-01 00:06:32")) . '<br />';

哪个应该 return:

"Mar 31, 1893, 11:59:59 PM GMT+0:53:28" and
"Apr 1, 1893, 12:06:32 AM GMT+1"

我认为这与时区的历史变化有关(像这样:https://github.com/eggert/tz/blob/2017b/asia#L891,尽管那是关于亚洲的,我使用的是 GMT+1)。

如果我们假设我实际上需要使用这个日期,01.01.1800 - 有人会看到任何 "normal" 绕过这个 "problem" 的方法吗?

你是对的,它与historical change of the timezone有关。 Europe/Berlin 的 GMT 偏移量是 +0:53:28,直到 1893 年 4 月跳到完整的 +1。

因此它的第一个 GMT+1 是:

  • 1893 年 4 月 1 日,12:06:32 上午 GMT+1

之前的第二个是:

  • 1893 年 3 月 31 日,11:59:59 下午 GMT+00:53:28。

基本上这意味着 1893 年 4 月 1 日 00:00:00 到 00:06:31 不存在。


避免围绕这种事情产生很多混淆的正常方法是仅在 UTC 中工作,并处理仅用于显示的时区转换。例如:

date_default_timezone_set('UTC');

$formatter = new \IntlDateFormatter(
    'en_US',
    \IntlDateFormatter::MEDIUM,
    \IntlDateFormatter::LONG,
    'Europe/Berlin'
);
echo $formatter->format(new \DateTime("1800-01-01 00:00:00")) , "\n";
echo $formatter->format(new \DateTime("1893-03-31 23:06:31")) , "\n";
echo $formatter->format(new \DateTime("1893-03-31 23:06:32")) , "\n";
echo $formatter->format(new \DateTime("1893-04-01 00:00:00")) , "\n";

输出:

Jan 1, 1800, 12:53:28 AM GMT+0:53:28
Mar 31, 1893, 11:59:59 PM GMT+0:53:28
Apr 1, 1893, 12:06:32 AM GMT+1
Apr 1, 1893, 1:00:00 AM GMT+1

如果您愿意,也可以强制 IntlDateFormatter 使用 GMT+1 而不是您的时区:

$formatter = new \IntlDateFormatter(
    'en_US',
    \IntlDateFormatter::MEDIUM,
    \IntlDateFormatter::LONG,
    'GMT+01:00'
);