PHP DateTime :: createFromFormat不解析ISO8601日期时间

时间:2010-12-10 16:59:15

标签: php datetime

代码说了一百万字:

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)

知道发生了什么事吗?

顺便说一句,是的,新的DateTime(“2010-12-07T23:00:00.000Z”)运行正常。但我更愿意知道我得到了什么输入。

11 个答案:

答案 0 :(得分:39)

有一个错误报告可以准确描述您的问题:)

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

自2016-08-07以来,错误报告已被标记为“不是错误”。您需要改为使用strtotimenew DateTime

已定义的常量以同样的方式应用于格式化和解析,这会强制您的方式。

答案 1 :(得分:13)

解析ISO8601日期,并切换时区:

// 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'

答案 2 :(得分:8)

没有人提到过使用DATE_ATOM,据我所知,这是最正确的ISO 8601实施方案。至少应该适用于最后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个精度数字才有效。随意改进它。

答案 3 :(得分:6)

试试这个:

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

答案 4 :(得分:2)

对于此处列出的答案https://stackoverflow.com/a/14849503/2425651 我们可以使用这种格式&#34; Y-m-d \ TH:i:s.u +&#34;保持微秒。

#define IS_IPHONE_XS      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)
#define IS_IPHONE_X_MAX      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 896.0)
#define IS_RETINA        ([[UIScreen mainScreen] scale] >= 2.0) // 3.0 for iPhone X, 2.0 for others

#define IS_IPAD_DEVICE   [(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"]

答案 5 :(得分:2)

简单地说:

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

答案 6 :(得分:2)

在像@Steven所说的那样进行格式化时,请使用DATE_ATOM而不是'c'。这就是您在PHP中使用ISO 8601的方式。

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

答案 7 :(得分:0)

这个适用于我:

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

答案 8 :(得分:0)

我正在使用允许多种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版本中使用。

答案 9 :(得分:0)

我遇到了带有时区格式的POSTGRES默认时间的问题,这是为我解决的格式:

Y-m-d H:i:s.uO

答案 10 :(得分:0)

这对我有用:

$timeStamp = "2020-12-10T14:54:25.618Z";
var_dump(DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $timeStamp));
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2020-12-10 14:54:25.618000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}