DateTime :: Diff должен рассчитывать правильный интервал и учитывать Летнее время (DST) и високосные годы. Хотя, видимо, это не так. Код ужаса:
$d1 = new DateTime("2011-10-30 01:05:00", new DateTimeZone("Europe/Stockholm")); $d2 = new DateTime("2011-10-30 03:05:00", new DateTimeZone("Europe/Stockholm")); echo $d1->getOffset() / (60 * 60);
Печатает '2'! Имейте в виду, что время UTC = 1 час – 2 часа = 23:05:00 днем раньше.
echo $d2->getOffset() / (60 * 60);
Печатает '1'. Прошло DST. UTC time = 3h – 1h = 02:05:00.
$di = $d1->diff($d2); echo "Hours of DateInterval: " . $di->h;
Печатает '2'! Неправильно?
$hoursofdiff = ($d2->getTimeStamp() - $d1->getTimeStamp()) / 60 / 60; echo "Calculated difference in hours: $hoursofdiff";
Печатает '3'! Верный?
Когда часы были установлены в 03:00:00 в указанную дату, все шведы вернули часы на один час до 02:00:00. Это означает, что общая сумма, пройденная между 01:05 и 03:05, составляет три часа, так же, как и ручной расчет, эхо-сигнал при использовании UNIX TimeStamp. И, как мы рассчитываем на наших пальцах, если мы используем аналоговые часы. Тем более, когда мы вычисляем разницу между двумя временными метками UTC, я получил собственную логику смещений PHP (!).
Это PHP или мой мозг перестает работать правильно? Выговор любого из вас всех богов, которые существуют на этом сайте, сделает меня такой счастливой!
Я использую PHP 5.4 (VC9) на Apache-сервере. К сожалению, я использую Windows 7 x64 в качестве ОС. Я тестировал свою настройку против всех утверждений об ошибках в классах PHP / Date / Time (есть пара, связанная с Windows) и может подтвердить, что в моей системе нет ни одного из них. За исключением вышеуказанного кода, я не обнаружил никаких других ошибок. Я в значительной степени подтвердил весь код и вывел книгу «Руководство PHP Architect для программирования даты и времени». Поэтому я должен заключить, что моя мозговая ведьма дефолтна, но я подумал, что сначала буду снимать здесь.
Вы правы, PHP в настоящее время не обрабатывает переходы DST …
Сообщения об ошибках # 51051 (по-прежнему открыты) и # 55253 (исправлены в PHP 5.3.9) описывают проблемы, которые у вас возникают.
Daniel Convissor написал RFC, пытающийся решить проблему некоторое время назад, но журналы изменений не предполагают, что это было рассмотрено. Я надеялся, что это будет зафиксировано в 5.4, но я не вижу никаких доказательств.
Когда / если оно реализовано, похоже, что вам нужно добавить «DST» или «ST» к строке времени.
Лучшая практика – делать все расчеты по дате в UTC , что позволяет избежать этой проблемы.
Этот пост передовой практики DST также очень информативен.
Получите удовольствие от этого списка связанных ошибок в классе PHP DateTime, большинство из которых были открыты для 2+ лет:
Хорошо, у меня есть класс обертки. Он вычисляет пройденное в реальном времени. Сначала он сравнивает смещения от UTC и добавляет или вычитает эту временную разницу в объект datetime, переданный в качестве аргумента. После этого не нужно ничего делать, кроме как вызвать parent :: diff. Ну ладно, мне нужно было ввести один лайнер, чтобы взломать то, что может быть еще одной ошибкой в PHP (см. Исходный код ниже). Метод DateTimeDiff: diff вычисляет пройденное время REAL. Чтобы понять, что это значит, я советую вам протестировать этот класс с использованием разных дат и времени и помочь вашей рабочей нагрузке. Я также включил в нижнюю часть этого комментария довольно простую HTML-страницу, которую я написал. Эта ссылка может быть хорошей отправной точкой для получения некоторых идей для комбинаций даты и времени:
https://wiki.php.net/rfc/datetime_and_daylight_saving_time
Кроме того, обратите внимание, что когда у нас есть обратный переход в летнее время, некоторые комбинации даты и времени могут принадлежать обоим часовым поясам. Эта двусмысленность может сделать результаты этого класса отличными от ожидаемых. Таким образом, если вы серьезно подумываете об использовании этого класса, его дальнейшее развитие и попросите разъяснить пользователю в этих случаях.
Вот вы, класс:
<?php class DateTimeDiff extends DateTime { public function diff($datetime, $absolute = false) { // Future releases could fix this bug and if so, this method would become counterproductive. if (version_compare(PHP_VERSION, '5.4.0') > 0) trigger_error("You are using a PHP version that might have adressed the problems of DateTime::diff", E_USER_WARNING); // Have the clock changed? $offset_start = $this->getOffset(); $offset_end = $datetime->getOffset(); if ($offset_start != $offset_end) { // Remember the difference. $clock_moved = $offset_end - $offset_start; // We wouldn't wanna fuck things up for our caller; thus work on a clone. $copy = clone $datetime; if ($clock_moved > 0) { $timestamp_beforesub = $copy->getTimestamp(); // Subtract timedifference from end-datetime should make parent::diff produce accurate results. $copy->sub( DateInterval::createFromDateString("$clock_moved seconds") ); // No change occured; sometimes sub() fails. This is a workable hack. if ($timestamp_beforesub == $copy->getTimestamp()) $copy->setTimezone(new DateTimeZone("UTC")); } else // ..else < 0 and its a negative. { $clock_moved *= -1; // Adding that timedifference to end-datetime should make parent::diff produce accurate results. $copy->add( DateInterval::createFromDateString("$clock_moved seconds") ); } return parent::diff($copy, $absolute); } // <-- END "if ($offset_start != $offset_end)" return parent::diff($datetime, $absolute); } } ?>
И страница для тестирования (отобразит результаты с использованием как DateTime :: diff, так и DateTimeDiff :: diff):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>DateTimeDiff-class</title> <?php if (! (empty($_GET['identifier']) && empty($_GET['start']) && empty($_GET['end']))) { $dt1_new = new DateTimeDiff("{$_GET['start']} {$_GET['identifier']}"); $dt1_old = new DateTime("{$_GET['start']} {$_GET['identifier']}"); $dt2 = new DateTime("{$_GET['end']} {$_GET['identifier']}"); $di_new = $dt1_new->diff($dt2); $di_old = $dt1_old->diff($dt2); // Extract UNIX timestamp and transitional data $timezone_start = $dt1_new->getTimezone(); $timezone_end = $dt2->getTimezone(); $timestamp_start = $dt1_new->getTimeStamp(); $timestamp_end = $dt2->getTimeStamp(); $transitions_start = $timezone_start->getTransitions($timestamp_start, $timestamp_start); $transitions_end = $timezone_end->getTransitions($timestamp_end, $timestamp_end); echo <<<BUILDCONTAINER <script type='text/javascript'> function Container() { } var c_new = new Container; var c_old = new Container; var t_start = new Container; var t_end = new Container; </script> BUILDCONTAINER; echo <<<SETTRANSITIONS <script type='text/javascript'> t_start.ts = '{$transitions_start[0]['ts']}'; t_start.time = '{$transitions_start[0]['time']}'; t_start.offset = '{$transitions_start[0]['offset']}'; t_end.ts = '{$transitions_end[0]['ts']}'; t_end.time = '{$transitions_end[0]['time']}'; t_end.offset = '{$transitions_end[0]['offset']}'; </script> SETTRANSITIONS; foreach ($di_new as $property => $value) echo "<script type='text/javascript'>c_new.$property = $value</script>"; foreach ($di_old as $property => $value) echo "<script type='text/javascript'>c_old.$property = $value</script>"; } ?> <script type='text/javascript'> window.onload = function() { if (c_new != null) // <-- em assume everything else is valid too. { // Update page with the results for (var prop in c_new) addtext(prop + ": " + c_new[prop] + " (" + c_old[prop] + ")"); addtext("Read like so.."); addtext("PROPERTY of DateInterval: VALUE using DateTimeDiff::diff ( VALUE using DateTime::diff )"); // Restore values sent/recieved <?php foreach ($_GET as $key => $value) echo "document.getElementById('$key').value = '$value';"; ?> // Display transitiondata (For DateTime start) var p_start = document.getElementById('p_start'); var appendstring = "TS: " + t_start.ts + ", Time: " + t_start.time + ", Offset: " + t_start.offset; p_start.appendChild(document.createTextNode(appendstring)); // Display transitiondata (For DateTime end) var p_end = document.getElementById('p_end'); appendstring = "TS: " + t_end.ts + ", Time: " + t_end.time + ", Offset: " + t_end.offset; p_end.appendChild(document.createTextNode(appendstring)); } } function addtext() { var p = document.createElement("p"); p.appendChild(document.createTextNode(arguments[0])); document.forms[0].appendChild(p); } </script> </head> <body> <form action="test2.php" method="get"> <p>Identifier: <input type="text" name="identifier" id="identifier" value="Europe/Stockholm" /></p> <p id="p_start">Start: <input type="text" name="start" id="start" /></p> <p id="p_end">End: <input type="text" name="end" id="end" /></p> <p><input type="submit" /></p> </form> </body> </html>