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だと何も起きなくなります。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です