2016-09-21 5 views
4

Рассмотрим этот код, в котором мы хотим, чтобы добавить или вычесть один второй:Есть ли strtotime() ошибка?

date_default_timezone_set("Europe/Amsterdam"); 

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

Это правильно, так как это метка времени остается только в DST (летнее время/летнее время).

Но теперь давайте добавим одну секунду метки времени целым числом, а выход 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 

Yikes! Мы пропустили более одного часа вместо одной секунды. И даже не важно, добавляете ли вы одну секунду, один час, один день или один год, вы всегда получите один дополнительный час. Даже если вы добавите несколько лет, вы получите только один лишний час, что было бы странно, потому что мы каждый год входим и выходим из DST, но вы получаете только один дополнительный час, независимо от того, сколько лет вы добавляете.

Но раз мы exit DST в октябре и вычесть одну секунду, все идет хорошо ...

Но с другой стороны. Если бы мы были в марте, и мы только что вошли в DST, и мы вычитаем одну секунду, мы наблюдаем точно то же самое в обратном порядке.


Подождите, что?! Так ... ?

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

Вау ...


Для меня это звучит как ошибка. Или это «по дизайну»? Кто-нибудь знает, что это где-то документально, или нужно сообщить об этом?

+0

К сожалению. Если бы я проверил пул ошибок PHP перед публикацией, я бы нашел, что аналогичная проблема будет опубликована в 2012 году без последующих действий: https://bugs.php.net/bug.php?id=62185 –

+1

Из [doc] (http://php.net/manual/en/function.strtotime.php): 'Использование этой функции для математических операций нецелесообразно. Лучше использовать DateTime :: add() и DateTime :: sub() в PHP 5.3 и более поздних версиях, или DateTime :: modify() в PHP 5.2. ' –

+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 не соответствие и нет перекрытия на CET ->CEST переход).