«Сколько дней прошло?» — вопрос, который ломает новичков
Каждый PHP-разработчик рано или поздно сталкивается с задачей: нужно узнать, сколько дней прошло между двумя датами. Казалось бы, что может быть проще? Берём две строки, вычитаем одну из другой — и готово. Но именно здесь, на ровном месте, совершаются самые коварные ошибки.
Мы заглянули на Stack Overflow и нашли реальный вопрос «Finding the number of days between two dates», который собрал тысячи просмотров. Оказалось, что даже опытные программисты попадаются в ловушки, связанные с часовыми поясами, unix-метками и неожиданным поведением strtotime().
В этой статье мы разберём пять самых распространённых ошибок при расчёте количества дней между датами на PHP и покажем, как их избежать. Готовьте редактор — будет много кода и практических советов.
Ошибка №1: Вычитание unix-меток «в лоб»
Самая популярная ошибка — попытка получить разницу между датами простым вычитанием timestamp'ов:
<?php
$date1 = strtotime('2024-01-01');
$date2 = strtotime('2024-01-10');
$days = ($date2 - $date1) / (60 * 60 * 24);
echo $days; // 9
?>
Внешне всё работает. Но попробуйте перевести часы на летнее время. При переходе на «лето» сутки сокращаются на час, а при обратном переходе — удлиняются. Результат: 9.04166 или 8.95833 дней вместо ровно 9.
Как исправить
Используйте объекты DateTime и метод diff(). Они учитывают все скачки времени:
<?php
$date1 = new DateTime('2024-01-01');
$date2 = new DateTime('2024-01-10');
$interval = $date1->diff($date2);
echo $interval->days; // 9 — всегда корректно
?>
Работа с разницей дат PHP через DateTime::diff — это стандарт индустрии. Запомните этот паттерн.
Ошибка №2: Игнорирование часового пояса
Представьте: ваш сервер стоит в Европе/Москве, а пользователь из Нью-Йорка вводит дату «2024-03-10». Вы создаёте DateTime без указания часового пояса. PHP использует системную таймзону сервера. Результат: разница в днях может «уплыть» на единицу.
Особенно это критично для границ месяцев и переходов на летнее время. Ошибка проявляется редко, но когда она вылезает в продакшене — отловить её очень сложно.
Решение: явно задавайте часовой пояс
<?php
$tz = new DateTimeZone('UTC');
$date1 = new DateTime('2024-03-10', $tz);
$date2 = new DateTime('2024-03-20', $tz);
$interval = $date1->diff($date2);
echo $interval->days; // 10 — честно
?>
Всегда используйте UTC для внутренних расчётов. Если нужно показать дату пользователю — конвертируйте в его локальный пояс только на этапе вывода.
Ошибка №3: Путаница между days и format('%a')
У объекта DateInterval есть свойство days и метод format('%a'). Кажется, что это одно и то же. Но есть нюанс:
$interval->days— общее количество дней (учитывает все месяцы и годы). Значение всегда корректно.$interval->format('%a')— то же самое, но в виде строки.$interval->d— только дни без учёта месяцев. Если разница 35 дней, тоdвернёт 4 (остаток после вычитания месяца).
Новички часто путают d и days. В результате получают 4 дня вместо 35. Отсюда — баги в отчётах, биллинге и планировщиках задач.
Всегда проверяйте, какое свойство используете
<?php
$date1 = new DateTime('2024-01-01');
$date2 = new DateTime('2024-02-05');
$interval = $date1->diff($date2);
echo $interval->days; // 35
echo $interval->d; // 4 — НЕ ТО ЖЕ САМОЕ!
?>
Если вам нужно именно количество дней между двумя датами — берите days. Свойство d используйте только когда нужна «человеческая» разбивка: «2 месяца и 4 дня».
Ошибка №4: Сравнение дат без нормализации
Иногда разработчики пытаются сравнить даты «на лету» внутри одного выражения:
<?php
$days = (new DateTime('now'))->diff(new DateTime('2024-12-31'))->days;
?>
Выглядит лаконично, но в чём подвох? new DateTime('now') создаёт объект с текущим временем вплоть до секунды. А вторая дата — только «2024-12-31 00:00:00». Если сейчас 15:30, то diff посчитает не полные сутки, а дробную разницу. days округлит, но не всегда так, как вы ожидаете.
Нормализуйте даты к началу дня
<?php
$today = new DateTime('today'); // 00:00:00
$target = new DateTime('2024-12-31');
$interval = $today->diff($target);
echo $interval->days; // точное количество полных дней
?>
Используйте модификаторы 'today', 'yesterday', 'midnight' — они сбрасывают время. Это особенно важно при расчёте разницы дат PHP в системах бронирования, где учитываются только даты без времени.
Ошибка №5: Работа с датами в будущем и прошлом
Когда разница считается от будущей даты к прошлой, свойство invert становится равным 1. Многие забывают его проверить и получают отрицательное количество дней, которое на самом деле положительное.
<?php
$date1 = new DateTime('2024-12-31');
$date2 = new DateTime('2024-01-01');
$interval = $date1->diff($date2);
echo $interval->days; // 365
echo $interval->invert; // 1 (означает, что date1 меньше date2)
?>
Если вы просто выводите days, пользователь увидит положительное число, хотя по логике «до Нового года осталось -365 дней» — это чушь.
Как правильно обрабатывать направление разницы
<?php
function daysBetween(DateTime $start, DateTime $end): int {
$interval = $start->diff($end);
$days = $interval->