Los ejemplos siguientes muestran algunos problemas de la aritmética de DateTime en lo que respecta a las transiciones DST y los meses con diferente número de días.
Ejemplo #1 DateTimeImmutable::add/sub añadir un intervalo de tiempo transcurrido
Añadir PT24H más allá de una transición DST parecerá añadir 23/25 horas (para la mayoría de los husos horarios).
<?php
$dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->add(new DateInterval("PT3H"));
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;El ejemplo anterior mostrará:
Inicio: 2015-11-01 00:00:00 -04:00 Fin: 2015-11-01 02:00:00 -05:00
Ejemplo #2 DateTimeImmutable::modify y strtotime incrementar o decrementar valores individuales
Añadir +24 horas más allá de una transición DST puede añadir exactamente 24 horas como se ve con la cadena fecha/hora (excepto si la hora de inicio o fin está en un punto de transición).
<?php
$dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+24 hours");
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;El ejemplo anterior mostrará:
Inicio: 2015-11-01 00:00:00 -04:00 Fin: 2015-11-02 00:00:00 -05:00
Ejemplo #3 DateInterval::createFromDateString crea intervalos que incrementan o decrementan los valores de cada componente
DateInterval::createFromDateString() utiliza el
analizador de fecha/hora, y la parte horaria del intervalo resultante
incrementa o decrementa los valores de cada componente. Para un intervalo
creado con DateInterval::__construct() la parte
horaria representa en cambio tiempo transcurrido, de modo que ambos no son
intercambiables cuando una transición de zona horaria cae dentro de esa
parte horaria. La parte de fecha se aplica de la misma forma en ambos
casos, por lo que P1D y 1 day dan
siempre el mismo resultado.
<?php
$dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
echo "PT24H: ", $dt->add(new DateInterval("PT24H"))->format("Y-m-d H:i:s P"), PHP_EOL;
echo "24 hours: ", $dt->add(DateInterval::createFromDateString("24 hours"))->format("Y-m-d H:i:s P"), PHP_EOL;El ejemplo anterior mostrará:
Inicio: 2015-11-01 00:00:00 -04:00 PT24H: 2015-11-01 23:00:00 -05:00 24 hours: 2015-11-02 00:00:00 -05:00
Ejemplo #4 La adición o sustracción de fechas/horas puede exceder (en más o en menos) fechas
Como para 31 de Enero + 1 mes dará como resultado 2 de Marzo (año bisiesto) o 3 de Marzo (año normal).
<?php
echo "Año normal:\n"; // febrero tiene 28 días
$dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+1 month");
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
echo "Año bisiesto:\n"; // febrero tiene 29 días
$dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+1 month");
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;El ejemplo anterior mostrará:
Año normal: Inicio: 2015-01-31 00:00:00 -05:00 Fin: 2015-03-03 00:00:00 -05:00 Año bisiesto: Inicio: 2016-01-31 00:00:00 -05:00 Fin: 2016-03-02 00:00:00 -05:00
Para obtener el último día del mes próximo (es decir, para prever el
excedente), el formato last day of está disponible.
<?php
echo "Año normal:\n"; // febrero tiene 28 días
$dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("last day of next month");
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
echo "Año bisiesto:\n"; // febrero tiene 29 días
$dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo "Inicio: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("last day of next month");
echo "Fin: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;El ejemplo anterior mostrará:
Año normal: Inicio: 2015-01-31 00:00:00 -05:00 Fin: 2015-02-28 00:00:00 -05:00 Año bisiesto: Inicio: 2016-01-31 00:00:00 -05:00 Fin: 2016-02-29 00:00:00 -05:00