Дата и время в 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+). Начальная дата включается всегда.