Aritmética de DateTime

Los siguientes ejemplos muestran algunos inconvenientes de la artimética de DateTime con respeco a las transiciones de DST y a los meses que tengan diferente número de días.

Ejemplo #1 DateTime::add/sub, añadir intervalos que cubren el tiempo transcurrido

Al añadir PT24H a una transición de DST parecerá que se añaden 23/25 horas (para la mayoría de las zonas horarias).

<?php
$dt 
= new DateTime("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->add(new DateInterval("PT3H"));
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

El resultado del ejemplo sería:

Inicio: 2015-11-01 00:00:00 -04:00
Fin:    2015-11-01 02:00:00 -05:00

Ejemplo #2 DateTime::modify y strtotime, aumentar o disminuir valores de componentes individuales

Al añadir +24 hours a una transición de DST se añadirán exactamente 24 horas como se ve en la cadena de fecha/hora (a menos que el inicio o el final esté en un punto de transición).

<?php
$dt 
= new DateTime("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->modify("+24 hours");
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

El resultado del ejemplo sería:

Inicio: 2015-11-01 00:00:00 -04:00
Fin:    2015-11-02 00:00:00 -05:00

Ejemplo #3 Añadir o restar horas puede sobrepasar o subpasar fechas

January 31st + 1 month resultará en March 2nd (año bisiesto) o 3rd (año normal).

<?php
echo "Año normal:\n"// February has 28 days
$dt = new DateTime("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->modify("+1 month");
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;

echo 
"Año bisiesto:\n"// February has 29 days
$dt = new DateTime("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->modify("+1 month");
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

El resultado del ejemplo sería:

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 siguiente mes (esto es, para prevenir el desbordamiento), a partir de PHP 5.3.0 está disponible el formato last day of.

<?php
echo "Año normal:\n"// February has 28 days
$dt = new DateTime("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->modify("last day of next month");
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;

echo 
"Año bisiesto:\n"// February has 29 days
$dt = new DateTime("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->modify("last day of next month");
echo 
"Fin:    "$dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

El resultado del ejemplo sería:

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

add a note add a note

User Contributed Notes 1 note

up
0
info at mobger dot de
6 days ago
There is a pitfall with the summertime in the timezone Europe/Berlin.

Beginning Summer-Timer
=======================

The hour between 2020-03-29 02:00:00 and 2020-03-29 03:00:00 is missing in the Timezone 'Europe/Berlin'.

Explanation to the code below
----------------------------------
The DateInterval of 20700 minutes point to 2020-03-29 03:00:00.
The DateInterval of 20701 minutes points to the not existing '2020-03-29 02:59:00', which is resolved to '2020-03-29 03:59:59' by rounding up.
The DateInterval-object of 20761 minutes calculate the existing '2020-03-29 01:59:00'. The hour between 2:00 - 3:00 does not exist in the euopean summertime for the date 2020-03-29. The missing hour will be additionally calculated in the substraction. This explain the result '2020-03-29 00:59:00'. It seems, that the addition of the DateInterval-object does not recalculate the missing hour for some (?) reasons.

<?php

        $test
= [];
       
$check = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
        for (
$i = 0; $i < 30000; $i++) {
           
$expectEaster = clone $check;
           
$interval = new DateInterval('PT' . $i . 'M');
           
$expectEaster->sub($interval);
           
$dummy = clone $expectEaster;
           
$expectEaster->add($interval);
            if (!
in_array($expectEaster->format('Y-m-d H:i:s'), $test)) {
               
$test[$i] = $expectEaster->format('Y-m-d H:i:s');
                echo(
$i . ' near summertime start ' . $expectEaster->format('Y-m-d H:i:s') . '[' . $dummy->format('Y-m-d H:i:s') . ']' . "\n");
            }
        }
       
//        0 near summertime start 2020-04-12 12:00:00[2020-04-12 12:00:00]
        //    20701 near summertime start 2020-04-12 13:00:00[2020-03-29 03:59:00]
        //    20761 near summertime start 2020-04-12 11:00:00[2020-03-29 00:59:00]

?>

Ending Summer Timer
====================
The european summertime defines the existence of two hours with the notation 2020-10-25 02:00:00 - 2020-10-25 03:00:00 (a) and 2020-10-25 02:00:00 - 2020-10-25 03:00:00 (b).
It seems, that the dateTime-object handles the additional hour on an simple internal way. It seems, that you can't differ between this two hours (a) and (b) with the DateTime-object in a simple way.

<?php
        $check
= date_create_from_format('Y-m-d H:i:s', '2020-10-25 06:00:00', new DateTimeZone('Europe/Berlin'));
        for (
$i = 0; $i < 361; $i = $i + 60) {
           
$expectEaster = clone $check;
           
$interval = new DateInterval('PT' . $i . 'M');
           
$expectEaster->sub($interval);
           
$dummy = clone $expectEaster;
           
$expectEaster->add($interval);
           
$test[$i] = $expectEaster->format('Y-m-d H:i:s');
            echo(
$i . ' near summertime end ' . $expectEaster->format('Y-m-d H:i:s') .
               
'[' . $dummy->format('Y-m-d H:i:s') . ' / ' . $dummy->getTimestamp() . ']' .
               
"\n");
        }
//      0 near summertime end 2020-10-25 06:00:00[2020-10-25 06:00:00 / 1603602000]
//     60 near summertime end 2020-10-25 06:00:00[2020-10-25 05:00:00 / 1603598400]
//    120 near summertime end 2020-10-25 06:00:00[2020-10-25 04:00:00 / 1603594800]
//    180 near summertime end 2020-10-25 06:00:00[2020-10-25 03:00:00 / 1603591200]
//    240 near summertime end 2020-10-25 06:00:00[2020-10-25 02:00:00 / 1603587600]
//    300 near summertime end 2020-10-25 06:00:00[2020-10-25 02:00:00 / 1603587600]
//    360 near summertime end 2020-10-25 06:00:00[2020-10-25 01:00:00 / 1603580400]    }

?>
To Top