DateTimeImmutable

Коли час змінюється, а обʼєкти ні

  • Додано у версії: PHP 5.5
  • Що дає: клас, який поводиться як DateTime, але не змінюється при модифікації, а повертає клонований обʼєкт.

В чому суть

У PHP якщо ми зробимо зміни часу у обʼєкта DateTime, він змінився всюди, де передавався, бо обʼєкти в PHP завжди передаються по посиланню, і така поведінка може призвести до неочікуваної поведінки.

DateTimeImmutable вирішує цю проблему — будь-яка дія над датою повертає новий екземпляр, а не мутує старий.

Коли це справді має сенс

  • у DTO / Value Object — щоб час гарантовано не змінювався після передачі;
  • у планувальниках, розкладах, звітах — коли ти рахуєш багато дат від базової;
  • у бізнес-логіці — коли з однієї дати обчислюється інша (початок/кінець періоду, дедлайн, акція, тощо).

Проблема, яку можна не помітити

У продакшн-коді DateTime часто передають між сервісами, DTO або кешують у контейнерах.

І тут трапляється типова історія:

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

Ми очікували, що початок буде 1 жовтня, але $base мутував під час modify, як наслідок — початок і кінець однакові.

Той самий код, але з DateTimeImmutable працює коректно:

$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

Додатковий приклад

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

Жодних несподіваних мутацій — об’єкт можна безпечно кешувати чи передавати далі.

Висновок

DateTimeImmutable — це безпечний вибір для роботи з логікою часу. Він виключає випадкові зміни дат, коли одна змінна призводить до неочікуваних результатів.

Якщо твоя дата здатна змінитись — вона рано чи пізно зламає щось у звіті, логіці або фінансах. Immutable цього не дозволить.

← Попередня: Генератори · Уся серія · Наступна: Іменовані аргументи →


Вперше опубліковано в Telegram-каналі «Створений щоб помирати» 21.10.2025.