🤕

Carbon::createFromFormat にやられたはなし

2025/04/01に公開

起こったこと

$a = Carbon::createFromFormat('Ym', $value);
$b = $a->startOfMonth();

のような処理を行っているところで、月末だけ意図通りの値が取得できない問題に遭遇した。

# 3/31 に実行すると `2024-10-01` として扱われる
$a = Carbon::createFromFormat('Ym', '202409');
$b = $a->startOfMonth();

原因は、 Carbon (や PHP の DateTime 型)は省略した部分を現在の日時で初期化するから。
上に挙げたケースだと、 $a は 2024-09-31 として扱われる。これは 2024-10-01 と言うことになる。

全てのフィールドは現在の日付/時刻で初期化されます。

https://www.php.net/manual/ja/datetimeimmutable.createfromformat.php

解決方法

解決方法もドキュメントに書いてある。

ほとんどの場合、これらの値を "ゼロ" (Unix タイムのエポック 1970-01-01 00:00:00 UTC) でリセットしたいはずです。 これは、format の最初の文字に ! を入れるか、 最後の文字に | を入れることで実現できます。 詳細な情報は、以下で示すそれぞれの文字に関するドキュメントを参照ください。

と言うことで、以下のようにすれば解決できる。

# いつ実行しても `2024-09-01` として扱われる
$a = Carbon::createFromFormat('!Ym', '202409');
$b = $a->startOfMonth();

ググったときに解決方法として、 createFromFormat('Ymd', "{$value}01") みたいにすればよい、という記事があったんだけど、無駄に日付を入れるのが気持ち悪かった。

結論

マニュアルは読み得

余談

バグってたコードの修正を日を入れる方法でやっちゃったのでなおさないと……

Discussion