← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

DateTimeImmutable vs DateTime

PHP PHP 5.5+ Beginner
debt(d7/e3/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools field is not specified, meaning no standard linter or SAST tool is called out. Accidentally mutating a shared DateTime or ignoring the return value of modify() on a DateTimeImmutable produces no compiler or runtime error — the bug silently passes through. Only careful code review or a test that checks date values after a shared-object operation will catch it.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix states: replace 'new DateTime()' with 'new DateTimeImmutable()' everywhere, and update any modify() call-sites to capture the return value. This is a mechanical find-and-replace pattern across a codebase, but it is localised per call-site rather than requiring deep architectural changes. It rates slightly above e1 because multiple files are touched and type-hints (DateTime → DateTimeInterface) must also be updated.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The applies_to contexts are web and cli — broad PHP usage. Any project that passes DateTime objects through multiple layers of business logic carries the ongoing risk of accidental mutation. Developers must remember to use DateTimeImmutable, update type-hints to DateTimeInterface, and avoid date_create() helpers. This is a recurring tax on every developer who writes date-handling code, not just a single component.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field highlights a performance red herring (devs avoid DateTimeImmutable thinking it's slower), but the deeper trap from common_mistakes is more dangerous: calling modify() on a DateTimeImmutable and silently discarding the result looks exactly like the DateTime mutation pattern but does nothing. Additionally, type-hinting DateTime instead of DateTimeInterface silently rejects valid DateTimeImmutable values. These contradict how mutation works in DateTime and how type-hints behave with related classes, making this a serious trap for competent developers unfamiliar with the distinction.

About DEBT scoring →

Also Known As

DateTimeImmutable DateTime immutable PHP date immutable DateTimeInterface

TL;DR

DateTimeImmutable returns a new object for every modification — the original is never changed. DateTime modifies in place. Prefer DateTimeImmutable in all new code to avoid subtle bugs where shared date objects are accidentally mutated.

Explanation

DateTime and DateTimeImmutable both implement DateTimeInterface and accept the same constructor arguments and format strings. The difference is mutation: DateTime::modify(), add(), sub(), and setDate() mutate the object and return $this. DateTimeImmutable::modify() returns a new object with the change applied — the original is unchanged. This matters when a date is passed to a function that calls modify() — with DateTime, the caller's date changes too; with DateTimeImmutable, it cannot. The DateTimeInterface type hint accepts both, so you can write functions that work with either while defaulting to the immutable variant.

Common Misconception

DateTimeImmutable is slower because it creates new objects. The object creation overhead is negligible — a few microseconds per operation. The correctness benefit far outweighs the cost.

Why It Matters

Accidentally mutating a shared DateTime object is a classic PHP bug — a function that 'just reads' a date may call modify() internally, silently changing the caller's date. This is especially dangerous with dates passed through multiple layers of business logic. DateTimeImmutable makes it structurally impossible.

Common Mistakes

  • Calling modify() on a DateTimeImmutable and ignoring the return value — the call does nothing visible because the original is unchanged and the new object is discarded.
  • Type-hinting DateTime instead of DateTimeInterface — this rejects DateTimeImmutable instances; use DateTimeInterface to accept both.
  • Using date_create() helper functions which return DateTime not DateTimeImmutable — use 'new DateTimeImmutable()' or DateTimeImmutable::createFromFormat().
  • Storing mutable DateTime objects in value objects or entities — any code that holds the same reference can accidentally change the date.

Code Examples

✗ Vulnerable
<?php
// ❌ DateTime mutation bug — modify() changes the original
function addBusinessDays(DateTime $date, int $days): DateTime
{
    for ($i = 0; $i < $days; $i++) {
        $date->modify('+1 day'); // Mutates the caller's DateTime!
        if ((int) $date->format('N') >= 6) {
            $date->modify('+2 days');
        }
    }
    return $date;
}

$orderDate = new DateTime('2026-01-10');
$dueDate = addBusinessDays($orderDate, 3);
// $orderDate is now '2026-01-15' — silently mutated!
✓ Fixed
<?php
// ✅ DateTimeImmutable — originals are never modified
function addBusinessDays(DateTimeImmutable $date, int $days): DateTimeImmutable
{
    $current = $date;
    for ($i = 0; $i < $days; $i++) {
        $current = $current->modify('+1 day'); // Returns new object
        if ((int) $current->format('N') >= 6) {
            $current = $current->modify('+2 days');
        }
    }
    return $current;
}

$orderDate = new DateTimeImmutable('2026-01-10');
$dueDate = addBusinessDays($orderDate, 3);
// $orderDate is still '2026-01-10' — unchanged

Added 23 Mar 2026
Views 40
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 1 ping S 0 pings M 1 ping T 4 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 7 Scrapy 7 ChatGPT 3 Ahrefs 3 Google 2 Perplexity 2 Claude 2 SEMrush 2 Meta AI 1 Bing 1
crawler 27 crawler_json 3
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Replace 'new DateTime()' with 'new DateTimeImmutable()' everywhere. Where you used '$date->modify('+1 day')' and expected mutation, capture the return value: '$nextDay = $date->modify('+1 day');'.
📦 Applies To
PHP 5.5+ web cli


✓ schema.org compliant