Подтвердить что ты не робот

PHP DateTime:: createFromFormat не обрабатывает дату ISO 8601

Код говорит миллион слов:

php > echo strtotime("2010-12-07T23:00:00.000Z");
1291762800
echo date('c', 1291762800);
2010-12-08T00:00:00+01:00
php > var_dump(DateTime::createFromFormat('c', "2010-12-07T23:00:00.000Z"));
bool(false)
php > var_dump(DateTime::createFromFormat(DateTime::ISO8601, "2010-12-07T23:00:00.000Z"));
bool(false)

Любая идея, что происходит?

Btw, да, новый DateTime ( "2010-12-07T23: 00: 00.000Z" ) работает нормально. Но я предпочитаю знать, какой вклад я получаю.

4b9b3361

Ответ 1

Там отчет об ошибке, который точно описывает вашу проблему :)

https://bugs.php.net/bug.php?id=51950

С 2016-08-07 отчет об ошибке помечен как "не ошибка". Вам нужно использовать strtotime или new DateTime.

Определенные константы применяются к форматированию и синтаксическому анализу одинаково, что обуславливает ваши пути.

Ответ 2

Анализ даты ISO8601, а также временной зоны переключения:

// create ISO8601 dateTime 
$date = DateTime::createFromFormat(DateTime::ISO8601, '2016-07-27T19:30:00Z');

// set to user timezone
$date -> setTimeZone('Asia/Singapore');

echo $date -> format(DateTime::ISO8601);
// prints '2016-07-28T03:30:00+0800'

Ответ 3

Никто не упомянул использовать DATE_ATOM, насколько я знаю, что наиболее корректно реализует ISO 8601 php. Он должен, по крайней мере, работать для последних 3 из них:

<?php

$dates = array(
    "2010-12-07T23:00:00.000Z",
    "2010-12-07T23:00:00",
    "2010-12-07T23:00:00Z",
    "2010-12-07T23:00:00+01:00",
    (new \DateTime("now"))->format(DATE_ATOM)
);

foreach($dates as $d) {

    $res = \DateTime::createFromFormat(DATE_ATOM, $d);

    echo "try $d: \n";
    var_dump($res);
    echo "\n\n";
}

?>

Чтобы разобрать все из них, я написал крошечную функцию:

<?php

function parse_iso_8601($iso_8601_string) {
    $results = array();
    $results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s",$iso_8601_string);
    $results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s.u",$iso_8601_string);
    $results[] = \DateTime::createFromFormat("Y-m-d\TH:i:s.uP",$iso_8601_string);
    $results[] = \DateTime::createFromFormat("Y-m-d\TH:i:sP",$iso_8601_string);
    $results[] = \DateTime::createFromFormat(DATE_ATOM,$iso_8601_string);

    $success = array_values(array_filter($results));
    if(count($success) > 0) {
        return $success[0];
    }
    return false;
}

// Test
$dates = array(
    "2010-12-07T23:00:00.000Z",
    "2010-12-07T23:00:00",
    "2010-12-07T23:00:00Z",
    "2010-12-07T23:00:00+01:00",
    (new \DateTime("now"))->format(DATE_ATOM)
);

foreach($dates as $d) {

    $res = parse_iso_8601($d);

    echo "try $d: \n";
    var_dump($res);
    echo "\n\n";
}

?>

Как упоминал @Glutexo, он работает только в том случае, если для десятичной части есть только от 1 до 6 цифр точности. Не стесняйтесь улучшать его.

Ответ 4

попробуйте следующее:

DateTime::createFromFormat('Y-m-d\TH:i:sP', $date)

Ответ 5

Очень странно и неутешительно, что эта ошибка по-прежнему актуальна. Вот правильный шаблон для парсинга даты с микросекундами в десятичной части секунд:

Y-m-d\TH:i:s.uO

Использование:

$dateStr = '2015-04-29T11:42:56.000+0400'
$ISO = 'Y-m-d\TH:i:s.uO'
$date = DateTime::createFromFormat($ISO, $dateStr)

Ответ 6

Для ответа, указанного здесь fooobar.com/questions/168053/... мы можем использовать этот формат "Y-m-d\TH: i: s.u +" для сохранения микросекунд.

$format = 'Y-m-d\TH:i:s.u+';
$value = '2017-09-21T10:11:19.026Z'; // jsDate.toUTCString();
var_dump(\DateTime::createFromFormat($format, $value));

Ответ 7

Просто:

$dt = new DateTime('2018-04-07T16:32:44Z');
$dt->format('Ymd'); // 20180407

Ответ 8

Используйте DATE_ATOM вместо 'c' при форматировании, как сказал @Steven. Вот как вы работаете с ISO 8601 в PHP.

<?php
$now_date = new DateTime();
$now_iso_8601 = $now_date->format(DATE_ATOM);
echo "Now in ISO 8601 format: {$now_iso_8601}\n";
$date_from_string_and_format = date_create_from_format(DATE_ATOM, $now_iso_8601);
echo "ISO 8601 formatted string, back to DateTime object:\n";
var_dump($date_from_string_and_format);

печать

Now in ISO 8601 format: 2018-09-05T08:17:35-10:00
ISO 8601 formatted string, back to DateTime object:
object(DateTime)#2 (3) {
  ["date"]=>
  string(26) "2018-09-05 08:17:35.000000"
  ["timezone_type"]=>
  int(1)
  ["timezone"]=>
  string(6) "-10:00"
}

Ответ 9

Этот работает для меня:

$date = (new DateTime)->setTimestamp(strtotime('2017-12-31T23:00:00.000Z'));

Ответ 10

Я использую функцию follow, которая допускает несколько форматов ISO8601:

function fromISO8601($time, \DateTimeZone $timezone = null) {
    // valid ISO time 2019-04-01T00:00:00.000+02:00
    $t = \DateTime::createFromFormat('Y-m-d\TH:i:s.uO', $time) or
    // ISO time without millis 2019-04-01T00:00:00+02:00
    $t = \DateTime::createFromFormat('Y-m-d\TH:i:sO', $time) or
    // ISO time without timezone 2019-04-01T00:00:00.000
    $t = \DateTime::createFromFormat('Y-m-d\TH:i:s.u', $time, $timezone) or
    // ISO time without millis and timezone 2019-04-01T00:00:00.000+02:00
    $t = \DateTime::createFromFormat('Y-m-d\TH:i:s', $time, $timezone);

    return $t;
}

здесь все поддерживаемые даты

var_dump(
    fromISO8601('2019-04-01T00:00:00.000+02:00'),
    fromISO8601('2019-04-01T00:00:00+02:00'),
    fromISO8601('2019-04-01T00:00:00.000'),
    fromISO8601('2019-04-01T00:00:00')
);

Этот код полезен для отсутствующих часовых поясов и миллисекунд и работает в более старых версиях php.