Pit: Rechnen mit Datum / Brett vorm Kopf

Hallo Forum,

ich möchte gerade von einem Anfangs-Unix-Timestamp bis zu einem End-Unix-Timestamp die Kalenderwochen in ein Array packen. Leider ergibt mein Code lediglich die ersten beiden KWs und ich finde den Fehler nicht. Irgendwie hab ich ein Brett vorm Kopf, glaub ich.

Helft mir mal bitte auf die Sprünge.

$myStart = 1519095600; 
$myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
$myJahr = date('Y',$myStart);
$myKW = date('W',$myEnd);
$arr_alleWochen[] = $myKW."_".$myJahr;     // 1.Eintrag
$myZeiger = $myStart;
while ($myZeiger <= $myEnd) {
$myZeiger = $myJahr."W".$myKW;      // Montag dieser Woche
$next_week = date('W', strtotime($myZeiger." +1 week"))."_".date('Y', strtotime($myZeiger." +1 week +3day"));
$arr_alleWochen[] = $next_week;            // alle weiteren KWs
$myZeiger = strtotime($myZeiger." +1 week");
}
echo("<pre>");
print_r($arr_alleWochen);

ergibt:

Array ( [0] => 11_2018 [1] => 12_2018 )

Pit

  1. Hallo Pit,

    ich möchte gerade von einem Anfangs-Unix-Timestamp bis zu einem End-Unix-Timestamp die Kalenderwochen in ein Array packen. Leider ergibt mein Code lediglich die ersten beiden KWs

    und die sind auch noch falsch

    $myStart = 1519095600;
    $myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
    

    19.02.2018 - 18.03.2018 (KW 8 - 11)

    Array
    (
        [0] => 11_2018
        [1] => 12_2018
    )
    

    Bis demnächst
    Matthias

    --
    Rosen sind rot.
    1. Hi Matthias,

      und die sind auch noch falsch

      Jain.

      $myStart = 1519095600;
      $myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
      

      19.02.2018 - 18.03.2018 (KW 8 - 11)

      Wird doch über $myZeiger = $myJahr."W".$myKW; zu Mo1 00:00:00.

      Array
      (
          [0] => 11_2018
          [1] => 12_2018
      )
      

      Und natürlich stimmen diese KWs nicht mit meiner Erwartung überein, sonst gäbe es doch mein Post erst gar nicht.

      Pit

  2. Tach!

    ich möchte gerade von einem Anfangs-Unix-Timestamp bis zu einem End-Unix-Timestamp die Kalenderwochen in ein Array packen. Leider ergibt mein Code lediglich die ersten beiden KWs

    Nein, die erste Woche wäre die achte nicht die elfte. Die elfte ist die Woche vom Enddatum, weil $myKW = date('W',$myEnd);. Nehme ich $myStart statt $myEnd gibts eine Endlosschleife, weil $myZeiger die Abbruchbedingung ist, aber nie weitergezählt wird.

    Kontrollausgaben der beteiligten Variablen helfen die Abläufe nachzuvollziehen und so Fehler zu finden.

    dedlfix.

    1. Hi dedlfix,

      Nein, die erste Woche wäre die achte nicht die elfte. Die elfte ist die Woche vom Enddatum, weil $myKW = date('W',$myEnd);. Nehme ich $myStart statt $myEnd gibts eine Endlosschleife, weil $myZeiger die Abbruchbedingung ist, aber nie weitergezählt wird.

      Du hast recht. Leider bin ich grad wirklich wirr im Kopf, ich bekomme $myZeiger nicht hochgezählt. Sich, ich könnte $myJahr und $myKW hochzählen und daraus dann $myZeiger errechnen, aber das muß doch eigentlich in einem 1-zeiler gehen??? Heut komme ich da jedenfalls nicht mehr drauf... eventuell morgen wieder 😉

      Pit

  3. Hi,

    $myStart = 1519095600; 
    $myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
    

    nö, sind es nicht. Die Differenz ist 2318399 (ich hau mal noch 1 drauf, als 2318400): 2318400 / 3600 ergibt 644 Stunden, / 24 ergibt 26,83333 Tage. 4 Wochen wären aber 28 Tage +/- 1 Stunde (Ende März/Ende Oktober weicht's ja etwas ab).

    
    > $myJahr = date('Y',$myStart);
    > $myKW = date('W',$myEnd);
    
    

    Das Jahr vom Start, die Woche vom Ende? Das geht spätestens dann schief, wenn Start und Ende in verschiedenen Jahren liegen …

    
    > $arr_alleWochen[] = $myKW."_".$myJahr;     // 1.Eintrag
    > $myZeiger = $myStart;
    
    

    $myZeiger ist jetzt ein Unix-Timestamp.

    
    > while ($myZeiger <= $myEnd) {
    > $myZeiger = $myJahr."W".$myKW;      // Montag dieser Woche
    
    

    jetzt ist $myZeiger ein String 2018W11. Warum Du das mit "Montag dieser Woche" kommentierst, erschließt sich mir nicht.

    
    > $next_week = date('W', strtotime($myZeiger." +1 week"))."_".date('Y', strtotime($myZeiger." +1 week +3day"));
    
    

    Hier wird der Jahreswechsel berücksichtigt (+3day), bei Jahreswechsel zwischen $myStart und $myEnd ist aber schon der erste Eintrag falsch.

    
    > $arr_alleWochen[] = $next_week;            // alle weiteren KWs
    > $myZeiger = strtotime($myZeiger." +1 week");
    
    

    Oh, $myZeiger wird jetzt wieder ein Unix-Timestamp. Sehr verwirrend, der ständige Bedeutungswechsel von $myZeiger. Geht Dir das nicht auf den Zeiger? 😉

    Ach ja, Einrückungen würden den Code besser lesbar machen.

    cu,
    Andreas a/k/a MudGuard

    1. Hi Andreas,

      > $myJahr = date('Y',$myStart);
      > $myKW = date('W',$myEnd);
      

      Das Jahr vom Start, die Woche vom Ende? Das geht spätestens dann schief, wenn Start und Ende in verschiedenen Jahren liegen …

      Alles gut, das hatte dedlfix schon bemerkt, das war ein Flüchtigkeitsfehler.

      > $arr_alleWochen[] = $myKW."_".$myJahr;     // 1.Eintrag
      > $myZeiger = $myStart;
      

      $myZeiger ist jetzt ein Unix-Timestamp.

      Genau... damit die While-Bedingung aufgeht, denn $myEnd ist auch einer.

      > while ($myZeiger <= $myEnd) {
      > $myZeiger = $myJahr."W".$myKW;      // Montag dieser Woche
      

      jetzt ist $myZeiger ein String 2018W11. Warum Du das mit "Montag dieser Woche" kommentierst, erschließt sich mir nicht.

      Ich denke, das habe ich für mich gemacht, weil das mir (als ich das irgendwo las, neu war...)

      > $next_week = date('W', strtotime($myZeiger." +1 week"))."_".date('Y', strtotime($myZeiger." +1 week +3day"));
      

      Hier wird der Jahreswechsel berücksichtigt (+3day), bei Jahreswechsel zwischen $myStart und $myEnd ist aber schon der erste Eintrag falsch.

      Das siehst Du? Das ist echt nicht trivial, Hut ab, spricht für Dich. DaumenHoch!

      > $arr_alleWochen[] = $next_week;            // alle weiteren KWs
      > $myZeiger = strtotime($myZeiger." +1 week");
      

      Oh, $myZeiger wird jetzt wieder ein Unix-Timestamp. Sehr verwirrend, der ständige Bedeutungswechsel von $myZeiger. Geht Dir das nicht auf den Zeiger? 😉

      Aber hallo doch! Ja, tut es... Aber das wolte ich ja so, wegen der while-Bedigung...

      Ach ja, Einrückungen würden den Code besser lesbar machen.

      Wenn Du einen guten Editor kennst, der das gut macht... immer her mit Deinen Tips 😉

      Pit

  4. Moin,

    $myStart = 1519095600; 
    $myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
    

    falscher Ansatz. Eine Woche drückt sich in Tagen aus, nicht in Sekunden.

    MfG

    1. Moin,

      $myStart = 1519095600; 
      $myEnd = 1521413999; // sind 4 Wochen in Sekunden von Mo1, 00:00:00 bis So4, 23:59:59
      

      falscher Ansatz. Eine Woche drückt sich in Tagen aus, nicht in Sekunden.

      Hi pl,

      von mir aus. Aber am grundsätzlichen Problem meines Codes ändert das nichts. Sekunden habe ich ja übrigens nur genommen, weil php bzw. unixtimestamps das auch nimmt. Aber wie gesagt, es ändert am Problem selber nichts.

      Pit

  5. @@Pit

    Das Rechnen mit Zeitpunkten/Daten ist eigentlich recht einfach, wenn man folgende Grundregel verinnerlicht hat: Rechne immer mit timestamps! Die Formatierung in ein menschenfreundliches Format (Jahr/Monat/Tag oder Jahr/Kalenderwoche/Wochentag) passiert bei der Ausgabe; keinen Moment früher.

    Um von deinem Startzeitpunkt zum Zeitpunkt eine Woche später zu kommen, musst du die Anzahl der Sekunden einer Woche draufaddieren. (PHP rechnet in Sekunden; JavaScript in Millisekunden.)

    Ich nehme an, du willst die Zeitpunkte in der lokalen Zeit haben, 1518998100 soll 2018-02-19 00:55 MEZ sein (also Montag der 8. KW), nicht 2018-02-18 23:55 UTC (Sonntag der 7. KW).

    Dann kann es allerdings passieren, dass du beim Aufaddieren in der falschen KW landest, wenn zwischendurch die Zeit umgestellt wird. Bsp.: auf 2018-03-18 23:55 MEZ (Sonntag der 11.KW) die Anzahl der Sekunden einer Woche draufaddiert ergibt 2018-03-26 00:55 MESZ (Montag der 13.KW); auf 2018-10-22 00:55 MESZ (Montag der 43. KW) draufaddiert ergibt 2018-10-28 23:55 MEZ (Sonntag der 43. KW).

    Um das zu verhindern, setzt du die Uhrzeit (um die geht es dir ja nicht) der betreffenden Tage auf 12:00 mittags. In deinem Beispiel machst du den Startzeitpunkt 2018-02-20 04:00 MEZ (1519095600) zu 2018-02-20 12:00 MEZ (1519124400).

    Auf diesen Startzeitpunkt kannst du in einer Schleife jeweils die Anzahl der Sekunden einer Woche draufaddieren und den Zeitpunkt in einem Array ablegen – im gewünschten Format Jahr/Kalenderwoche[1] –, solange der Zeitpunkt vor dem Endzeitpunkt liegt. Danach musst du prüfen, ob du die Kalenderwoche des Endzeitpunkts schon erfasst hast, also ob die Kalenderwoche des letzten Elements im Array mit der des Endzeitpunkts übereinstimmt. Wenn nicht, letztere noch ergänzen. Fertig.

    LLAP 🖖

    --
    „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann

    1. Das Format sollte wohl besser standardgemäß 2018-W11 (oder 2018W11) sein, nicht 11_2018. ↩︎

    1. Tach!

      Das Rechnen mit Zeitpunkten/Daten ist eigentlich recht einfach, wenn man folgende Grundregel verinnerlicht hat: Rechne immer mit timestamps! Die Formatierung in ein menschenfreundliches Format (Jahr/Monat/Tag oder Jahr/Kalenderwoche/Wochentag) passiert bei der Ausgabe; keinen Moment früher.

      Um von deinem Startzeitpunkt zum Zeitpunkt eine Woche später zu kommen, musst du die Anzahl der Sekunden einer Woche draufaddieren. (PHP rechnet in Sekunden; JavaScript in Millisekunden.)

      Alternative unter PHP: Timestamps als Basis nehmen, aber Rechnungen mit strtotime() durchführen. Hat den Vorteil, dass zum einen die Zeitzonenwechsel berücksichtigt werden und zum anderen man sprechende Operatoren angeben kann, statt Magic Numbers oder Formeln, die man erst zurück- oder ausrechnen muss, um den Code zu verstehen.

      Das ist ja im Wesentlichen auch, was Pit macht. Es hilft nur beides nicht, wenn man logische Fehler im Programm hat.

      Das Format sollte wohl besser standardgemäß 2018-W11 (oder 2018W11) sein, nicht 11_2018.

      Auf welchen Faktoren des Anwendungsfalles basiert diese Bewertung? Wenn du mal genau hinschaust, steht in dem gezeigten Code genau diese Schreibweise (die in Klammern), an einer Stelle, wo es für den Anwendungsfall benötigt wird (abgesehen vom logischen Problem im Code).

      dedlfix.

      1. Hi,

        Alternative unter PHP: Timestamps als Basis nehmen, aber Rechnungen mit strtotime() durchführen. Hat den Vorteil, dass zum einen die Zeitzonenwechsel berücksichtigt werden und zum anderen man sprechende Operatoren angeben kann, statt Magic Numbers oder Formeln, die man erst zurück- oder ausrechnen muss, um den Code zu verstehen.

        Das ist ja im Wesentlichen auch, was Pit macht. Es hilft nur beides nicht, wenn man logische Fehler im Programm hat.

        Ich dachte auch eigentlich, dass ich es schon so mache…

        Verbesserungsvorschlag:

        $myStart = 1519095600; 
        $myEnd = 1521413999;
        $myJahr = date('Y',$myStart);
        $myKW = date('W',$myStart);
        $arr_alleWochen[] = $myJahr."W".$myKW;     // 1.Eintrag
        $myZeiger = $myJahr."W".$myKW;      // ergibt immer den Montag dieser Woche
        while ($myZeiger < date('Y',$myEnd)."W".date('W',$myEnd)) {
        $myZeiger = date('Y', strtotime($myZeiger." +1 week +3day"))."W".date('W', strtotime($myZeiger." +1 week"));
        $arr_alleWochen[] = $myZeiger;            // alle weiteren KWs
        }
        echo("<pre>");
        print_r($arr_alleWochen);
        

        ergibt:

        Array
        (
            [0] => 2018W08
            [1] => 2018W09
            [2] => 2018W10
            [3] => 2018W11
        )
        

        Pit

        1. Tach!

          $myJahr = date('Y',$myStart);
          $myKW = date('W',$myStart);
          $myZeiger = $myJahr."W".$myKW;      // ergibt immer den Montag dieser Woche
          

          Hier gibt es ein verstecktes Problem. Das äußert sich nur sporadisch in manchen Jahren. Y ist immer das Jahr des Datums. Die Kalenderwoche kann in den letzten Tagen des Jahres aber auch schon die vom nächsten Jahr sein. Der 31.12.2018 ist bereits KW 01-2019. Du muss hier das zur Kalenderwoche passende Jahr nehmen, und das bekommst du mit o statt Y.

          dedlfix.

          1. Hi dedlfix,

            Hier gibt es ein verstecktes Problem. Das äußert sich nur sporadisch in manchen Jahren. Y ist immer das Jahr des Datums. Die Kalenderwoche kann in den letzten Tagen des Jahres aber auch schon die vom nächsten Jahr sein. Der 31.12.2018 ist bereits KW 01-2019. Du muss hier das zur Kalenderwoche passende Jahr nehmen, und das bekommst du mit o statt Y.

            Autsch! und Danke. 😀 Sollte ich das dann nicht besser an allen Stellen in meinen Scripten ändern oder gibt es für date('Y',...) eine Dahseinsberechtigung in unseren Gefilden?

            Pit

            1. Tach!

              Sollte ich das dann nicht besser an allen Stellen in meinen Scripten ändern

              Überall da, wo du die Kalenderwoche und das Jahr dazu brauchst.

              oder gibt es für date('Y',...) eine Dahseinsberechtigung in unseren Gefilden?

              Ja klar, der 31.12. bleibt ja weiterhin im Jahr 2018, egal welches Jahr zu seiner Kalenderwoche gehört.

              dedlfix.

              1. Hi dedlfix,

                oder gibt es für date('Y',...) eine Dahseinsberechtigung in unseren Gefilden?

                Ja klar, der 31.12. bleibt ja weiterhin im Jahr 2018, egal welches Jahr zu seiner Kalenderwoche gehört.

                Oops, da hätte ich selber drauf kommen können 😉

                Danke für die Hilfe,

                Pit