PHP タイムゾーンがAsia/Tokyoの場合、1888-01-01より前のdateの結果がおかしい

確認スクリプト

~~~php:test.php
<?php
$format = 'Y-m-d H:i:s';
foreach(['Asia/Tokyo', 'Etc/GMT-9'] as $timezone) {
    date_default_timezone_set($timezone);
    echo "$timezone\n";
    $datetime = new DateTimeImmutable();
    echo "     / timestamp   / date                / DateTimeImmutable\n";
    for($i = 0; $i < 10; $i++) {
        $timestamp = -2587712405 + $i;
        printf("%4d / %d / %s / %s\n",
            $i,
            $timestamp,
            date($format, $timestamp),
            $datetime->setTimestamp($timestamp)->format($format)
        );
    }
    echo "\n";
}

foreach(['Asia/Tokyo', 'Etc/GMT-9'] as $timezone) {
    date_default_timezone_set($timezone);
    echo "$timezone\n";
    echo strtotime('1887-12-31 23:59:59')."\n";
    echo strtotime('1888-01-01 00:00:00')."\n\n";
}
~~~

~~~plain
$ php -v
PHP 7.4.4 (cli) (built: Mar 20 2020 13:47:45) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.4, Copyright (c), by Zend Technologies

$ php test.php
Asia/Tokyo
     / timestamp   / date                / DateTimeImmutable
   0 / -2587712405 / 1888-01-01 00:18:54 / 1888-01-01 00:18:54
   1 / -2587712404 / 1888-01-01 00:18:55 / 1888-01-01 00:18:55
   2 / -2587712403 / 1888-01-01 00:18:56 / 1888-01-01 00:18:56
   3 / -2587712402 / 1888-01-01 00:18:57 / 1888-01-01 00:18:57
   4 / -2587712401 / 1888-01-01 00:18:58 / 1888-01-01 00:18:58
   5 / -2587712400 / 1888-01-01 00:00:00 / 1888-01-01 00:00:00
   6 / -2587712399 / 1888-01-01 00:00:01 / 1888-01-01 00:00:01
   7 / -2587712398 / 1888-01-01 00:00:02 / 1888-01-01 00:00:02
   8 / -2587712397 / 1888-01-01 00:00:03 / 1888-01-01 00:00:03
   9 / -2587712396 / 1888-01-01 00:00:04 / 1888-01-01 00:00:04

Etc/GMT-9
     / timestamp   / date                / DateTimeImmutable
   0 / -2587712405 / 1887-12-31 23:59:55 / 1887-12-31 23:59:55
   1 / -2587712404 / 1887-12-31 23:59:56 / 1887-12-31 23:59:56
   2 / -2587712403 / 1887-12-31 23:59:57 / 1887-12-31 23:59:57
   3 / -2587712402 / 1887-12-31 23:59:58 / 1887-12-31 23:59:58
   4 / -2587712401 / 1887-12-31 23:59:59 / 1887-12-31 23:59:59
   5 / -2587712400 / 1888-01-01 00:00:00 / 1888-01-01 00:00:00
   6 / -2587712399 / 1888-01-01 00:00:01 / 1888-01-01 00:00:01
   7 / -2587712398 / 1888-01-01 00:00:02 / 1888-01-01 00:00:02
   8 / -2587712397 / 1888-01-01 00:00:03 / 1888-01-01 00:00:03
   9 / -2587712396 / 1888-01-01 00:00:04 / 1888-01-01 00:00:04

Asia/Tokyo
-2587713540
-2587712400

Etc/GMT-9
-2587712401
-2587712400
~~~

タイムゾーンをEtc/GMT-9にすると大丈夫っぽい。
他のタイムゾーンは未確認。


JavaScriptはどうなのか確認してみたら、同様のずれ。
(Chrome 83で確認)

~~~js
{
    for(let i = 0; i < 10; i++) {
        const timestamp = -2587712405 + i;
        console.log(timestamp * 1000, new Date(timestamp * 1000));
    }
}
~~~

~~~plain
-2587712405000 Sun Jan 01 1888 00:18:54 GMT+0918 (日本標準時)
-2587712404000 Sun Jan 01 1888 00:18:55 GMT+0918 (日本標準時)
-2587712403000 Sun Jan 01 1888 00:18:56 GMT+0918 (日本標準時)
-2587712402000 Sun Jan 01 1888 00:18:57 GMT+0918 (日本標準時)
-2587712401000 Sun Jan 01 1888 00:18:58 GMT+0918 (日本標準時)
-2587712400000 Sun Jan 01 1888 00:00:00 GMT+0900 (日本標準時)
-2587712399000 Sun Jan 01 1888 00:00:01 GMT+0900 (日本標準時)
-2587712398000 Sun Jan 01 1888 00:00:02 GMT+0900 (日本標準時)
-2587712397000 Sun Jan 01 1888 00:00:03 GMT+0900 (日本標準時)
-2587712396000 Sun Jan 01 1888 00:00:04 GMT+0900 (日本標準時)
~~~