【PHP】Carbon で 「YYYY-MM-DDTHH:MM:SSZ」の形式で強制的にUTC時刻にされてしまう罠

公開:2020/11/05
更新:2020/11/05
2 min読了の目安(約1900字TECH技術記事

概要

PHPのCarbonを使用した際に日付形式「YYYY-MM-DDTHH:MM:SSZ」(ISO 8601)の場合にある罠。

Carbon
ISO 8601

サンプル

使用するCarbonのコンストラクタです。

Carbon::__construct
$time = null, $tz = null
Create a new Carbon instance.

サンプルコード(失敗編)

<?php
echo (new Carbon('2020-11-05T12:34:56Z'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56Z', 'UTC'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56Z', 'Asia/Tokyo'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56Z', 'America/Vancouver'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000

結果は何故か全て同じ…

サンプルコード(成功編)

<?php
echo (new Carbon('2020-11-05T12:34:56Z'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56Z'))->timezone('UTC')->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56Z'))->timezone('Asia/Tokyo')->toString();
// Thu Nov 05 2020 21:34:56 GMT+0900
echo (new Carbon('2020-11-05T12:34:56Z'))->timezone('America/Vancouver')->toString();
// Thu Nov 05 2020 04:34:56 GMT-0800

タイムゾーンを設定することで正確な時間の取得が可能になりました。

原因

末尾の「Z」を削除すると普通にタイムゾーンごとの結果が受け取れます。

<?php
echo (new Carbon('2020-11-05T12:34:56'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56', 'UTC'))->toString();
// Thu Nov 05 2020 12:34:56 GMT+0000
echo (new Carbon('2020-11-05T12:34:56', 'Asia/Tokyo'))->toString();
// Thu Nov 05 2020 21:34:56 GMT+0900
echo (new Carbon('2020-11-05T12:34:56', 'America/Vancouver'))->toString();
// Thu Nov 05 2020 04:34:56 GMT-0800

可能性ですがおそらく、内部処理的にはタイムゾーン設定後に日時の設定を行っているようです。
そのため、末尾にZが着いていると以下の処理を行っているものと考えられます。

1.タイムゾーンを設定
2.日時の末尾にZが付いてる
3.タイムゾーンはUTCの時間

最後にCarbonはPHPの DateTime class を拡張しているので DateTimeクラスを使用していれば、おそらく同様の現象が発生すると思います。
(未確認)