MidТеория5 min

Дата и время

DateTime, DateTimeImmutable, DateInterval, форматирование

Дата и время в PHP

DateTime и DateTimeImmutable

Создание объектов даты

<?php
declare(strict_types=1);

// Current date/time
$now = new DateTime();
$nowImmutable = new DateTimeImmutable();

// From string
$date = new DateTime('2024-12-25');
$date = new DateTime('2024-12-25 14:30:00');
$date = new DateTime('next Monday');
$date = new DateTime('+2 weeks');
$date = new DateTime('last day of December 2024');

// From format
$date = DateTime::createFromFormat('d/m/Y', '25/12/2024');
$date = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2024-12-25 14:30:00');

// From timestamp
$date = new DateTime('@1703505600');  // Unix timestamp
$date = (new DateTimeImmutable())->setTimestamp(1703505600);

DateTime vs DateTimeImmutable

<?php
declare(strict_types=1);

// DateTime — MUTABLE (modifies the original object)
$date = new DateTime('2024-01-15');
$modified = $date->modify('+1 month');

echo $date->format('Y-m-d');      // 2024-02-15 — ORIGINAL CHANGED!
echo $modified->format('Y-m-d');   // 2024-02-15 — same object!
var_dump($date === $modified);     // true — same reference!

// DateTimeImmutable — IMMUTABLE (returns new object)
$date = new DateTimeImmutable('2024-01-15');
$modified = $date->modify('+1 month');

echo $date->format('Y-m-d');      // 2024-01-15 — UNCHANGED!
echo $modified->format('Y-m-d');   // 2024-02-15 — new object
var_dump($date === $modified);     // false — different objects

Запомни: ВСЕГДА используй DateTimeImmutable вместо DateTime. Мутабельный DateTime — частый источник багов, когда объект изменяется в неожиданном месте. DateTimeImmutable возвращает НОВЫЙ объект при каждой модификации.

Ловушка экзамена: $date->modify() на DateTime изменяет сам объект И возвращает его (тот же объект). На DateTimeImmutable — возвращает НОВЫЙ объект, оригинал не тронут. Экзамен любит проверять это различие.

Форматирование дат

<?php
declare(strict_types=1);

$date = new DateTimeImmutable('2024-12-25 14:30:45');

// Common format characters
echo $date->format('Y');    // 2024  — 4-digit year
echo $date->format('y');    // 24    — 2-digit year
echo $date->format('m');    // 12    — month with leading zero
echo $date->format('n');    // 12    — month without leading zero
echo $date->format('d');    // 25    — day with leading zero
echo $date->format('j');    // 25    — day without leading zero
echo $date->format('H');    // 14    — 24-hour with leading zero
echo $date->format('G');    // 14    — 24-hour without leading zero
echo $date->format('h');    // 02    — 12-hour with leading zero
echo $date->format('i');    // 30    — minutes
echo $date->format('s');    // 45    — seconds
echo $date->format('A');    // PM    — AM/PM
echo $date->format('l');    // Wednesday — day name
echo $date->format('D');    // Wed   — short day name
echo $date->format('F');    // December — month name
echo $date->format('M');    // Dec   — short month name

// Common formats
echo $date->format('Y-m-d');              // 2024-12-25
echo $date->format('d.m.Y H:i:s');       // 25.12.2024 14:30:45
echo $date->format('D, d M Y H:i:s O');  // Wed, 25 Dec 2024 14:30:45 +0000
echo $date->format('U');                   // Unix timestamp
echo $date->format('c');                   // ISO 8601: 2024-12-25T14:30:45+00:00
echo $date->format('r');                   // RFC 2822

// Predefined format constants
echo $date->format(DateTimeInterface::ATOM);       // ISO 8601
echo $date->format(DateTimeInterface::RSS);        // RSS format
echo $date->format(DateTimeInterface::W3C);        // W3C format
echo $date->format(DateTimeInterface::ISO8601);    // Legacy ISO 8601
Символ Значение Пример
Y Год, 4 цифры 2024
m Месяц с нулём 01-12
n Месяц без нуля 1-12
d День с нулём 01-31
j День без нуля 1-31
H Час 24ч с нулём 00-23
G Час 24ч без нуля 0-23
i Минуты 00-59
s Секунды 00-59
N День недели ISO 1 (Пн) - 7 (Вс)
w День недели 0 (Вс) - 6 (Сб)
W Номер недели ISO 01-53
t Дней в месяце 28-31
L Високосный год 0 или 1
U Unix timestamp числ

Ловушка экзамена: N (ISO день недели) начинается с 1 (понедельник) до 7 (воскресенье). w начинается с 0 (воскресенье) до 6 (суббота). Не путайте! На экзамене могут спросить, какой день недели вернёт формат.

DateInterval

<?php
declare(strict_types=1);

// Creating intervals
$interval = new DateInterval('P1Y2M3D');    // 1 year, 2 months, 3 days
$interval = new DateInterval('P1Y2M3DT4H5M6S'); // + 4h 5m 6s
$interval = new DateInterval('PT1H30M');     // 1 hour 30 minutes

// Format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
// P = Period (required)
// T = Time separator (required before time components)

// Using modify()
$date = new DateTimeImmutable('2024-01-15');
$newDate = $date->modify('+1 year +2 months');
echo $newDate->format('Y-m-d');  // 2025-03-15

// Using add() / sub()
$date = new DateTimeImmutable('2024-01-15');
$interval = new DateInterval('P1M');

$added = $date->add($interval);    // 2024-02-15
$subtracted = $date->sub($interval); // 2023-12-15

// Difference between dates
$start = new DateTimeImmutable('2024-01-01');
$end = new DateTimeImmutable('2024-12-31');
$diff = $start->diff($end);

echo $diff->days;     // 365 — total days
echo $diff->m;        // 11 — months component
echo $diff->d;        // 30 — days component
echo $diff->invert;   // 0 — positive (end > start)

// Formatting intervals
echo $diff->format('%y years, %m months, %d days');
// 0 years, 11 months, 30 days

Подводные камни с интервалами

<?php
declare(strict_types=1);

// Adding 1 month is NOT always 30 days!
$jan31 = new DateTimeImmutable('2024-01-31');
$plusMonth = $jan31->modify('+1 month');
echo $plusMonth->format('Y-m-d');  // 2024-03-02 — NOT February 29!

// PHP adds 31 days to January 31 => March 2
// This is often unexpected!

// To get "last day of next month":
$lastDayFeb = $jan31->modify('last day of next month');
echo $lastDayFeb->format('Y-m-d');  // 2024-02-29 (2024 is leap year)

Ловушка экзамена: Добавление "1 month" к 31 января даёт 2 марта (или 3 марта в невисокосный год), а НЕ 28/29 февраля! PHP прибавляет буквально 1 месяц (31 день), и если результат превышает количество дней в месяце — переносит на следующий.

Часовые пояса (Timezones)

<?php
declare(strict_types=1);

// Setting timezone
$tz = new DateTimeZone('Europe/Moscow');
$date = new DateTimeImmutable('now', $tz);
echo $date->format('Y-m-d H:i:s T');  // ... MSK

// Converting between timezones
$utc = new DateTimeImmutable('2024-01-15 12:00:00', new DateTimeZone('UTC'));
$moscow = $utc->setTimezone(new DateTimeZone('Europe/Moscow'));
echo $moscow->format('H:i');  // 15:00 (UTC+3)

// Default timezone
date_default_timezone_set('Europe/Moscow');
echo date_default_timezone_get();  // Europe/Moscow

// List all timezones
$timezones = DateTimeZone::listIdentifiers();
// ['Africa/Abidjan', 'Africa/Accra', ...]

// Timezone offset
$tz = new DateTimeZone('Europe/Moscow');
$offset = $tz->getOffset(new DateTimeImmutable());
echo $offset / 3600;  // 3 (hours)

Функции даты/времени

<?php
declare(strict_types=1);

// time() — current Unix timestamp
$timestamp = time();

// date() — format timestamp
echo date('Y-m-d H:i:s');              // Current date/time
echo date('Y-m-d H:i:s', $timestamp);  // From timestamp

// mktime() — create timestamp
$ts = mktime(14, 30, 0, 12, 25, 2024);  // H, M, S, month, day, year
echo date('Y-m-d H:i:s', $ts);           // 2024-12-25 14:30:00

// strtotime() — parse date string to timestamp
$ts = strtotime('2024-12-25');
$ts = strtotime('+1 week');
$ts = strtotime('next Monday');
$ts = strtotime('last day of December 2024');

// checkdate() — validate a date
var_dump(checkdate(2, 29, 2024));   // true (2024 is leap year)
var_dump(checkdate(2, 29, 2023));   // false
var_dump(checkdate(13, 1, 2024));   // false (no month 13)

// microtime() — microsecond precision
$start = microtime(true);
// ... some operation ...
$elapsed = microtime(true) - $start;
echo "Elapsed: {$elapsed} seconds";

DatePeriod — итерация по датам

<?php
declare(strict_types=1);

// Iterate over a period
$start = new DateTimeImmutable('2024-01-01');
$end = new DateTimeImmutable('2024-01-10');
$interval = new DateInterval('P1D');  // 1 day

$period = new DatePeriod($start, $interval, $end);

foreach ($period as $date) {
    echo $date->format('Y-m-d') . "\n";
}
// 2024-01-01 through 2024-01-09 (end date excluded!)

// With recurrences count
$period = new DatePeriod($start, $interval, 5);
// 5 occurrences starting from $start

// Include end date (PHP 8.2+)
$period = new DatePeriod($start, $interval, $end, DatePeriod::INCLUDE_END_DATE);

// PHP 8.3+: Using enum-like constants
echo $period->getStartDate()->format('Y-m-d');
echo $period->getEndDate()->format('Y-m-d');
echo $period->getDateInterval()->format('%d days');

Запомни: DatePeriod по умолчанию НЕ включает конечную дату. Чтобы включить, используйте DatePeriod::INCLUDE_END_DATE (PHP 8.2+). Начальная дата включается всегда.


Вопросы с экзамена ZCE

Проверь себя

5 из 14

Что выведет следующий PHP-скрипт? ```php <?= date("M-d-Y", mktime(0, 0, 0, 12, 32, 1965)); ```

Что вернёт `gettype()` для `DateTime` объекта?

Какая функция возвращает текущее время в секундах с начала Unix Epoch (1 января 1970, 00:00:00 GMT)?

Что выведет следующий скрипт? ```php <?= date("M-d-Y", mktime(0, 0, 0, 12, 32, 1995)); ```

`DatePeriod` включает конечную дату по умолчанию?