DateTimeImmutableを使うべき理由と破壊的変更の罠
DateTimeImmutableとは、modifyやaddなどの変更操作が元のオブジェクトを書き換えず、変更後の新しいインスタンスを返す日付クラスです。意図しない書き換え事故を防げます。
DateTimeのmodifyは元オブジェクトを書き換える
DateTime の落とし穴でいちばん怖いのが、modify や add が自分自身を書き換える破壊的メソッドだという点です。基準日を一つ持っておいて、そこから何日後かを計算しようとすると、基準日そのものがずれていく、という事故が起きます。
$base = new DateTime('2026-07-22');
$next = $base; // 参照コピー、同じオブジェクト
$next->modify('+1 day'); // base も動いてしまう
echo $base->format('Y-m-d'); // 2026-07-23(!?)
= はオブジェクトのハンドルをコピーするだけなので、$base と $next が同じ実体を指していて、片方をいじると両方動く、というわけですね。clone を忘れると静かにバグります。
DateTimeImmutableは新しいインスタンスを返す
DateTimeImmutable なら modify や add が元を変えず、変更後の新しいインスタンスを返します。関数型的に「元は不変、結果は戻り値で受ける」という自然な書き方になり、意図しない書き換えが起きません。
$base = new DateTimeImmutable('2026-07-22');
$next = $base->modify('+1 day'); // 新しいインスタンスが返る
echo $base->format('Y-m-d'); // 2026-07-22(元は不変)
echo $next->format('Y-m-d'); // 2026-07-23
戻り値を受け忘れるミスに気づける
Immutable の地味に嬉しい点は、戻り値を受け取り忘れたコードが「何も起きない」形になることです。DateTime だと書き換えは成功してしまうので気づきにくいのですが、Immutable なら結果を捨てているのが明確で、レビューでも気づきやすいですね。
$d = new DateTimeImmutable('2026-07-22');
$d->modify('+1 month'); // 戻り値を捨てている=バグ。dは変わらない
$d = $d->modify('+1 month'); // こう書くのが正しい
まとめ
特別な理由がなければ、日付は DateTimeImmutable を既定で選んでおくのが安全だと思います。共有された基準日を壊す心配がなくなり、コードの見通しもよくなります。両者は DateTimeInterface を共通で実装しているので、引数の型宣言はそちらにしておくと、どちらを渡しても受けられて柔軟ですね。
よくある質問
Q. DateTimeとDateTimeImmutable、既定はどちらを使えばいいですか?
A. 特別な理由がなければImmutableを既定にするのが安全です。共有した基準日を意図せず書き換える事故がなくなります。
Q. 引数の型宣言はどちらにすべきですか?
A. 両者が実装するDateTimeInterfaceにしておくと、どちらのインスタンスも受けられて柔軟です。
Q. 既存のDateTimeコードをImmutableに移すとき注意点は?
A. modifyやaddの戻り値を必ず受け取るように直すことです。DateTime時代の戻り値を捨てる書き方は、Immutableだと何も起きなくなります。