← DateTimeImmutable

Ashterix

docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards

uk/articles/from-tg/series/php-can-do/datetime-immutable.md новий файл +97 −0

1 + # DateTimeImmutable
2 +
3 + *Коли час змінюється, а обʼєкти ні*
4 +
5 + - **Додано у версії:** PHP 5.5
6 + - **Що дає:** клас, який поводиться як `DateTime`, але не змінюється при модифікації, а повертає клонований обʼєкт.
7 +
8 + ## В чому суть
9 +
10 + У PHP якщо ми зробимо зміни часу у обʼєкта `DateTime`, він змінився всюди, де передавався, бо обʼєкти в PHP завжди передаються по посиланню, і така поведінка може призвести до неочікуваної поведінки.
11 +
12 + `DateTimeImmutable` вирішує цю проблему — будь-яка дія над датою повертає новий екземпляр, а не мутує старий.
13 +
14 + ## Коли це справді має сенс
15 +
16 + - у DTO / Value Object — щоб час гарантовано не змінювався після передачі;
17 + - у планувальниках, розкладах, звітах — коли ти рахуєш багато дат від базової;
18 + - у бізнес-логіці — коли з однієї дати обчислюється інша (початок/кінець періоду, дедлайн, акція, тощо).
19 +
20 + ## Проблема, яку можна не помітити
21 +
22 + У продакшн-коді `DateTime` часто передають між сервісами, DTO або кешують у контейнерах.
23 +
24 + І тут трапляється типова історія:
25 +
26 + ```php
27 + class Campaign {
28 + public function __construct(
29 + public string $name,
30 + public DateTime $startsAt,
31 + public DateTime $endsAt,
32 + ) {}
33 + }
34 +
35 + $base = new DateTime('2025-10-01');
36 + $campaign = new Campaign(
37 + name: 'Autumn Sale',
38 + startsAt: $base,
39 + endsAt: $base->modify('+7 days')
40 + );
41 +
42 + echo $campaign->startsAt->format('Y-m-d'); // 2025-10-08 ❌
43 + echo $campaign->endsAt->format('Y-m-d'); // 2025-10-08
44 + ```
45 +
46 + Ми очікували, що початок буде 1 жовтня, але `$base` мутував під час `modify`, як наслідок — початок і кінець однакові.
47 +
48 + Той самий код, але з `DateTimeImmutable` працює коректно:
49 +
50 + ```php
51 + $base = new DateTimeImmutable('2025-10-01');
52 + $campaign = new Campaign(
53 + name: 'Autumn Sale',
54 + startsAt: $base,
55 + endsAt: $base->modify('+7 days')
56 + );
57 +
58 + echo $campaign->startsAt->format('Y-m-d'); // 2025-10-01 ✅
59 + echo $campaign->endsAt->format('Y-m-d'); // 2025-10-08
60 + ```
61 +
62 + ## Додатковий приклад
63 +
64 + ```php
65 + class Period {
66 + public function __construct(
67 + public DateTimeImmutable $from,
68 + public DateTimeImmutable $to
69 + ) {}
70 +
71 + public function days(): int {
72 + return $this->to->diff($this->from)->days;
73 + }
74 + }
75 +
76 + $period = new Period(
77 + from: $from = new DateTimeImmutable('2025-06-01'),
78 + to: $from->modify('+10 days'),
79 + );
80 +
81 + echo $period->days(); // 10
82 + ```
83 +
84 + Жодних несподіваних мутацій — об’єкт можна безпечно кешувати чи передавати далі.
85 +
86 + ## Висновок
87 +
88 + > DateTimeImmutable — це безпечний вибір для роботи з логікою часу.
89 + > Він виключає випадкові зміни дат, коли одна змінна призводить до неочікуваних результатів.
90 +
91 + Якщо твоя дата здатна змінитись — вона рано чи пізно зламає щось у звіті, логіці або фінансах. `Immutable` цього не дозволить.
92 +
93 + ← [Попередня: Генератори](/articles/from-tg/series/php-can-do/generators) · [Уся серія](/articles/from-tg/series/php-can-do) · [Наступна: Іменовані аргументи →](/articles/from-tg/series/php-can-do/named-arguments)
94 +
95 + ---
96 +
97 + *Вперше опубліковано в Telegram-каналі [«Створений щоб помирати»](https://t.me/php_while_true/81) 21.10.2025.*