DateTimeImmutable
When time changes but objects don't
- Added in: PHP 5.5
- What it gives you: a class that behaves like
DateTime, but doesn't change when modified — it returns a cloned object instead.
What it's about
In PHP, if we change the time of a DateTime object, it changes everywhere it has been passed, because objects in PHP are always passed by reference, and this can lead to unexpected behaviour.
DateTimeImmutable solves this problem — any operation on a date returns a new instance instead of mutating the old one.
When it really makes sense
- in DTOs / Value Objects — so the time is guaranteed not to change after being passed along;
- in schedulers, timetables, reports — when you compute many dates from a base one;
- in business logic — when one date is derived from another (start/end of a period, a deadline, a promotion, etc.).
A problem that's easy to miss
In production code, DateTime is often passed between services and DTOs, or cached in containers.
And here comes the typical story:
class Campaign {
public function __construct(
public string $name,
public DateTime $startsAt,
public DateTime $endsAt,
) {}
}
$base = new DateTime('2025-10-01');
$campaign = new Campaign(
name: 'Autumn Sale',
startsAt: $base,
endsAt: $base->modify('+7 days')
);
echo $campaign->startsAt->format('Y-m-d'); // 2025-10-08 ❌
echo $campaign->endsAt->format('Y-m-d'); // 2025-10-08
We expected the start to be October 1, but $base was mutated by modify, and as a result the start and the end are the same.
The same code with DateTimeImmutable works correctly:
$base = new DateTimeImmutable('2025-10-01');
$campaign = new Campaign(
name: 'Autumn Sale',
startsAt: $base,
endsAt: $base->modify('+7 days')
);
echo $campaign->startsAt->format('Y-m-d'); // 2025-10-01 ✅
echo $campaign->endsAt->format('Y-m-d'); // 2025-10-08
One more example
class Period {
public function __construct(
public DateTimeImmutable $from,
public DateTimeImmutable $to
) {}
public function days(): int {
return $this->to->diff($this->from)->days;
}
}
$period = new Period(
from: $from = new DateTimeImmutable('2025-06-01'),
to: $from->modify('+10 days'),
);
echo $period->days(); // 10
No unexpected mutations — the object can be safely cached or passed further.
Conclusion
DateTimeImmutable is the safe choice for time-related logic. It rules out accidental date changes, where one variable leads to unexpected results.
If your date can change, sooner or later it will break something in a report, in the logic or in the finances. Immutable won't let that happen.
← Previous: Generators · Whole series · Next: Named arguments →
First published in Ukrainian in the Telegram channel “Created to die” on October 21, 2025.