【PHP】JavaScriptが出力するISO 8601形式の時刻をDateTimeオブジェクトに変換する

2022-10-31JavaScript,PHP

はじめに

PHPをバックエンド、JavaScriptをフロントエンドで使っていて、フロントから投げた日付データをPHPで取り扱う際に少しハマったため、メモとして残しておきます。

PHPのバージョン

  • PHP 8.0.8

JavaScriptの作成するISO 8601形式の日付

JavaScriptではDateオブジェクトから、次のようにするとISO 8601形式の文字列を生成できます。

const today = new Date('2022-10-18 15:00:00');
console.log(today.toISOString());

この出力結果は以下のようになります。末尾の"Z"はUTCを表します

2022-10-18T06:00:00.000Z

PHPでJavaScriptのISO 8601形式の日付をDateTimeオブジェクトに変換する

PHPでJavaScriptのtoISOString()が生成するISO 8601形式の文字列をDateTimeオブジェクトに変換するためには注意が必要です。PHPのマニュアルでDateTimeオブジェクトを見ると、DateTime::ISO8601という書式が定義されていますが、これはマニュアルに記載がある通り使えません。

注意: このフォーマットは、ISO-8601 と互換性がありません。 しかし、後方互換性を保つために残されています。 ISO-8601 と互換性を保つためには、 DateTimeInterface::ISO8601_EXPANDED, DateTimeInterface::ATOM を使うようにしてください。

注釈にあるようにDateTime::ATOMまたはDateTime::ISO8601_EXPANDEDを使えとなっていますので、試しに使ってみます。

$date = DateTime::createFromFormat(DateTime:ATOM, "2022-10-18T06:00:00.000Z");
var_dump($date);

この出力結果は以下のようになります。

bool(false)

なんと、パースに失敗します。
PHPのDateTime::ATOMは、JavaScriptのtoISOString()の吐き出す形式と互換性がありません。

あらためて、PHPのマニュアルを見てみると、DateTime::ATOMは次のように定義されています。

const string DateTimeInterface::ATOM = "Y-m-d\TH:i:sP";

ここからわかるように、書式にミリ秒が指定されていません。このため、パースに失敗します。

対策

対策として、ミリ秒に対応した書式を指定します。これには、DateTime::RFC3339_EXTENDEDを使用しますが、PHPはタイムゾーンとしてUTCを表す"Z"に対応していないため、"Z"を置換しておく必要があります。

$strDate = preg_replace("/Z$/", "UTC", "2022-10-18T06:00:00.000Z");
$date = DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $strDate);
$date->setTimeZone(new DateTimeZone("Asia/Tokyo"));
var_dump($date);

出力結果は以下のようになります。

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2022-10-18 15:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

(2022/10/31追記)
もう1つの対策として、日時を扱うライブラリとして有名なCarbonを使うという方法もあります。Carbon::parse()メソッドが多様な書式に対応しているので、特別な処理を必要とせずに、Carbonオブジェクトに変換できます(CarbonクラスはDateTimeクラスを継承しています)。

Laravelなどでは標準で採用されていることも多いので、こちらを使うのがよいかもしれません。

use Carbon\Carbon;

$date = Carbon::parse("2022-10-18T06:00:00.000Z");
$date->setTimeZone(new DateTimeZone("Asia/Tokyo"));
var_dump($date->toDateTimeString());

出力結果は以下のようになります。

string(19) "2022-10-18 15:00:00"

まとめ

JavaScriptが生成したISO 8601形式の時刻をPHPで取り扱う際の注意点についてまとめてみました。

サーバーとJSON形式でやり取りする際に、JavaScriptはDateオブジェクトをtoISOString()を使ってISO 8601形式に変換します。PHPで処理する際の注意点に関する情報がなかなか見つからなかったため、この記事が参考になれば幸いです。

JavaScript,PHP

Posted by izadori