Как вычислить разницу между двумя датами в PHP: подробное руководство
Использование класса DateTime и DateInterval для точного вычисления разницы
Ваш подход с strtotime() в целом рабочий, но он не учитывает временные зоны, переход на летнее время и может давать погрешности. В PHP есть гораздо более надёжный и элегантный способ — использовать встроенные классы DateTime и DateInterval. Они были добавлены в PHP 5.2 и с тех пор являются стандартом для работы с датами.
Базовый пример: разница в днях
Вот как правильно вычислить разницу между двумя датами с помощью DateTime::diff():
<?php
$date1 = new DateTime("2023-01-15");
$date2 = new DateTime("2023-02-20");
$interval = $date1->diff($date2);
echo "Разница: " . $interval->days . " дней"; // Выведет: Разница: 36 дней
?>
Метод diff() возвращает объект DateInterval, который содержит всю информацию о разнице: годы, месяцы, дни, часы, минуты, секунды. Свойство days — это общее количество дней (учитывает все переходы).
Получение разницы в годах, месяцах и днях
Если вам нужно не только общее количество дней, но и структурированная разница (например, "2 года 3 месяца 10 дней"), используйте отдельные свойства DateInterval:
<?php
$date1 = new DateTime("2020-05-10");
$date2 = new DateTime("2023-08-25");
$interval = $date1->diff($date2);
echo "Лет: " . $interval->y . "<br>";
echo "Месяцев: " . $interval->m . "<br>";
echo "Дней: " . $interval->d . "<br>";
echo "Всего дней: " . $interval->days . "<br>";
// Выведет:
// Лет: 3
// Месяцев: 3
// Дней: 15
// Всего дней: 1202
?>
Учёт временных зон и летнего времени
Класс DateTime корректно обрабатывает временные зоны. Если ваши даты относятся к разным часовым поясам, просто укажите их при создании объектов:
<?php
$date1 = new DateTime("2023-03-12 10:00:00", new DateTimeZone("America/New_York"));
$date2 = new DateTime("2023-03-13 10:00:00", new DateTimeZone("America/New_York"));
// В Нью-Йорке 12 марта 2023 был переход на летнее время (пропущен час)
$interval = $date1->diff($date2);
echo $interval->days; // Всё равно выведет 1, т.к. diff() работает с календарными днями
?>
Обратите внимание: diff() считает именно календарные дни, а не 24-часовые интервалы. Это важно, если вам нужно точное количество часов — для этого используйте h (часы) из DateInterval.
Форматирование разницы в читаемый текст
Часто требуется вывести разницу вроде "3 года 4 месяца 12 дней". Вот функция для этого:
<?php
function formatDateDiff(DateInterval $interval): string {
$parts = [];
if ($interval->y > 0) $parts[] = $interval->y . ' ' . plural($interval->y, 'год', 'года', 'лет');
if ($interval->m > 0) $parts[] = $interval->m . ' ' . plural($interval->m, 'месяц', 'месяца', 'месяцев');
if ($interval->d > 0) $parts[] = $interval->d . ' ' . plural($interval->d, 'день', 'дня', 'дней');
return implode(' ', $parts);
}
function plural($n, $form1, $form2, $form5) {
$n = abs($n) % 100;
$n1 = $n % 10;
if ($n > 10 && $n < 20) return $form5;
if ($n1 > 1 && $n1 < 5) return $form2;
if ($n1 == 1) return $form1;
return $form5;
}
$date1 = new DateTime("2020-01-05");
$date2 = new DateTime("2023-08-25");
$interval = $date1->diff($date2);
echo formatDateDiff($interval); // Выведет: 3 года 7 месяцев 20 дней
?>
Важные моменты
- Порядок важен:
$date1->diff($date2)считает разницу от $date1 до $date2. Если $date1 больше $date2, результат будет отрицательным (свойствоinvert= 1). - Используйте DateTimeImmutable, если не хотите изменять исходные объекты — он работает так же, но не меняет сам объект при вызове методов.
- Для сравнения дат используйте операторы
>,<,==— они перегружены для DateTime. - Для работы с timestamp можно использовать
DateTime::setTimestamp().
Этот подход полностью решает вашу проблему: он учитывает високосные годы, разную длину месяцев, переход на летнее время и любые временные зоны. Используйте DateTime и DateInterval — это современный, безопасный и читаемый способ работы с датами в PHP.
Альтернативный подход: использование Carbon (популярная библиотека)
Если вы работаете с современными фреймворками (Laravel, Symfony) или просто хотите более удобный синтаксис, рекомендую обратить внимание на библиотеку Carbon. Это расширение для DateTime, которое добавляет множество удобных методов.
Установка через Composer:
composer require nesbot/carbon
Пример использования для вычисления разницы:
<?php
use Carbon\Carbon;
$date1 = Carbon::parse("2023-01-15");
$date2 = Carbon::parse("2023-02-20");
// Разница в днях
echo $date1->diffInDays($date2); // 36
// Разница в месяцах
echo $date1->diffInMonths($date2); // 1
// Человекочитаемый формат
echo $date1->diffForHumans($date2); // "1 month after"
echo $date2->diffForHumans($date1); // "1 month before"
// Полный объект разницы
$diff = $date1->diff($date2);
echo $diff->y . " лет, " . $diff->m . " месяцев, " . $diff->d . " дней";
?>
Carbon также поддерживает работу с временными зонами, локализацию (русский язык), и множество других функций. Это отличный выбор для проектов, где даты используются активно. Однако если вы не хотите добавлять зависимости, встроенный DateTime — тоже прекрасное решение.