Travailler avec les dates et heures en PHP : guide détaillé
Что это такое и зачем? (аналогия из реальной жизни)
Представьте, что вы ведёте дневник. Каждый день вы записываете, что произошло: «Сегодня, 15 марта, я встретился с другом» или «Завтра, в 14:30, у меня тренировка». Без даты и времени ваши записи превратились бы в кашу: вы бы не знали, когда что случилось и что планировать.
В программировании то же самое. Любой сайт или приложение постоянно работает с датами и временем:
- Когда пользователь зарегистрировался? (дата регистрации)
- Когда был опубликован пост в блоге? (дата публикации)
- Через сколько дней заканчивается акция? (разница между датами)
- Сколько времени прошло с момента отправки сообщения? («5 минут назад»)
PHP — это язык, который работает на сервере. Когда вы заходите на сайт, сервер «думает» в своём времени, а пользователь может находиться в другом часовом поясе. Например, сервер стоит в Москве (MSK, UTC+3), а пользователь живёт во Владивостоке (UTC+10). Если не учитывать это, пользователь увидит, что сообщение отправлено «в 2 часа ночи», хотя на самом деле он писал его в 10 утра по своему времени.
Важно: Работа с датой и временем — это не просто «вывести текущую дату». Это целая наука: перевод между часовыми поясами, вычисление разницы, форматирование для разных стран (в США пишут «03/15/2025», а в России — «15.03.2025»).
В этой статье мы разберём, как PHP помогает решать все эти задачи. Вы научитесь не только получать текущее время, но и управлять им, как настоящий повелитель времени.
Теория: как это работает изнутри
Чтобы понять, как PHP работает с датами, нужно заглянуть «под капот». В основе всего лежит одно число — Unix timestamp (метка времени Unix).
Что такое Unix timestamp?
Представьте себе огромный секундомер, который запустили 1 января 1970 года в 00:00:00 по Гринвичу (UTC). С тех пор он тикает каждую секунду. Текущее значение этого секундомера — это и есть Unix timestamp.
Например:
- Timestamp = 0 — это 1 января 1970 года, 00:00:00 UTC.
- Timestamp = 86400 — это 2 января 1970 года, 00:00:00 UTC (потому что в сутках 86400 секунд).
- Timestamp = 1 700 000 000 — это примерно 14 ноября 2023 года.
Почему это удобно? Компьютеры обожают числа. Сравнить два числа (какой timestamp больше) — это одна операция. Прибавить 7 дней — это прибавить 7 × 24 × 60 × 60 = 604800 секунд. Всё просто и быстро.
Часовые пояса и UTC
Земля круглая, и Солнце встаёт в разное время в разных местах. Чтобы не было путаницы, придумали UTC (Coordinated Universal Time — Всемирное координированное время). Это «нулевой» часовой пояс, от которого пляшут все остальные.
Москва: UTC+3 (зимой) или UTC+4 (летом, но в России отменили переход на летнее время). Владивосток: UTC+10. Нью-Йорк: UTC-5 (зимой).
Когда вы работаете с датами в PHP, лучше всего хранить всё в UTC, а показывать пользователю в его часовом поясе. Так вы избежите путаницы.
Объекты и функции: два подхода
В PHP есть два способа работать с датами:
- Функции (старый стиль):
date(),time(),strtotime(). Простые, но имеют ограничения. Появились в PHP 4. - Класс DateTime (новый стиль): Объектно-ориентированный подход. Появился в PHP 5.2. Гораздо мощнее и удобнее. Мы будем использовать его.
Совет профессионала: Если вы пишете новый код, всегда используйте класс DateTime. Функцииdate()иtime()оставьте для очень простых скриптов или для поддержки старого кода.
Синтаксис в PHP: все формы и варианты
Давайте разберём основные инструменты для работы с датами. Мы сфокусируемся на классе DateTime, но покажем и функции для понимания.
1. Получение текущего времени
Функции (старый стиль):
<?php
// time() возвращает текущий Unix timestamp (число секунд с 1970 года)
$timestamp = time();
echo "Текущий timestamp: " . $timestamp . "<br>";
// date() форматирует timestamp в понятную строку
// Первый параметр — формат, второй (необязательный) — timestamp
echo "Сегодня: " . date('Y-m-d H:i:s') . "<br>";
echo "Сегодня (с указанием timestamp): " . date('Y-m-d H:i:s', $timestamp);
?>
Объектно-ориентированный стиль (рекомендуется):
<?php
// Создаём объект DateTime для текущего момента
$now = new DateTime();
echo "Текущее время: " . $now->format('Y-m-d H:i:s') . "<br>";
// Можно передать строку с датой в конструктор
$specificDate = new DateTime('2025-03-15 14:30:00');
echo "Конкретная дата: " . $specificDate->format('d.m.Y H:i');
?>
2. Форматы даты и времени
Формат задаётся с помощью символов. Вот самые популярные:
| Символ | Значение | Пример |
|---|---|---|
Y |
Год (4 цифры) | 2025 |
y |
Год (2 цифры) | 25 |
m |
Месяц (01-12) | 03 |
n |
Месяц (1-12, без нуля) | 3 |
d |
День месяца (01-31) | 15 |
j |
День месяца (1-31, без нуля) | 15 |
H |
Часы (00-23) | 14 |
i |
Минуты (00-59) | 30 |
s |
Секунды (00-59) | 45 |
l |
День недели (полное название) | Saturday |
D |
День недели (3 буквы) | Sat |
F |
Месяц (полное название) | March |
M |
Месяц (3 буквы) | Mar |
3. Изменение даты (прибавить/отнять)
Самый удобный способ — использовать метод modify() или DateInterval.
<?php
$date = new DateTime('2025-03-15');
// Прибавляем 7 дней с помощью modify
$date->modify('+7 days');
echo "Через неделю: " . $date->format('Y-m-d') . "<br>";
// Отнимаем 1 месяц
$date->modify('-1 month');
echo "Месяц назад: " . $date->format('Y-m-d') . "<br>";
// Более сложный пример: прибавляем 2 года, 3 месяца и 10 дней
$date2 = new DateTime('2025-01-01');
$interval = new DateInterval('P2Y3M10D'); // P — period, Y — years, M — months, D — days
$date2->add($interval);
echo "Через 2 года 3 месяца 10 дней: " . $date2->format('Y-m-d');
?>
4. Разница между датами
Метод diff() возвращает объект DateInterval, из которого можно извлечь годы, месяцы, дни и т.д.
<?php
$start = new DateTime('2025-01-01');
$end = new DateTime('2025-12-31');
$diff = $start->diff($end);
echo "Разница: " . $diff->y . " лет, " . $diff->m . " месяцев, " . $diff->d . " дней<br>";
echo "Всего дней: " . $diff->days; // days — общее количество дней
?>
5. Часовые пояса
Класс DateTime умеет работать с часовыми поясами через класс DateTimeZone.
<?php
// Создаём дату в часовом поясе Москвы
$moscow = new DateTime('now', new DateTimeZone('Europe/Moscow'));
echo "Время в Москве: " . $moscow->format('Y-m-d H:i:s') . "<br>";
// Переводим в часовой пояс Нью-Йорка
$newYork = clone $moscow; // Копируем объект
$newYork->setTimezone(new DateTimeZone('America/New_York'));
echo "Время в Нью-Йорке: " . $newYork->format('Y-m-d H:i:s');
?>
Примеры кода — от простого к сложному
Теперь применим теорию на практике. Каждый пример — это реальная задача, которая может встретиться в разработке.
Пример 1: Вывод текущей даты в русском формате
<?php
// Задача: показать сегодняшнюю дату как "15 марта 2025 года"
// Создаём объект DateTime для текущего момента
$now = new DateTime();
// Форматируем: d — день, F — полное название месяца, Y — год
echo "Сегодня: " . $now->format('d F Y года');
// Вывод: Сегодня: 15 March 2025 года
// Проблема: месяц будет на английском (March)
// Чтобы получить русские названия, нужно использовать setlocale и strftime,
// но это тема отдельного разговора. Пока оставим так.
?>
Пример 2: Сколько дней осталось до Нового года?
<?php
// Задача: вывести сообщение "До Нового года осталось X дней"
$today = new DateTime(); // Сегодня
$newYear = new DateTime('2026-01-01'); // 1 января следующего года
// Вычисляем разницу
$diff = $today->diff($newYear);
// diff->days содержит общее количество дней
echo "До Нового года осталось " . $diff->days . " дней.";
// Вывод: До Нового года осталось 291 день. (зависит от текущей даты)
?>
Пример 3: Определение дня недели для любой даты
<?php
// Задача: узнать, каким днём недели было 1 сентября 2025 года
$date = new DateTime('2025-09-01');
// l — полное название дня недели на английском
echo "1 сентября 2025 года — это " . $date->format('l') . "<br>";
// D — сокращённое название (3 буквы)
echo "Сокращённо: " . $date->format('D') . "<br>";
// N — номер дня недели (1 = понедельник, 7 = воскресенье)
echo "Номер дня недели: " . $date->format('N');
// Вывод: 1 сентября 2025 года — это Monday; Сокращённо: Mon; Номер дня недели: 1
?>
Пример 4: Прибавляем рабочие дни (пропускаем выходные)
<?php
// Задача: к текущей дате прибавить 10 рабочих дней (пн-пт)
$date = new DateTime(); // Начинаем с сегодня
$workingDaysToAdd = 10;
$added = 0;
while ($added < $workingDaysToAdd) {
$date->modify('+1 day'); // Прибавляем один день
// Проверяем, не выходной ли это (N = 6 (суббота) или 7 (воскресенье))
if ($date->format('N') < 6) {
$added++; // Если рабочий день — увеличиваем счётчик
}
}
echo "Через 10 рабочих дней будет: " . $date->format('Y-m-d (l)');
// Вывод: Через 10 рабочих дней будет: 2025-04-01 (Tuesday) (пример)
?>
Пример 5: Сравнение дат
<?php
// Задача: проверить, истекла ли дата (например, срок действия купона)
$couponExpiry = new DateTime('2025-06-01');
$today = new DateTime();
// DateTime можно сравнивать операторами <, >, ==
if ($today > $couponExpiry) {
echo "Купон истёк. Срок действия закончился " . $couponExpiry->format('d.m.Y');
} elseif ($today == $couponExpiry) {
echo "Купон истекает сегодня!";
} else {
echo "Купон действителен. Истекает " . $couponExpiry->format('d.m.Y');
}
?>
Пример 6: Вывод времени в формате «только что», «5 минут назад»
<?php
// Задача: показать относительное время (как в соцсетях)
function timeAgo(DateTime $pastTime): string {
$now = new DateTime();
$diff = $now->getTimestamp() - $pastTime->getTimestamp();
if ($diff < 60) {
return "только что";
} elseif ($diff < 3600) {
$minutes = floor($diff / 60);
return $minutes . " минут назад";
} elseif ($diff < 86400) {
$hours = floor($diff / 3600);
return $hours . " часов назад";
} elseif ($diff < 2592000) { // 30 дней
$days = floor($diff / 86400);
return $days . " дней назад";
} else {
return $pastTime->format('d.m.Y');
}
}
// Пример использования
$postTime = new DateTime('-2 hours'); // 2 часа назад
echo "Пост опубликован: " . timeAgo($postTime);
// Вывод: Пост опубликован: 2 часов назад
?>
Пример 7: Генерация календаря на месяц
<?php
// Задача: вывести календарь на март 2025 года
$year = 2025;
$month = 3;
// Первый день месяца
$firstDay = new DateTime("$year-$month-01");
// Количество дней в месяце
$daysInMonth = (int) $firstDay->format('t');
// Номер дня недели для первого дня (1 = пн, 7 = вс)
$firstDayOfWeek = (int) $firstDay->format('N');
echo "<h3>Календарь на " . $firstDay->format('F Y') . "</h3>";
echo "<table border='1'>";
echo "<tr><th>Пн</th><th>Вт</th><th>Ср</th><th>Чт</th><th>Пт</th><th>Сб</th><th>Вс</th></tr>";
echo "<tr>";
// Пустые ячейки до первого дня
for ($i = 1; $i < $firstDayOfWeek; $i++) {
echo "<td></td>";
}
// Заполняем дни
for ($day = 1; $day <= $daysInMonth; $day++) {
echo "<td>$day</td>";
// Если текущий день — воскресенье (7), начинаем новую строку
if (($day + $firstDayOfWeek - 1) % 7 == 0) {
echo "</tr><tr>";
}
}
// Заполняем пустые ячейки в конце
$lastDayOfWeek = ($daysInMonth + $firstDayOfWeek - 1) % 7;
if ($lastDayOfWeek != 0) {
for ($i = $lastDayOfWeek + 1; $i <= 7; $i++) {
echo "<td></td>";
}
}
echo "</tr>";
echo "</table>";
?>
Сравнение с Python: чем похоже и чем отличается
Если вы уже знакомы с Python, вам будет легче. Но есть важные различия.
| Характеристика | PHP | Python |
|---|---|---|
| Основной класс | DateTime |
datetime.datetime |
| Текущее время | new DateTime() |
datetime.now() |
| Форматирование | format('Y-m-d') |
strftime('%Y-%m-%d') или isoformat() |
| Разница между датами | diff() возвращает DateInterval |
Вычитание дат возвращает timedelta |
| Прибавить время | modify('+7 days') или add(DateInterval) |
timedelta(days=7) |
| Часовые пояса | DateTimeZone |
pytz (сторонняя библиотека) или zoneinfo (Python 3.9+) |
| Unix timestamp | time() или getTimestamp() |
time.time() или timestamp() |
Главное отличие: В Python работа с датами более «математическая». timedelta — это просто количество секунд, и вы можете складывать его с датами как числа. В PHP DateInterval — это более сложный объект, который учитывает особенности календаря (например, прибавление месяца к 31 января даст 28 февраля, а не 31 февраля, которого не существует).
Важно: В PHP методmodify('+1 month')работает не так, как вы могли бы подумать. Если сегодня 31 января, то +1 месяц даст 3 марта (потому что PHP «переполняет» дни). Будьте осторожны! Для точных расчётов используйтеDateIntervalс явным указанием дней.
Типичные ошибки и подводные камни
Даже опытные программисты иногда спотыкаются на датах. Вот список самых частых ошибок.
Ошибка 1: Забыть про часовые пояса
<?php
// Плохо: сервер в Москве, пользователь во Владивостоке
$serverTime = new DateTime();
echo "Время на сервере: " . $serverTime->format('H:i:s'); // Покажет московское время
// Хорошо: хранить в UTC, показывать в часовом поясе пользователя
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
$userTimezone = new DateTimeZone('Asia/Vladivostok');
$userTime = clone $utcTime;
$userTime->setTimezone($userTimezone);
echo "Время пользователя: " . $userTime->format('H:i:s');
?>
Ошибка 2: Неправильное форматирование
Путаница между m (месяц с нулём) и n (месяц без нуля), d и j. Или использование Y (4 цифры) вместо y (2 цифры).
Ошибка 3: Сравнение строк вместо объектов
<?php
// Плохо: сравнение строк
$date1 = '2025-03-15';
$date2 = '2025-03-20';
if ($date1 < $date2) { // Работает, но только для формата Y-m-d
echo "Дата1 раньше";
}
// Хорошо: сравнение объектов
$d1 = new DateTime('2025-03-15');
$d2 = new DateTime('2025-03-20');
if ($d1 < $d2) {
echo "Дата1 раньше";
}
?>
Ошибка 4: Игнорирование високосных годов
Февраль 2024 года имеет 29 дней, а февраль 2025 — 28. PHP учитывает это автоматически, но если вы работаете с timestamp вручную, легко ошибиться.
Ошибка 5: Предположение, что в месяце 30 дней
Прибавляя «1 месяц» к 31 января, вы получите 3 марта. Если вам нужно «ровно через 30 дней», используйте modify('+30 days').
Лучшие практики и советы профессионалов
Вот несколько правил, которые помогут вам писать надёжный код.
- Всегда храните даты в UTC. В базе данных, в логах, в кэше — везде используйте UTC. Преобразуйте в локальное время только при показе пользователю.
- Используйте объекты DateTime, а не строки. Строки обманчиво просты, но они не умеют учитывать часовые пояса, високосные годы и прочие сложности.
- Не изобретайте велосипед. Для сложных операций (например, «каждый второй вторник месяца») используйте библиотеки, такие как Carbon (популярное расширение для DateTime в PHP).
- Проверяйте даты на валидность. Пользователь может ввести «30 февраля». Используйте
checkdate()или попробуйте создать DateTime и отловить исключение. - Используйте формат ISO 8601 для обмена данными. Формат
Y-m-dTH:i:sP(например,2025-03-15T14:30:00+03:00) понятен всем системам. - Будьте осторожны с modify('+1 month'). Если вам нужно «следующий месяц, тот же день», используйте более сложную логику или библиотеку Carbon.
- Тестируйте на граничных значениях. Проверьте, как ваш код работает 31 декабря, 29 февраля, в день перехода на летнее время (если вы работаете с часовыми поясами, где он есть).
Совет: Установите библиотеку Carbon через Composer (composer require nesbot/carbon). Она расширяет DateTime и добавляет удобные методы:Carbon::now(),->addWeekday(),->diffForHumans()(как раз для формата «5 минут назад»). Это стандарт де-факто в современном PHP.
Вопросы для самопроверки
Проверьте, как вы усвоили материал. Постарайтесь ответить сначала сами, потом сверьтесь с ответами.
- Что такое Unix timestamp и от какой даты он отсчитывается?
- Какой класс в PHP является основным для работы с датами и временем?
- Как получить текущую дату в формате «2025-03-15»?
- Как прибавить 7 дней к дате с помощью метода modify?
- Как вычислить разницу между двумя датами в днях?
- Как перевести дату из одного часового пояса в другой?
- Почему не стоит сравнивать даты как строки?
- Какая проблема может возникнуть при использовании
modify('+1 month')для 31 января? - Какой формат даты рекомендуется использовать для хранения в базе данных и обмена данными?
- Назовите хотя бы одну популярную библиотеку для работы с датами в PHP.
Ответы:
- Unix timestamp — это количество секунд, прошедших с 1 января 1970 года 00:00:00 UTC.
- Класс
DateTime. (new DateTime())->format('Y-m-d');$date->modify('+7 days');$diff = $date1->diff($date2); echo $diff->days;- Создать объект
DateTimeZoneдля нужного пояса и вызватьsetTimezone(). - Строки сравниваются лексикографически (по алфавиту), что может дать неверный результат для разных форматов.
- 31 января + 1 месяц = 3 марта, потому что PHP «переполняет» дни (в феврале нет 31 дня).
- ISO 8601:
Y-m-dTH:i:sP(например,2025-03-15T14:30:00+03:00). - Carbon (nesbot/carbon).
Итог и что изучить дальше
Мы проделали большой путь. Вы узнали:
- Что такое Unix timestamp и почему это основа всего.
- Как работать с классом DateTime: создавать, форматировать, изменять даты.
- Как вычислять разницу между датами и работать с часовыми поясами.
- Какие ошибки подстерегают новичков и как их избежать.
- Чем PHP отличается от Python в работе с датами.
Теперь вы можете решать большинство повседневных задач: от вывода даты публикации до сложных расчётов сроков.
Что изучить дальше?
- Библиотека Carbon: Это «швейцарский нож» для дат в PHP. Изучите её — она сэкономит вам часы работы.
- Класс DatePeriod: Позволяет создавать диапазоны дат (например, «все понедельники между 1 марта и 1 апреля»).
- Функции для работы с календарём:
cal_days_in_month(),easter_date()(дата Пасхи) — для специфических задач. - Интернационализация (intl):