## Арифметика даты/времени

В следующих примерах показываются некоторые подводные камни вычислений даты/времени, относительно переходов на летнее и зимнее время (DST), и месяцев, имеющих разное количество дней.

Пример #1 DateTimeImmutable::add/sub добавляет интервалы, охватывающие прошедшее время

Добавление PT24H через переход DST приведёт к добавлению 23/25 часов (для большинства часовых поясов).

    <?php
    $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->add(new DateInterval("PT3H"));
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

Результат выполнения приведённого примера:

    Начало: 2015-11-01 00:00:00 -04:00
    Конец:  2015-11-01 02:00:00 -05:00

Пример #2 DateTimeImmutable::modify и strtotime увеличит или уменьшит значения индивидуальных компонентов

Добавление +24 часов через переход DST добавит точно 24 часов (вместо учёта перехода на зимнее или летнее время).

    <?php
    $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+24 hours");
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

Результат выполнения приведённого примера:

    Начало:  2015-11-01 00:00:00 -04:00
    Конец:   2015-11-02 00:00:00 -05:00

Пример #3 Добавление или вычитание времени может уменьшить или увеличить дату

Например, 31 января + 1 месяц вернёт 2 марта (високосный год) или 3 марта (обычный год).

    <?php
    echo "Обычный год:\n"; // В феврале 28 дней
    $dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+1 month");
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

    echo "Високосный год:\n"; // В феврале 29 дней
    $dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("+1 month");
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

Результат выполнения приведённого примера:

    Обычный год:
    Начало: 2015-01-31 00:00:00 -05:00
    Конец:  2015-03-03 00:00:00 -05:00
    Високосный год:
    Начало: 2016-01-31 00:00:00 -05:00
    Конец:  2016-03-02 00:00:00 -05:00

Для получения последнего дня следующего месяца (то есть чтобы предотвратить переполнение) существует директива `last day of`.

    <?php
    echo "Обычный год:\n"; // Февраль содержит 28 дней
    $dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("last day of next month");
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

    echo "Високосный год:\n"; // Февраль содержит 29 дней
    $dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
    echo "Начало: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
    $dt = $dt->modify("last day of next month");
    echo "Конец:  ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

Результат выполнения приведённого примера:

    Обычный год:
    Начало: 2015-01-31 00:00:00 -05:00
    Конец:  2015-02-28 00:00:00 -05:00
    Високосный год:
    Начало: 2016-01-31 00:00:00 -05:00
    Конец:  2016-02-29 00:00:00 -05:00

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open