Манипуляции с датами и временем в PHP: добавление, вычитание, относительные строки

Раздел: Программирование на 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).

PHP: изменение даты (модификация) - comments

En
Php изменение даты (php)