📝

Carbon使用時の注意点

に公開

こんにちは!Booost株式会社Webエンジニアのramcursweです。
昔、日付や時間を扱うライブラリであるCarbonを使って実装した際、ちょっとした罠にハマってしまったので、その備忘および共有としてまとめたいと思います。

注意点

実際のコードで見た方が分かりやすいですので、さっそくですが例を挙げます。
まず、以下のように記述します。

use Carbon\Carbon;

$now = Carbon::now(); 
// 以下のようなCarbonオブジェクトが返却される
// Carbon\Carbon @1759042601 {#10972
//	date: 2025-09-28 15:56:41.519474 Asia/Tokyo (+09:00),
// }

これはごく普通の動きなので問題ないですね!
次に上記で定義した$nowを使い、時間を加算します。

$afterFiveHours = $now->addHours(5);
// $nowの結果に5時間加算される
// Carbon\Carbon @1759060601 {#10972
//  date: 2025-09-28 20:56:41.519474 Asia/Tokyo (+09:00),
// }

これも問題ないと思います。
ただ5時間加算されただけですね。
それでは、今度は$nowに対して別の時間を加算してみます。

$afterTenHours = $now->addHours(10);
// $nowの結果に10時間加算される...?
// Carbon\Carbon @1759096601 {#10972
//  date: 2025-09-29 06:56:41.519474 Asia/Tokyo (+09:00),
// }

おかしいですね…
上記の例において、元々$now2025-09-28 15:56:41.519474という日時であるので、「10時間加算すると15時の10時間後である1時になるはずでは?」と当時の私は考えていました(そんな訳ないのですが…)。

このズレは、2番目の処理で$nowに5時間加算された時点で元のオブジェクトが更新され、15→20時に上書きされることで発生します(破壊的操作)。
これが実装者の意図通りであるならば何の問題もありません。
しかし、$nowを元々の定義から変更したくないというケースでは、どのようにすべきなのでしょうか?

解決方法はとてもシンプルです!
Carbonではなく、CarbonImmutableを使いましょう!
改めて$nowを定義し、時間を加算していきます。

use Carbon\CarbonImmutable;

$now = CarbonImmutable::now();
// Carbon\CarbonImmutable @1759045481 {#10983
//  date: 2025-09-28 16:44:41.728274 Asia/Tokyo (+09:00),
// }

$afterFiveHours = $now->addHours(5);
// Carbon\CarbonImmutable @1759063481 {#10970
//  date: 2025-09-28 21:44:41.728274 Asia/Tokyo (+09:00),
// }

$afterTenHours = $now->addHours(10);
// 元々の$nowの結果に10時間加算される
// Carbon\CarbonImmutable @1759081481 {#11020
//  date: 2025-09-29 02:44:41.728274 Asia/Tokyo (+09:00),
// }

Carbon使用時と違い、元々の$nowの結果を変えることなく、初めの日時に対して10時間後を求めることができました。
元のオブジェクトの値を保持して使い回したい場合は、CarbonImmutableにすべきです。

学び

当時は「日時関係の処理=Carbonを使えばいい」と安直に考えていたのですが、破壊的操作は予期せぬエラーの原因となり得るので、できるだけ使用は控えるべきです(自戒の意味を込めて…)。

Booost

Discussion