В следующих примерах показываются некоторые подводные камни вычислений DateTime, относительно переходов на летнее и зимнее время (DST), и месяцев, имеющих разное количество дней.
Пример #1 DateTime::add/sub добавляет интервалы покрывающие прошедшее время
Добавление PT24H через переходы DST приведет к добавлению 23/25 часов (для большинства временных зон).
<?php
$dt = new DateTime("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->add(new DateInterval("PT3H"));
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>
Результат выполнения данного примера:
Start: 2015-11-01 00:00:00 -04:00 End: 2015-11-01 02:00:00 -05:00
Пример #2 DateTime::modify и strtotime увеличит или уменьшит значения индивидуальных компонентов
Добавление +24 часов через переход DST добавит точно 24 часов (вместо учета перехода на зимнее или летнее время).
<?php
$dt = new DateTime("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->modify("+24 hours");
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>
Результат выполнения данного примера:
Start: 2015-11-01 00:00:00 -04:00 End: 2015-11-02 00:00:00 -05:00
Пример #3 Добавление или вычитание времени может уменьшить или увеличить дату
Например 31 Января + 1 месяц вернет 2 Марта (високосный год) или 3 Марта (обычный год).
<?php
echo "Обычный год:\n"; // Февраль содержит 28 дней
$dt = new DateTime("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->modify("+1 month");
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
echo "Високосный год:\n"; // Февраль содержит 29 дней
$dt = new DateTime("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->modify("+1 month");
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>
Результат выполнения данного примера:
Обычный год: Start: 2015-01-31 00:00:00 -05:00 End: 2015-03-03 00:00:00 -05:00 Високосный год: Start: 2016-01-31 00:00:00 -05:00 End: 2016-03-02 00:00:00 -05:00
Для получения последнего дня следующего месяца (например для исключения переполнения), с PHP 5.3.0 существует директива last day of.
<?php
echo "Обычный год:\n"; // Февраль содержит 28 дней
$dt = new DateTime("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->modify("last day of next month");
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
echo "Високосный год:\n"; // Февраль содержит 29 дней
$dt = new DateTime("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt->modify("last day of next month");
echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>
Результат выполнения данного примера:
Обычный год: Start: 2015-01-31 00:00:00 -05:00 End: 2015-02-28 00:00:00 -05:00 Високосный год: Start: 2016-01-31 00:00:00 -05:00 End: 2016-02-29 00:00:00 -05:00