Avatar ·

Вычисление количества дней между двумя датами в PHP

Всем привет! Я новичок в PHP и пишу небольшой скрипт для учёта задач. Мне нужно посчитать, сколько дней прошло между двумя датами. Например, у меня есть дата начала задачи: "2023-10-01" и дата завершения: "2023-10-25". Я хочу получить разницу в днях. Я пробовал использовать простое вычитание строк, но это не работает, потому что PHP не умеет вычитать строки. Потом я преобразовал даты в timestamp через strtotime(), вычел один из другого и поделил на 86400 (количество секунд в дне). Вот пример моего кода: ```php $start = '2023-10-01'; $end = '2023-10-25'; $diff = (strtotime($end) - strtotime($start)) / 86400; echo $diff; ``` Но я заметил, что иногда результат получается с дробной частью (например, 24.958333), а не целым числом. Ещё я слышал, что из-за перехода на летнее/зимнее время может быть ошибка на 1 день. Как правильно и надёжно посчитать разницу в днях? Есть ли встроенные функции 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
?>

Этот приём удобен, если вам нужно знать направление разницы (например, для проверки просрочки).