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

Jak*_*ake 41 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)
Run Code Online (Sandbox Code Playgroud)

知道发生了什么事吗?

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

Ja͢*_*͢ck 40

有一个错误报告准确描述了你的问题:)

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

自2016-08-07以来,错误报告已被标记为"不是错误".你需要使用strtotimenew DateTime代替.

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

  • 确实令人失望.如果它实际上不符合该标准,为什么要命名一个常量"ISO8601".这就是我不喜欢PHP的原因. (6认同)
  • 血腥的7年后,它仍然存在....甚至被标记为不是bug?`如果要接受不同的ISO-8601日期/时间格式,请执行以下操作。不同的ISO-8601格式?为什么我不喜欢php。 (3认同)
  • 也感到痛苦 (2认同)

a20*_*a20 14

解析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'
Run Code Online (Sandbox Code Playgroud)

  • 提醒一下,不要使用 `format(\DateTime::ISO8601)`,因为它不是有效的 ISO8601 格式。而是使用 `c`、`\DateTime::ATOM` 或 `DATE_ATOM`。原因是因为 `\DateTime:ISO8601` 方式会错误地存储为 `2005-08-15T15:52:01+0000`,而实际上一个有效的 ISO8601 日期字符串应该在时区指示符之间有一个冒号,如 `2005- 08-15T15:52:01+00:00`。PHP 文档中提到了这一点,但不是很清楚。在此处阅读文档:http://ca3.php.net/manual/en/class.datetime.php#datetime.constants.iso860 (2认同)

ste*_*ven 9

没有人提到使用DATE_ATOM,据我所知,phps最正确的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";
}

?>
Run Code Online (Sandbox Code Playgroud)

为了能够解析所有这些,我写了一个小函数:

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

?>
Run Code Online (Sandbox Code Playgroud)

正如@Glutexo所提到的那样,只有小数部分只有1到6个精度数字才有效.随意改进它.


小智 6

试试这个:

DateTime::createFromFormat('Y-m-d\TH:i:sP', $date)
Run Code Online (Sandbox Code Playgroud)


Dmi*_*dov 5

非常奇怪和令人失望的是这个错误仍然存​​在。以下是解析带有秒小数部分微秒的日期的正确模式:

Y-m-d\TH:i:s.uO
Run Code Online (Sandbox Code Playgroud)

用法:

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


stl*_*loc 5

简单地 :

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


Joh*_*rck 5

使用DATE_ATOM而不是'c'像@Steven 所说的那样格式化。这就是您在 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);
Run Code Online (Sandbox Code Playgroud)

印刷

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"
}
Run Code Online (Sandbox Code Playgroud)