PHP DateTime: понятное руководство с примерами, таймзонами и типичными ошибками
Класс DateTime в PHP — это гибкий инструмент для работы с датой и временем. В отличие от функций уровня date() и time(), он даёт контроль над часовыми поясами, парсингом и арифметикой дат. Ниже — практические рецепты, которые закроют 90% задач новичка и продолжающего разработчика.
Быстрый старт: текущее время и форматирование
$now = new DateTime(); // использует текущий timezone
echo $now->format('Y-m-d H:i:s'); // 2026-03-03 12:45:10
echo "\n";
echo $now->format(DATE_ATOM); // 2026-03-03T12:45:10+03:00
Совет: для обмена через API и JSON используйте ISO 8601 (константа DATE_ATOM).
Часовые пояса (timezone): почему это важно
// Глобально (php.ini или в коде — на старте приложения)
date_default_timezone_set('Europe/Moscow');
// Локально для даты
$utcNow = new DateTime('now', new DateTimeZone('UTC'));
echo $utcNow->format(DATE_ATOM); // всегда +00:00
// Конвертация между часовыми поясами
$dt = new DateTime('2026-03-03 12:00:00', new DateTimeZone('Europe/Moscow'));
$dt->setTimezone(new DateTimeZone('America/New_York'));
echo $dt->format('Y-m-d H:i:sP'); // учитывает смещение и DST
Лучшая практика: храните время в БД в UTC, конвертируйте в локальный часовой пояс на уровне презентации.
Парсинг дат из строки: надёжно через createFromFormat
$input = '28.02.2026 14:30';
$tz = new DateTimeZone('Europe/Moscow');
$dt = DateTime::createFromFormat('d.m.Y H:i', $input, $tz);
if (!$dt) {
$errors = DateTime::getLastErrors();
var_dump($errors); // покажет где ошибка
} else {
echo $dt->format(DATE_ATOM); // 2026-02-28T14:30:00+03:00
}
// Если строка уже содержит смещение — PHP распознает его сам
$iso = '2026-03-03T12:00:00+03:00';
echo (new DateTime($iso))->setTimezone(new DateTimeZone('UTC'))->format(DATE_ATOM);
Проверяйте ошибки парсинга через DateTime::getLastErrors() — это спасает от неожиданных дата-«перевертышей».
Арифметика дат: add, sub и DateInterval
$dt = new DateTime('2026-03-01 10:00:00', new DateTimeZone('UTC'));
$dt->add(new DateInterval('P1D')); // +1 день
$dt->add(new DateInterval('PT90M')); // +90 минут
$dt->sub(new DateInterval('P2M')); // -2 месяца
echo $dt->format('Y-m-d H:i');
// Полезные шорткаты через modify
echo (new DateTime('2026-03-03'))
->modify('monday this week')
->format('Y-m-d'); // понедельник текущей недели
Совет: для «конца дня» используйте setTime(23, 59, 59), а не добавление 1 дня и вычитание секунды — так код читаемее.
Разница между датами: diff
$a = new DateTime('2026-03-01 10:00:00', new DateTimeZone('UTC'));
$b = new DateTime('2026-03-03 15:30:00', new DateTimeZone('UTC'));
$diff = $a->diff($b);
// %a — всего дней, %h — часы остатка, %i — минуты
echo $diff->format('%a дней %h часов %i минут'); // 2 дней 5 часов 30 минут
DateTimeImmutable: меньше побочных эффектов
$base = new DateTimeImmutable('2026-03-03 10:00:00');
$plus1h = $base->modify('+1 hour'); // вернет новый объект
// $base прежний, это безопаснее в сложных вычислениях
Рекомендация: по умолчанию используйте DateTimeImmutable. Для пошаговых «мутаций» допустим DateTime, но контролируйте копии объектов.
Хранение дат в БД: UTC, ISO и типы
- Храните в UTC — меньше проблем с переводом часов и сравнением.
- Форматы: ISO 8601 строка (2026-03-03T09:00:00Z) или TIMESTAMP/DATETIME в UTC.
- Отдавайте во внешние API в ISO 8601 (DATE_ATOM).
// Преобразование для записи в БД (UTC)
$local = new DateTime('2026-03-03 12:00:00', new DateTimeZone('Europe/Moscow'));
$utc = (clone $local)->setTimezone(new DateTimeZone('UTC'));
$forDb = $utc->format('Y-m-d H:i:s'); // 2026-03-03 09:00:00
Типичные подводные камни и как их избежать
- Не полагайтесь на системный timezone сервера — задайте его явно через date_default_timezone_set().
- Не измеряйте длительность через разницу DateTime — используйте монотонные часы.
- Осторожно с «+1 month» в конце месяца — февраль и DST дадут сюрпризы. Валидируйте результат.
- Всегда указывайте timezone при парсинге пользовательского ввода.
Измерение времени выполнения: hrtime и microtime
// hrtime — монотонные часы, точнее и стабильнее
$start = hrtime(true);
// ... код ...
$ns = hrtime(true) - $start; // наносекунды
$ms = $ns / 1e6;
echo "Выполнено за {$ms} ms";
// Альтернатива: microtime(true)
$start = microtime(true);
// ... код ...
$ms = (microtime(true) - $start) * 1000;
echo "Выполнено за {$ms} ms";
Микросекунды, JSON и удобные форматы
$dt = new DateTime();
echo $dt->format('Y-m-d H:i:s.uP'); // 2026-03-03 12:45:10.123456+03:00
// Для JSON отдавайте ISO 8601
$data = [
'created_at' => $dt->format(DATE_ATOM),
];
echo json_encode($data, JSON_UNESCAPED_SLASHES);
Работа с локальным временем пользователя
function toUserTz(DateTimeInterface $utc, string $userTz): string {
$dt = (new DateTimeImmutable($utc->format(DATE_ATOM)))
->setTimezone(new DateTimeZone($userTz));
return $dt->format('d.m.Y H:i');
}
$utc = new DateTimeImmutable('2026-03-03T09:00:00+00:00');
echo toUserTz($utc, 'Europe/Moscow'); // 03.03.2026 12:00
DST и повторяющиеся события
Если вы планируете ежедневные нотификации в локальном времени (например, 08:00 по Москве), храните «локальное правило» и каждый раз вычисляйте следующую дату через modify('tomorrow 08:00') в нужном timezone. Если нужна строгая периодичность «ровно каждые 24 часа», используйте UTC и интервалы в секундах — так вы избежите скачков при переходе на летнее/зимнее время.
Где быстро прокачать навык
Хотите закрепить работу с датой/временем в реальных проектах и параллельно освоить БД, формы и безопасность? Посмотрите программу и первые уроки курса: Пройти практический курс «PHP и MySQL с Нуля до Гуру 3.0» — пошаговые задания, обратная связь и современные подходы под PHP 8+.
Короткий чек-лист лучших практик
- Глобально задайте timezone и используйте UTC для хранения.
- Для парсинга — только DateTime::createFromFormat или ISO 8601.
- Для вычислений используйте DateInterval и DateTimeImmutable, где возможно.
- Для метрик — hrtime() или microtime(true), не DateTime::diff().
- В API и JSON — ISO 8601 (DATE_ATOM).
Освоив эти приёмы, вы избежите 80% багов с датами и трудозатрат на расследования «почему время у пользователя на час отличается».
-
Создано 04.03.2026 17:01:44
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.