Манипуляции с датами и временем в PHP: добавление, вычитание, относительные строки
Основные методы изменения дат в PHP
Рассмотрим различные способы модификации объектов даты и времени в PHP. Основным инструментом является класс DateTime и его метод modify(), а также альтернативы для конкретных задач.
DateTime::modify() позволяет изменять дату с помощью строки относительного формата, например '+1 day', '-2 weeks', 'next monday'.
$date = new DateTime('2023-01-31');
$date->modify('+1 month');
echo $date->format('Y-m-d'); // 2023-03-03
Php форматирование даты (php: форматирование даты)
Цель: быстрое изменение даты без создания интервалов. Используется для относительных изменений.
Частая проблема: переполнение дней.
Как видно из примера, при добавлении месяца к 31 января получается 3 марта, а не 28 февраля. Это поведение по умолчанию. Решение: перед модификацией установить день на последний день предыдущего месяца или использовать modify('last day of').
Как добавить или вычесть точное количество дней, месяцев, лет, не опасаясь переполнения?
Используйте методы DateTime::add() и DateTime::sub() с объектом DateInterval.
$date = new DateTime('2023-01-31');
$interval = new DateInterval('P1M');
$date->add($interval);
echo $date->format('Y-m-d'); // 2023-03-03
Php дата день недели (php: получить день недели из даты)
Проблема та же - переполнение. Для точного добавления месяцев с сохранением последнего дня используйте модификацию 'last day of' до операции или после. Например:
$date = new DateTime('2023-01-31');
$date->modify('first day of next month')->modify('-1 day');
echo $date->format('Y-m-d'); // 2023-02-28
Php изменение даты (php: изменение даты (модификация))
Цель: когда требуется точное количество единиц времени (не относительная строка).
Типичная ошибка: создание неверного формата DateInterval, например 'P1M' без указания дней. Интерпретация: P - период, 1M - один месяц. Для дней используется D, например 'P30D'.
Как изменить дату, используя функции strtotime и date?
Процедурный подход:
$timestamp = strtotime('2023-01-01 +2 months -5 days');
echo date('Y-m-d', $timestamp); // 2023-02-24
Php дата числом (php: получить дату в числовом формате)
Цель: быстрые вычисления без создания объектов. Удобно для простых скриптов. Недостаток: мутация глобального состояния, сложно комбинировать с часовыми поясами.
Ошибка: если строка непарсится, strtotime возвращает false, что приводит к выводу 1970-01-01.
Как избежать изменения исходного объекта даты?
Используйте DateTimeImmutable вместо DateTime. Все модифицирующие методы возвращают новый объект, а исходный остаётся без изменений.
$date = new DateTimeImmutable('2023-01-15');
$newDate = $date->modify('+1 month');
echo $date->format('Y-m-d'); // 2023-01-15
echo $newDate->format('Y-m-d'); // 2023-02-15
создание даты php (php: создание объекта даты)
Цель: функциональный стиль, иммутабельность, безопасность при передаче объектов.
Проблема: DateTimeImmutable не поддерживает прямой вызов add()/sub() - возвращает новый объект, нужно присваивать.
Как перейти к следующему понедельнику или последнему дню месяца?
Относительные строки: 'next monday', 'previous sunday', 'last day of this month', 'first day of next month'.
$date = new DateTime('2023-03-15');
$date->modify('last day of this month');
echo $date->format('Y-m-d'); // 2023-03-31
$date2 = new DateTime('2023-03-15');
$date2->modify('next monday');
echo $date2->format('Y-m-d'); // 2023-03-20 (ближайший понедельник)
Цель: удобное перемещение к определённым дням недели или границам месяца.
Важно: 'next monday' от понедельника перейдёт на следующий понедельник (через 7 дней). Если нужно сам понедельник, проверяйте условие.
Расширенные примеры модификации дат
Пример 1. Цепочка изменений с DateTimeImmutable
Покажем, как безопасно выполнить несколько модификаций, не затрагивая исходную дату.
$birthday = new DateTimeImmutable('2000-02-29');
$nextYear = $birthday->modify('+1 year'); // 2001-02-28 (так как 2001 не високосный)
$nextMonth = $nextYear->modify('+1 month'); // 2001-03-28
echo $nextMonth->format('Y-m-d'); // 2001-03-28
2001-03-28
Обратите внимание, что при переходе из високосного года в невисокосный день 29 февраля корректируется на 28 февраля.
Пример 2. Модификация с учётом часового пояса
Изменение даты в конкретном часовом поясе и последующее преобразование в другой пояс.
$date = new DateTime('2023-06-01 12:00:00', new DateTimeZone('America/New_York'));
$date->modify('+12 hours'); // становится 2023-06-02 00:00:00 NY
$date->setTimezone(new DateTimeZone('Asia/Tokyo'));
echo $date->format('Y-m-d H:i:s'); // 2023-06-02 13:00:00 (разница NY-UTC-4, Tokyo UTC+9, +13 часов от 00:00)
2023-06-02 13:00:00
Важно: при смене часового пояса время остается тем же абсолютным моментом, но локальное представление меняется.
Пример 3. Добавление рабочих дней с пропуском выходных
Реализация функции добавления указанного количества рабочих дней (пн-пт).
function addBusinessDays(DateTime $date, int $days): DateTime {
for ($i = 0; $i < $days; $i++) {
$date->modify('+1 day');
// если выпадает на субботу или воскресенье, переходим на понедельник
while (in_array($date->format('N'), [6, 7])) {
$date->modify('+1 day');
}
}
return $date;
}
$start = new DateTime('2023-01-05'); // четверг
addBusinessDays($start, 2);
echo $start->format('Y-m-d'); // 2023-01-09 (пятница 6, суббота 7 -> пропуск, воскресенье 8 -> пропуск, понедельник 9)
2023-01-09
Для учета праздников потребуется дополнительная логика.
Пример 4. Использование относительных строк для границ месяца
Получение первого и последнего дня текущего и следующего месяца.
$date = new DateTime('2023-02-14');
$firstDay = (clone $date)->modify('first day of this month');
$lastDay = (clone $date)->modify('last day of this month');
echo 'Текущий месяц: ' . $firstDay->format('Y-m-d') . ' - ' . $lastDay->format('Y-m-d') . PHP_EOL;
$nextFirst = (clone $date)->modify('first day of next month');
$nextLast = (clone $date)->modify('last day of next month');
echo 'Следующий месяц: ' . $nextFirst->format('Y-m-d') . ' - ' . $nextLast->format('Y-m-d');
Текущий месяц: 2023-02-01 - 2023-02-28 Следующий месяц: 2023-03-01 - 2023-03-31
Обратите внимание на использование clone для избежания изменения исходного объекта (если используется DateTime, а не DateTimeImmutable).