Вычисление количества дней между двумя датами в PHP
Проблема с strtotime() и делением на 86400
Ваш подход с strtotime() и делением на 86400 действительно работает, но имеет недостатки:
- Дробные числа из-за того, что
strtotime()учитывает время. Если вы передаёте только дату, PHP подставляет текущее время (например, 00:00:00, но это не всегда так). - Переход на летнее/зимнее время — в дни перевода часов день может длиться 23 или 25 часов, и деление на 86400 даст ошибку.
- Ограничение 32-битных систем —
strtotime()не работает с датами до 1901 или после 2038 года.
Правильное решение: класс DateTime и метод diff()
Начиная с PHP 5.2, есть встроенный класс DateTime, который решает все эти проблемы. Используйте метод diff() для вычисления разницы:
<?php
$start = new DateTime('2023-10-01');
$end = new DateTime('2023-10-25');
$interval = $start->diff($end);
echo $interval->days; // Выведет 24
?>
Свойство days возвращает общее количество дней в интервале. Это целое число, без проблем с временными зонами и переходом на летнее время. Если нужно учитывать время, просто добавьте его в строку:
<?php
$start = new DateTime('2023-10-01 10:00:00');
$end = new DateTime('2023-10-25 15:30:00');
$interval = $start->diff($end);
echo $interval->days; // 24 (полных дней)
?>
Работа с временными зонами
Если ваши даты в разных часовых поясах, укажите их явно:
<?php
$start = new DateTime('2023-10-01', new DateTimeZone('Europe/Moscow'));
$end = new DateTime('2023-10-25', new DateTimeZone('America/New_York'));
$interval = $start->diff($end);
echo $interval->days;
?>
Альтернативный способ: DateTimeImmutable
Если вы не хотите изменять исходные объекты, используйте DateTimeImmutable — он создаёт копию при любых операциях:
<?php
$start = new DateTimeImmutable('2023-10-01');
$end = new DateTimeImmutable('2023-10-25');
$interval = $start->diff($end);
echo $interval->days;
?>
Детальная информация об интервале
Объект DateInterval содержит не только days, но и другие поля:
y— годыm— месяцыd— дни (только в пределах одного месяца)h— часыi— минутыs— секундыinvert— 0, если разница положительная, 1 — если отрицательная
Пример форматированного вывода:
<?php
$start = new DateTime('2023-01-01');
$end = new DateTime('2023-10-25');
$interval = $start->diff($end);
echo $interval->format('%y лет, %m месяцев, %d дней');
// Выведет: 0 лет, 9 месяцев, 24 дня
?>
Что делать, если нужна разница в днях с учётом времени?
Если вы хотите считать полные дни (например, от 2023-10-01 10:00 до 2023-10-02 09:00 — это 0 полных дней, а не 1), используйте:
<?php
$start = new DateTime('2023-10-01 10:00:00');
$end = new DateTime('2023-10-02 09:00:00');
$interval = $start->diff($end);
$fullDays = $interval->days;
if ($interval->h > 0 || $interval->i > 0 || $interval->s > 0) {
// Если есть остаток времени, уменьшаем количество полных дней
$fullDays--;
}
echo $fullDays; // 0
?>
Итог
Используйте DateTime::diff() — это надёжный, современный и читаемый способ. Забудьте про strtotime() и деление на 86400 для вычисления разницы в днях.
Использование функции date_diff() — процедурный стиль
Если вы предпочитаете процедурный стиль (не ООП), можно использовать функцию date_diff(), которая является обёрткой над методом DateTime::diff():
<?php
$start = date_create('2023-10-01');
$end = date_create('2023-10-25');
$interval = date_diff($start, $end);
echo $interval->days; // 24
?>
Этот код делает то же самое, что и объектный вариант. Функция date_create() создаёт объект DateTime, а date_diff() возвращает DateInterval.
Когда использовать этот подход?
- Если ваш код уже написан в процедурном стиле и вы не хотите смешивать парадигмы.
- Если вы работаете с устаревшим кодом, где используется много функций типа
strtotime().
Важное замечание о свойстве days
Обратите внимание: свойство days в DateInterval доступно только после вызова DateTime::diff() или date_diff(). Если вы создадите интервал вручную (например, new DateInterval('P1D')), свойство days будет равно false.
Также помните, что days — это общее количество дней, а не количество полных месяцев или лет. Для форматированного вывода используйте format().
Пример с отрицательной разницей
Если дата окончания раньше даты начала, свойство invert будет равно 1, а days — положительному числу. Чтобы получить отрицательное значение, сделайте так:
<?php
$start = date_create('2023-10-25');
$end = date_create('2023-10-01');
$interval = date_diff($start, $end);
$days = $interval->invert ? -$interval->days : $interval->days;
echo $days; // -24
?>
Этот приём удобен, если вам нужно знать направление разницы (например, для проверки просрочки).
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги