PHP DateTime: понятное руководство с примерами, таймзонами и типичными ошибками

PHP DateTime: понятное руководство с примерами, таймзонами и типичными ошибками

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% багов с датами и трудозатрат на расследования «почему время у пользователя на час отличается».

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий