Форматирование даты и времени

При попытке проанализировать дату с помощью DateTime::createFromFormat PHP не будет распознавать часовой пояс.

Пример:

 $t = new \DateTime(); echo $t->format('Ym-dTH:i:s'); 

выйдет

 2012-01-24MSK16:53:52 

Теперь, когда я пытаюсь разобрать эту строку из того же формата

 var_dump(\DateTime::createFromFormat('Ym-dTH:i:s', '2012-01-24MSK16:53:52')); 

я получил

 bool(false) 

Когда я не помещаю Часовой пояс в строку, он работает

 $t = new \DateTime(); echo $t->format('Ym-dH:i:s'); 

дам

 2012-01-2417:17:24 

и разбор

 var_dump(\DateTime::createFromFormat('Ym-dH:i:s', "2012-01-2417:17:24")); 

дам

 object(DateTime)#3 (3) { ["date"]=> string(19) "2012-01-24 17:17:24" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Europe/Moscow" } 

Протестировано

  • PHP 5.3.6-13ubuntu3.3 с Suhosin-Patch (cli) (построено: 13 декабря 2011 18:18:37) и
  • PHP 5.3.9 (cli) (построено: 18 января 2012 20:02:33)

Проблема возникает, если мы заботимся о часовом поясе. Это ошибка? Или что я делаю неправильно? Заранее спасибо!

Это похоже на ошибку (или, по крайней мере, недокументированное ограничение ) с PHP … Если мы попробуем 4 возможных перестановки пробелов:

 var_dump(\DateTime::createFromFormat('Ym-dTH:i:s', '2012-01-24MSK16:53:52')); var_dump(\DateTime::createFromFormat('Ymd TH:i:s', '2012-01-24 MSK 16:53:52')); var_dump(\DateTime::createFromFormat('Ymd TH:i:s', '2012-01-24 MSK16:53:52')); var_dump(\DateTime::createFromFormat('Ym-dT H:i:s', '2012-01-24MSK 16:53:52')); 

Мы получаем (тестировали PHP 5.3, 5.4rc6 и Trunk):

 bool(false) object(DateTime)#2 (3) { ["date"]=> string(19) "2012-01-24 16:53:52" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "MSK" } bool(false) object(DateTime)#3 (3) { ["date"]=> string(19) "2012-01-24 16:53:52" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "MSK" } 

Таким образом, похоже, что идентификатор часового пояса и / или час чувствительны к пробелу … Далее:

 var_dump(\DateTime::createFromFormat('Ymd H:i:s', '2012-01-24 16:53:52')); var_dump(\DateTime::createFromFormat('Ym-dH:i:s', '2012-01-2416:53:52')); 

Получает правильные результаты. А также:

 var_dump(\DateTime::createFromFormat('TY-m-d', 'MSK2012-01-24')); var_dump(\DateTime::createFromFormat('T Ym-d', 'MSK 2012-01-24')); 

Урожайность:

 bool(false) object(DateTime)#4 (3) { ["date"]=> string(19) "2012-01-24 01:49:26" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "MSK" } 

Так что да, похоже, что спецификатор часового пояса чувствителен к завершающему пробелу …

Изменить: он чувствителен к пробелу

Если мы посмотрим на parse_date.c timelib_parse_from_format() в строке 25075 , мы увидим, что все 4 формата часовых поясов анализируются одинаково! Это означает, что между идентификаторами формата для синтаксического анализа нет никакой разницы, и поэтому для синтаксического анализа они взаимозаменяемы.

Это само по себе кажется достаточно ошибкой (или отсутствием функции), чтобы продолжать. Но давайте посмотрим, что происходит в timelib_get_zone() который вызывается при использовании идентификатора часового пояса. Ну, глядя, мы можем видеть, что мы вызываем timelib_lookup_zone() когда это не GMT ​​или смещение по времени.

И там мы обнаружили ошибку. В строке 768 timelib_lookup_zone мы увидим, что она будет потреблять всю входную строку до одной из следующих: \0 (null) ) или пробела:

 while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') { ++*ptr; } 

Что касается фиксации, это немного сложнее. Чтобы исправить эту проблему, потребуется повторная реализация парсеров формата для каждого часового пояса. Для T парсера это легко, так как это всегда 3-строчная строка. Но для остальных это немного интереснее, так как есть переменные буквы, и, как таковая, чувствительность к пробелу может быть проблемой.

Короче говоря, я бы предложил просто добавить заднее белое пространство к вашим идентификаторам часового пояса и сделать с ним …

Вы можете проверить, имеет ли ваш php.ini по умолчанию date.timezone и, если не использовать date_default_timezone_set, поскольку DateTime полагается на него.