PHP
JavaScript
ISO8601

PHPのDateTime::ISO8601はISO8601ではない

前提

以下のバージョンにおける情報です。

  • PHP 5 >= 5.2.0
  • PHP 7

ISO8601と互換性がないDateTime::ISO8601

PHPには DateTime::ISO8601 という定義済み定数があります。
この定義済み定数を利用して、以下のようなコードでフォーマットされた日付の文字列を取得できます。

$date = new DateTime();
echo $date->format(DateTime::ISO8601); // 2019-01-09T14:20:03+0000

上のコードを実行すると 2019-01-09T14:20:03+0000 のような文字列を取得できます。

DateTime::ISO8601 を指定して取得された文字列なので、ISO8601 に互換性があることを期待したのですが、なんと互換性はありません。

ISO8601 が規定している形式とは異なってしまっています。

ISO8601で規定されている形式

では、ISO8601とどう異なっているのでしょうか。

ISO8601には「基本形式」と「拡張形式」の2種類の形式があります。
それぞれ以下のようなものです。

形式 表記
基本形式 20190109T142003+0000
拡張形式 2019-01-09T14:20:03+00:00

一見、 DateTime::ISO8601 で得られる文字列は「拡張形式」のように見えますが、よく見るとタイムゾーンの表記が異なります。

拡張形式のタイムゾーンにはコロンが入りますが、 DateTime::ISO8601 で得られる文字列にはコロンが入っていません。

よって、基本形式と拡張形式のどちらにも互換性のない文字列となってしまっています。

ISO8601に互換性のない文字列によって起こる問題

このISO8601に互換性のない文字列をPHP内だけで扱う場合には問題は起きないかもしれません。
しかし、APIなど外部と連携するプログラムを書く場合には注意が必要です。

APIレスポンスとして受信した側が、この文字列を日時としてパースできない可能性があるからです。

JavaScriptの場合

JavaScriptでは一部のブラウザで、パースできない問題があります。
APIをPHPで構築しフロントエンドのJavaScriptでそれを呼び出す、というシチュエーションは多くあるので要注意です。

new Date('2019-01-09T14:20:03+0000')
ブラウザ パース可否
Chrome 71
Firefox 57
Safari 12
Edge 18
IE11
(Node.js v10)

Rubyの場合

Ruby 2.4.0で試したところ、パース可能でした。

DateTime.parse('2019-01-09T14:20:03+0000')

Pythonの場合

Python 3.6.2で試したところ、パース可能でした。

from dateutil.parser import parse
parse('2019-01-09T14:20:03+0000')

ISO8601に互換性のあるフォーマットの日時を取得するには

では、 ISO8601 に互換性のあるフォーマットの日時文字列を取得したい場合はどうしたらいいでしょうか。

その場合には DateTime::ISO8601 ではなく DateTime::ATOM を使用します。

$date = new DateTime();
echo $date->format(DateTime::ATOM); // 2019-01-09T14:20:03+00:00

上のコードでは、 2019-01-09T14:20:03+00:00 のようなISO8601の拡張形式に互換性のある文字列が取得できます。

ドキュメント

http://php.net/manual/ja/class.datetime.php