2016-09-21 43 views
4

,我們要添加或。減去1秒考慮以下代碼:strtotime()有bug嗎?

date_default_timezone_set("Europe/Amsterdam"); 

$time = 1477789199; 
echo $time . ' - ' . date('r', $time) . "\n"; 
// 1477789199 - Sun, 30 Oct 2016 02:59:59 +0200 

這是正確的,因爲這時間戳仍然只是內DST(夏令時間/夏令時間)。

但現在,讓我們1秒添加時間戳整數,退出 DST:

$new = $time + 1; 
echo $new . ' - ' . date('r', $new); 
// 1477789200 - Sun, 30 Oct 2016 02:00:00 +0100 

萬歲! PHP看到一秒鐘後就沒有DST了,並顯示一個合適的時間字符串。

但是,如果我們沒有添加第二個到時間戳的整數,但我們用strtotime()加那一秒:

$new = strtotime('+1 second', $time); 
echo $new . ' - ' . date('r', $new); 
// 1477792800 - Sun, 30 Oct 2016 03:00:00 +0100 

哎呀!我們剛剛超過一小時而不是一秒鐘。而且,如果您添加一秒鐘,一小時,一天或一年,則無需擔心,您將永遠得到一小時的額外時間。即使你添加多個年,你只會得到一個額外的一小時,因爲我們進入這是怪異和出口每年夏令時,但你只能得到不管你有多少年添加

一個額外的時刻,但是,一旦我們退出在十月的DST和減去一秒,一切順利...

但是再次。如果我們在3月份,剛剛進入夏令時,我們減去一秒鐘,我們觀察到完全相同的情況。


等等,什麼?!那麼......?

echo strtotime('+ 1 second - 1 second', 1477789199); // echoes 1477792799 

哇......


對我來說,這聽起來像一個錯誤。或者這是'按設計'?有人甚至知道這是否記錄在某處,或者是否需要報告?

+0

糟糕。如果我在發佈之前檢查了PHP bug池,我會在2012年發現一個類似的問題,並且沒有進行跟進:https://bugs.php.net/bug.php?id=62185 –

+1

從[doc]( http://php.net/manual/en/function.strtotime.php):'不建議使用這個函數進行數學運算。最好在PHP 5.3及更高版本中使用DateTime :: add()和DateTime :: sub(),或者在PHP 5.2中使用DateTime :: modify()。 ' –

+0

報告於https://bugs.php.net/bug.php?id=73138 –

回答

1

的行爲是「有據可查」 ......在一個測試:

https://bugs.php.net/bug.php?id=30532(這也表現爲預期的預期結果)和相關的測試文件(該文件稱,目前的行爲是正確的) https://github.com/php/php-src/blob/master/ext/date/tests/bug30532.phpt

<?php date_default_timezone_set("America/New_York"); 

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +1 hour'))."\n"; 
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +2 hours'))."\n"; 
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 EDT +3 hours'))."\n"; 
/* 2004-10-31 01:00:00 EDT 
    2004-10-31 01:00:00 EST 
    2004-10-31 02:00:00 EST */ 

echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +1 hour'))."\n"; 
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +2 hours'))."\n"; 
echo date('Y-m-d H:i:s T', strtotime('2004-10-31 +3 hours'))."\n"; 
/* 2004-10-31 01:00:00 EDT 
    2004-10-31 02:00:00 EST 
    2004-10-31 03:00:00 EST */ 

注意,在前者的情況下的時區(這裏:EDT)被直接傳遞到字符串,它不是後一種情況。將strtotime的時間戳(即2004-10-31 - 或在您的特定情況下:傳遞的時間戳)轉換爲具有單個參數的表示,忽略DST(即單獨的小時,分​​鍾,秒,日,月,年份等),操作將應用於它,然後轉換回時間戳。

特別是:

echo date('r', strtotime('+ 0 second', 1477789199)); 
#> Sun, 30 Oct 2016 02:59:59 +0100 

strtotime()拋出變換後的時區遠,即只需要

Sun, 30 Oct 2016 02:59:59 

,然後主要適用時區適用於您的時區的位置(即Europe/Amsterdam),與CET結束了(主!) - CEST也是可以的,但只是第二選擇。

現在,回頭看看上面的測試,只需明確指定發起時區即可。

因此,如果你希望它的行爲方式,你需要它:

echo date('r', strtotime('CEST', 1477789199)); 
#> Sun, 30 Oct 2016 02:59:59 +0200 
echo date('r', strtotime('CEST + 1 second', 1477789199)); 
#> Sun, 30 Oct 2016 02:00:00 +0100 

事實上,前面加上'CEST '將是罰款,所有的時間(因爲它總是會回退到CET如果CEST不匹配並且CETCEST轉換)沒有重疊。