PHP DateTime::createFromFormat 不解析 ISO 8601 日期时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4411340/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 12:58:50  来源:igfitidea点击:

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

phpdatetime

提问by Jake

Code speaks a million words:

代码有一百万个字:

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)

Any idea what's going on?

知道发生了什么吗?

Btw, yes, new DateTime("2010-12-07T23:00:00.000Z") works fine. But I prefer to know what input I am getting.

顺便说一句,是的, new DateTime("2010-12-07T23:00:00.000Z") 工作正常。但我更喜欢知道我得到了什么输入。

回答by Ja?ck

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

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

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

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 strtotimeor new DateTimeinstead.

自 2016 年 8 月 7 日起,该错误报告已被标记为“不是错误”。您需要使用strtotimenew DateTime代替。

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

已定义的常量以相同的方式适用于格式化和解析,这会强制您采用自己的方式。

回答by a20

Parsing ISO8601 date, and also switching timezone:

解析 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'

回答by steven

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:

没有人提到使用 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";
}

?>

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.

正如@Glutexo 提到的,它只有在小数部分也只有 1 到 6 个精度数字时才有效。随意改进它。

回答by Etienne

try this:

尝试这个:

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

回答by Dmitry Davydov

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)

回答by Dzung Nguyen

For the answer listed here https://stackoverflow.com/a/14849503/2425651we can use this format "Y-m-d\TH: i: s.u+" to keep the microseconds.

对于此处列出的答案https://stackoverflow.com/a/14849503/2425651,我们可以使用这种格式“Ymd\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));

回答by stloc

Simply :

简单地 :

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

回答by John Erck

Use DATE_ATOMrather than 'c'when formatting like @Steven said. This is how you work with ISO 8601 in PHP.

使用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);

prints

印刷

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

回答by OzzyCzech

I am using follow function that allow multiple ISO8601 formats:

我正在使用允许多种 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;
}

here are all supported dates

这是所有支持的日期

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')
);

This code is benevolent for missing timezone and milliseconds and works in older php versions.

此代码适用于缺少时区和毫秒,并且适用于较旧的 php 版本。

回答by Sasa Blagojevic

I've experienced this issue with POSTGRES default Time with timezone format and this was the format that fixed it for me:

我在使用时区格式的 POSTGRES 默认时间时遇到了这个问题,这是为我修复它的格式:

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

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