PHP Velho Oeste 2024

Rechnen mit Datum und Uhrzeit

Die folgenden Beispiele zeigen einige Fallstricke beim Rechnen mit Datum und Uhrzeit bezüglich der Sommerzeitumstellungen sowie der Monate mit unterschiedlicher Anzahl von Tagen.

Beispiel #1 DateTimeImmutable::add/sub addiert Intervalle, die die verstrichene Zeit umfassen

Wenn PT24H über den Zeitpunkt einer Sommerzeitumstellung hinzugefügt wird, scheinen 23/25 Stunden hinzugefügt zu werden (bei den meisten Zeitzonen).

<?php
$dt
= new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->add(new DateInterval("PT3H"));
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Beginn: 2015-11-01 00:00:00 -04:00
Ende:   2015-11-01 02:00:00 -05:00

Beispiel #2 DateTimeImmutable::modify und strtotime erhöhen oder verringern die Werte einzelner Komponenten

Wenn +24 Stunden über den Zeitpunkt einer Sommerzeitumstellung hinzugefügt werden, werden genau 24 Stunden hinzugefügt, wie sie in der Datum/Zeit-Zeichenkette zu sehen sind (es sei denn, die Anfangs- oder Endzeit liegt auf einem Übergangspunkt).

<?php
$dt
= new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+24 hours");
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Beginn: 2015-11-01 00:00:00 -04:00
Ende:   2015-11-02 00:00:00 -05:00

Beispiel #3 Durch die Addition oder Subtraktion von Zeiten kann das Datum über- oder unterschritten werden

Zum Beispiel ergibt der 31. Januar + 1 Monat den 2. März (Schaltjahr) oder den 3. März (normales Jahr).

<?php
echo "Normales Jahr:\n"; // Februar hat 28 Tage
$dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+1 month");
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

echo
"Schaltjahr:\n"; // Februar hat 29 Tage
$dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("+1 month");
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Normales Jahr:
Beginn: 2015-01-31 00:00:00 -05:00
Ende:   2015-03-03 00:00:00 -05:00
Schaltjahr:
Beginn: 2016-01-31 00:00:00 -05:00
Ende:   2016-03-02 00:00:00 -05:00

Um den letzten Tag des nächsten Monats zu erhalten (d.h. um den Überlauf zu verhindern), gibt es das Format last day of.

<?php
echo "Normales Jahr:\n"; // Februar hat 28 Tage
$dt = new DateTimeImmutable("2015-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("last day of next month");
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;

echo
"Schaltjahr:\n"; // Februar hat 29 Tage
$dt = new DateTimeImmutable("2016-01-31 00:00:00", new DateTimeZone("America/New_York"));
echo
"Beginn: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
$dt = $dt->modify("last day of next month");
echo
"Ende: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL;
?>

Das oben gezeigte Beispiel erzeugt folgende Ausgabe:

Normales Jahr:
Beginn: 2015-01-31 00:00:00 -05:00
Ende:   2015-02-28 00:00:00 -05:00
Schaltjahr:
Beginn: 2016-01-31 00:00:00 -05:00
Ende:   2016-02-29 00:00:00 -05:00

add a note add a note

User Contributed Notes 1 note

up
4
info at mobger dot de
3 years 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