PHP DateTime::createFromFormat doesn't parse ISO 8601 date time

There's a bug report that exactly describes your problem :)

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

Since 2016-08-07, the bug report has been marked as "not a bug". You need to use strtotime or new DateTime instead.

The constants that have been defined apply to both formatting and parsing in the same way, which forces your ways.


Parsing ISO8601 date, and also switching timezone:

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

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

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

Nobody mentioned to use DATE_ATOM which is as far as i know phps most correct implementation of ISO 8601. It should at least work for the last 3 of these:

<?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";
}

?>

To be able to parse all of them i wrote a tiny function:

<?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";
}

?>

As @Glutexo mentioned it works only if there are only 1 to 6 precision digits for the decimal part, too. Feel free to improve it.


try this:

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

It is very strange and disappointing that this bug is still actual. Here is a right pattern for parsing date with microseconds in decimal part of seconds:

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

Usage:

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