什么是正确的 ISO8601 格式?

What is the right ISO8601 format?

为什么

echo date('c');

不等于

$datetime = new DateTime();
echo $datetime->format(DateTime::ISO8601); 

结果:

2016-07-07T21:18:22+03:00
2016-07-07T21:18:22+0300

两者都必须以 ISO8601 格式给出当前时间。在 Wikipedia 中正确的格式是 2016-07-07T21:18:22+03:00 但一些银行在 API 中使用 2016-07-07T21:18:22+0300 格式。为什么?

2016-07-07T21:18:22+03:00

是正确的 ISO 8601:2004 表示。

2016-07-07T21:18:22+0300

不正确,当日期和时间为扩展格式时,区域指示符可能不是基本格式。

ISO 8601:2004 4.3 日期和时间:

[...] the expression shall either be completely in basic format, in which case the minimum number of separators necessary for the required expression is used, or completely in extended format, in which case additional separators shall be used [...]

更新 1:

ISO 8601 指定了三种不同的日期表示形式:日历、序号和星期日期。 Theese 可以格式化为基本格式(最少数量的分隔符)或扩展格式(包括附加分隔符的基本格式的扩展)。 ISO 8601 要求生成的表达式要么始终采用基本格式,要么始终采用扩展格式。

本地时间与 UTC 时差的日历日期和时间的组合:

2016-07-07T21:18:22+03:00 (extended format)
20160707T211822+0300 (basic format)

与 UTC 不同的本地时间的有序日期和时间的组合:

2016-189T21:18:22+03:00 (extended format)
2016189T211822+0300 (basic format)

本地时间的周日期和时间的组合与 UTC 的差异:

2016-W27-4T21:18:22+03:00 (extended format)
2016W274T211822+0300 (basic format)

以上所有表示均表示本地时间中的相同日期和时间,但与 UTC(和即时)不同。如果 API 文档表明它接受带有时间的 ISO 8601 日期和区域指示符(也称为完整表示),它应该接受上述所有表示以符合 ISO 8601。

更新 2:

由于 z 转换说明符的限制,我遇到的大多数错误都源于使用 strftime() to output an ISO 8601 date and time of day in local time with difference from UTC in extended format. The standard strftime() 只能输出基本格式的兼容表示:

本地时间与 UTC 时差的日历日期和时间的组合:

Format:          Example:
%Y%m%dT%H%M%S%z  20160707T211822+0300

与 UTC 不同的本地时间的有序日期和时间的组合:

Basic format:    Example:
%Y%jT%H%M%S%z    2016189T211822+0300

本地时间的周日期和时间的组合与 UTC 的差异:

Basic format:    Example:
%GW%V%uT%H%M%S%z 2016W274T211822+0300

GNU strftime 实现支持百分比和 z 转换说明符之间的 : 标志,以指定区域指示符应采用扩展格式:

本地时间与 UTC 时差的日历日期和时间的组合:

Format:                Example:
%Y-%m-%dT%H:%M:%S%:z   2016-07-07T21:18:22+03:00

与 UTC 不同的本地时间的有序日期和时间的组合:

Format:                Example:
%Y-%jT%H:%M:%S%:z      2016-189T21:18:22+03:00

本地时间的周日期和时间的组合与 UTC 的差异:

Format:                Example:
%G-W%V-%uT%H:%M:%S%:z  2016-W27-4T21:18:22+03:00

当给定毫秒数时,PHP 仍然无法正确解释 ISO8601,即使 DateTime::ATOM 也是如此。

$d=DateTime::createFromFormat(DateTime::ATOM,"2018-01-10T01:00:00.000Z"); 
// null

或使用 Carbon:

echo Carbon\Carbon::createFromFormat(Carbon\Carbon::ATOM,"2018-01-10T01:00:00.000Z","UTC");
// InvalidArgumentException with message 'The timezone could not be found in the database'

最好的方法是让 Carbon 或 datetime 自己解决:

$d = new Carbon\Carbon("2018-01-10T01:00:00.000Z");
// -> 2018-01-10 01:00:00
$d = new Carbon\Carbon("2018-01-10T01:00:00Z");
// -> 2018-01-10 01:00:00