Legacy Date Functions
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, rector, and php-cs-fixer — all specialist/configured tools rather than default linters or compilers. The code_pattern confirms these tools can flag date(), mktime(), strtotime() without explicit timezone, but this requires deliberate tooling setup, not out-of-the-box detection.
Closest to 'simple parameterised fix' (e3). The quick_fix describes replacing date() and mktime() with DateTimeImmutable, which is a pattern-level swap. Rector can automate much of this, but strtotime() replacements and clock-mocking in tests add slightly more work than a pure one-liner — landing squarely at e3 rather than e1.
Closest to 'persistent productivity tax' (b5). The applies_to covers both web and cli contexts broadly, and the issues (untestable time-dependent code, timezone surprises across the codebase) slow down multiple work streams — testing, i18n, and correctness reviews. Not quite load-bearing architecture (b7), but a persistent tax across many files wherever dates are used.
Closest to 'serious trap' (t7). The misconception directly states that strtotime() is assumed to reliably parse any date string, but it is locale-dependent, ambiguous with formats like '01/02/03', and silently returns false (which becomes timestamp 0). This contradicts reasonable developer expectations derived from similarly-named parsing functions in other languages/libraries, and the silence of the failure (not an exception) makes it a serious trap scoring t7.
Also Known As
TL;DR
Explanation
PHP's procedural date functions (date(), mktime(), strtotime()) operate on Unix timestamps and use the default timezone set by date_default_timezone_set(). They are mutable, cannot be mocked in tests, and lead to timezone bugs. DateTimeImmutable (PHP 5.5+) provides an immutable, object-oriented API: timezone-aware, chainable, testable (Carbon extends it), and compatible with DateTimeInterface. date() and strtotime() are acceptable for quick scripts but not for application date logic.
Common Misconception
Why It Matters
Common Mistakes
- date('Y-m-d') in tests — untestable; use DateTimeImmutable and mock the clock.
- strtotime('+1 month') — silently fails on some date strings; use modify('+1 month').
- Not checking strtotime() for false — invalid input silently becomes timestamp 0 (1970).
- DateTime (mutable) instead of DateTimeImmutable — shared DateTime references can be modified unexpectedly.
Code Examples
// Procedural, untestable, timezone-fragile:
$expiry = date('Y-m-d', strtotime('+30 days'));
$timestamp = mktime(0, 0, 0, 12, 25, 2026);
$age = (time() - strtotime($user->birthdate)) / (365.25 * 86400);
// Mutable DateTime — shared reference bug:
$start = new DateTime('2026-01-01');
$end = $start->modify('+1 month'); // $start also modified!
// DateTimeImmutable — immutable, testable, timezone-aware:
$expiry = (new DateTimeImmutable())->modify('+30 days')->format('Y-m-d');
$xmas = new DateTimeImmutable('2026-12-25', new DateTimeZone('UTC'));
$birth = new DateTimeImmutable($user->birthdate);
$age = (new DateTimeImmutable())->diff($birth)->y;
// Immutable — original unchanged:
$start = new DateTimeImmutable('2026-01-01');
$end = $start->modify('+1 month'); // $start unchanged