Ankündigung

Einklappen
Keine Ankündigung bisher.

[Info] DateInterval normalisieren

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • [Info] DateInterval normalisieren

    Wenn in PHP mit Zeiten (Arbeitszeiten, Dauer irgendwelcher Vorgänge) gearbeitet werden muss wird dafür oft strtotime() oder DateTime bemüht. Die sind dafür eigentlich nicht gedacht, da strtotime immer einen Zeitstempel und das DateTime-Objekt immer einen Zeitpunkt (ein bestimmtes Datum + Zeit) darstellt.

    DateInterval sollte eigentlich das Objekt für Zeitintervalle sein, besitzt aber nur einige wenige Methoden mit so einigen Problemen.

    Beispiel 1: Formatierung von Sekunden in Stunden:Minuten:Sekunden

    PHP-Code:
    $dt DateInterval::createFromDateString('2185 Seconds');
    echo 
    $dt->format('%H:%I:%S'); 
    Ausgabe: 00:00:2185

    Das Ergebnis ist nicht das was man hier erwarten würde. Noch mehr Kopfschütteln verursacht Beispiel 2:

    PHP-Code:
    $di DateInterval::createFromDateString('5 hour -63 Minutes 185 Seconds 1500000 Microseconds');
    echo 
    $di->format('%H:%I:%S.%F'); 
    Ausgabe: 05:-63:185.1500000

    Die DateTime::diff() Methode liefert dagegen immer ein normalisiertes DateInterval. Das wird von dieser smarten Funktion

    PHP-Code:
    function normalizeDateInterval(DateInterval $interval$base "@0")
    {
      
    $baseDate date_create($base);
      return 
    $baseDate->diff((clone $baseDate)->add($interval));

    ausgenutzt um Abhilfe für die obigen Probleme zu schaffen.

    Beispiel 3:
    PHP-Code:
    $di DateInterval::createFromDateString('2185 Seconds');
    $di normalizeDateInterval($di);
    echo 
    $di->format('%H:%I:%S'); 
    Ausgabe: 00:36:25

    Wird für Beispiel 2 die Normalisierung zugefügt, ergibt sich das folgende sinnvolle Resultat:
    Code:
    04:00:06.500000
    Ich denke die "Normalisierung" kann auch für DateIntervall einige interssante Anwendungen ermöglichen.



  • #2
    Formatierung von Sekunden in Stunden:Minuten:Sekunden
    Formatierung ist nicht gleichbedeutend mit Normalisierung.

    Noch mehr Kopfschütteln verursacht Beispiel 2
    Nur wenn man eine Normalisierung erwartet.

    Das wird von dieser smarten Funktion ausgenutzt um Abhilfe für die obigen Probleme zu schaffen.
    Das gilt aber nur für den Spezialfall, wo die Sekundenbereiche kleiner als 24 Stunden sind.

    Es gibt auch explizit im Handbuch ein Hinweis auf die "fehlende" Normalisierung.
    The DateInterval::format() method does not recalculate carry over points in time strings nor in date segments. This is expected because it is not possible to overflow values like "32 days" which could be interpreted as anything from "1 month and 4 days" to "1 month and 1 day".

    Kommentar


    • #3
      Zitat von Dormilich Beitrag anzeigen
      Es gibt auch explizit im Handbuch ein Hinweis auf die "fehlende" Normalisierung.
      Mag schon so sein, dass erklärt wird, dass es nicht gemacht wird. Aber das erklärt nicht, warum es nicht gemacht wird. Die Ausrede mit den Monaten ist nur eine Ausrede, aber kein wirklicher Grund.

      Kommentar


      • #4
        Du hast das Zitat nicht gelesen, oder?

        Ich halte die Aussage »because it is not possible to overflow values like "32 days" which could be interpreted as anything from "1 month and 4 days" to "1 month and 1 day".« für alles andere als eine Ausrede.

        Kommentar


        • #5
          Zitat von Dormilich Beitrag anzeigen
          Du hast das Zitat nicht gelesen, oder?

          Ich halte die Aussage »because it is not possible to overflow values like "32 days" which could be interpreted as anything from "1 month and 4 days" to "1 month and 1 day".« für alles andere als eine Ausrede.
          Deswegen lässt man 32 Tage auch als 32 Tage und versucht da nicht irgendwas hinein zu interpretieren, was gar nicht möglich ist.

          Hier mal ein Beispiel aus C#:
          Code:
          var timeSpan = new TimeSpan();
          
          timeSpan = timeSpan.Add(TimeSpan.FromDays(32));
          timeSpan = timeSpan.Add(TimeSpan.FromSeconds(3600));
          
          Console.WriteLine(timeSpan.ToString());
          // 32.01:00:00

          Kommentar


          • #6
            Was ist dann aber der prinzipielle Unterschied zwischen 32 Tagen und 2185 Sekunden?

            Kommentar


            • #7
              Deswegen lässt man 32 Tage auch als 32 Tage und versucht da nicht irgendwas hinein zu interpretieren, was gar nicht möglich ist.
              Und bei Schaltsekunden ist es ok, wenn man sie ignoriert?

              Kommentar


              • #8
                Zitat von Dormilich Beitrag anzeigen
                Was ist dann aber der prinzipielle Unterschied zwischen 32 Tagen und 2185 Sekunden?
                Eine Zeitspanne ist eine Zeitspanne. Da werden keine Schaltsekunden und sonstige Dinge berücksichtigt. Eine Zeitspanne hat auch keine Zeitzone.

                Kommentar


                • #9
                  Deswegen heißt die Klasse ja auch DateInterval und nicht TimeSpan.

                  Kommentar


                  • #10
                    Ich denke man sollte eine Normalisierung soweit möglich auch für DateInterval erwarten. Das betrifft Sekunden, Minuten, Stunden und Tage(total). Die Probleme mit Monaten und Jahren beim DateInterval in den Griff zu bekommen halte ich vom Grundsatz her für nicht möglich. Die obigen Beispiele #1 zeigen das eine Formatierung eines DateInterval-Objektes nur Sinn macht wenn eine Normalisierung vorausgeht.

                    Eine Frage die sich mir stellt ist:
                    An welcher Stelle sollte eine Normalisierung erfolgen?
                    1. bereits im Konstruktor, so dass das Objekt selbst normalisiert ist
                    2. jeweils vor einer Formatierung/Ausgabe

                    Ich neige zu Punkt 2. Denn wie was intern gespeichert wird ist doch erstmal sekundär, solange nicht auf die internen Properties zugegriffen wird.

                    Ein Aspekt sollte unabhängig davon jedoch immer erfüllt sein.
                    Wenn ich eine Differenz von StartDate zu EndDate ermittle, dann sollte die Probe Startdate + Differenz auch immer wieder EndDate ergeben.
                    Das ist leider nicht der Fall. Dieser Bug begleitet PHP seit Jahren:

                    PHP-Code:
                    <?php
                    $startDate 
                    "2020-02-01";
                    $endDate "2020-03-11";

                    date_default_timezone_set("Europe/Berlin");

                    $diff date_create($startDate)->diff(date_create($endDate));

                    $test date_create($startDate)->add($diff)->format("Y-m-d");

                    echo 
                    "test: ".$test." expected: ".$endDate;

                    //test: 2020-03-09 expected: 2020-03-11
                    Online: https://3v4l.org/gWteh


                    Kommentar


                    • #11
                      Zitat von jspit Beitrag anzeigen
                      Ich denke man sollte eine Normalisierung soweit möglich auch für DateInterval erwarten. Das betrifft Sekunden, Minuten, Stunden und Tage(total). Die Probleme mit Monaten und Jahren beim DateInterval in den Griff zu bekommen halte ich vom Grundsatz her für nicht möglich.
                      Nicht alle Tage sind gleich lang. Stichwort: Zeitzonen.

                      Zitat von jspit Beitrag anzeigen
                      Die obigen Beispiele #1 zeigen das eine Formatierung eines DateInterval-Objektes nur Sinn macht wenn eine Normalisierung vorausgeht.

                      Eine Frage die sich mir stellt ist:
                      An welcher Stelle sollte eine Normalisierung erfolgen?
                      1. bereits im Konstruktor, so dass das Objekt selbst normalisiert ist
                      2. jeweils vor einer Formatierung/Ausgabe
                      Weder noch. Man kann ein DateInterval nur sinnvoll "normalisieren", wenn man es auf ein bestimmtes Ausgangsdatum bezieht.

                      Zitat von jspit Beitrag anzeigen
                      Wenn ich eine Differenz von StartDate zu EndDate ermittle, dann sollte die Probe Startdate + Differenz auch immer wieder EndDate ergeben.
                      Das ist leider nicht der Fall. Dieser Bug begleitet PHP seit Jahren:
                      (...)
                      Das ist merkwürdig, scheint aber nur bei Zeitzonen danebenzuliegen, die östlich vom Nullmeridian angeordnet sind: https://3v4l.org/LVQDJ
                      Wenn man die Wurst schräg anschneidet, hält sie länger, weil die Scheiben größer sind.

                      Kommentar


                      • #12
                        Zitat von fireweasel Beitrag anzeigen
                        Nicht alle Tage sind gleich lang. Stichwort: Zeitzonen.
                        Sommer/Winterzeit ? Auch damit hat PHP so seine Probleme.

                        Beispiel:
                        Nehmen wir den 29.3.2020 als Beispiel als die Uhren um eine Stunde von 2 auf 3 Uhr vorgestellt wurden.
                        Wenn ich von 00:00 bis 06:00 die Differenz von PHP berechnen lasse
                        PHP-Code:
                        $start date_create("2020-03-29 00:00");
                        $end date_create("2020-03-29 06:00");

                        $dateInterval $start->diff($end);

                        var_dump($dateInterval->h);  //int(5) 
                        bekomme ich die echt verstrichene Zeit von 5 Stunden. Soweit ok.
                        Am Rande: Mit Uralt-Versionen (PHP 5.3) wurden hier mal 6 Stunden ermittelt.

                        Addiere ich jetzt genau dieses $dateInterval zum Startdatum
                        PHP-Code:
                        var_dump($start->add($dateInterval)); 
                        dann erwarte ich mein Enddatum als Resultat, was jedoch nicht der Fall ist (bekomme 05:00 anstelle 06:00):
                        PHP-Code:
                        object(DateTime)#2 (3) {
                          
                        ["date"]=>
                          
                        string(26"2020-03-29 05:00:00.000000"
                          
                        ["timezone_type"]=>
                          
                        int(3)
                          [
                        "timezone"]=>
                          
                        string(13"Europe/Berlin"

                        PHP 7.4.2

                        Zitat von fireweasel Beitrag anzeigen
                        Weder noch. Man kann ein DateInterval nur sinnvoll "normalisieren", wenn man es auf ein bestimmtes Ausgangsdatum bezieht.
                        Wenn die Addidion oder Subtraktion auf ein Datum zu einen sinnvollen Ergebnis führen soll und mit Monaten und Jahren gearbeitet wird keine Frage.
                        Beschränke ich mich auf Stunden, Minuten und Sekunden ist m.E. eine Normalisierung immer möglich und entsprechende Berechnungen auf ein Datum sollten immer richtige Ergebnisse bringen. Ob ich da die Tage mit einbeziehen kann da bin ich mir nicht mehr so sicher.

                        Mich beschäftigt schon länger die Frage wohin sich das DateInterval-Objekt entwickelt bei einer solchen Struktur
                        PHP-Code:
                        object(DateInterval)#4 (16) {
                          
                        ["y"]=>
                          
                        int(0)
                          [
                        "m"]=>
                          
                        int(0)
                          [
                        "d"]=>
                          
                        int(0)
                          [
                        "h"]=>
                          
                        int(5)
                          [
                        "i"]=>
                          
                        int(0)
                          [
                        "s"]=>
                          
                        int(0)
                          [
                        "f"]=>
                          
                        float(0)
                          [
                        "weekday"]=>
                          
                        int(0)
                          [
                        "weekday_behavior"]=>
                          
                        int(0)
                          [
                        "first_last_day_of"]=>
                          
                        int(0)
                          [
                        "invert"]=>
                          
                        int(0)
                          [
                        "days"]=>
                          
                        int(0)
                          [
                        "special_type"]=>
                          
                        int(0)
                          [
                        "special_amount"]=>
                          
                        int(0)
                          [
                        "have_weekday_relative"]=>
                          
                        int(0)
                          [
                        "have_special_relative"]=>
                          
                        int(0)

                        und den noch immer vorhandenen Bugs mit DateInterval/DateTime.


                        Kommentar

                        Lädt...
                        X