Bernd: Samstag / Sonntag herausfiltern

Moin,

ich habe ein Start- und ein Enddatum. Jetzt möchte ich die Anzahl der Samstage und Sonntage herausfinden die in diesem Zeitraum vorkommen. Hat PHP dafür eine Funktion?

$start = "2018-01-01";
$ende  = "2018-01-17";

In diesem Beispiel haben wir:

2x Samstag
2x Sonntag

Die Feiertage werden in einem anderen Schritt abgezogen, die spielen in diesem Beispiel keine Rolle.

  1. Tach!

    ich habe ein Start- und ein Enddatum. Jetzt möchte ich die Anzahl der Samstage und Sonntage herausfinden die in diesem Zeitraum vorkommen. Hat PHP dafür eine Funktion?

    Nicht dass ich wüsste. Wäre aber auch zu speziell, dafür was eigenes zu haben.

    Ist aber recht einfach umzusetzen. Iteriere über ein entsprechendes DatePeriod-Objekt, ermittle den Wochentag (Format: w), zähle wenn Sonnabend oder Sonntag.

    Das kann man auch funktional lösen mit entsprechenden Array-Funktionen, die das DatePeriod-Objekt als Ausgangswert bekommen. Damit lassen sich auch recht elegant weitere Aufgabenstellungen lösen, wie eine Liste der Wochenendtage oder auch Arbeitstage zu filtern.

    dedlfix.

    1. Hallo dedlfix,

      nein, nicht iterieren. Man muss folgende Größen bestimmen:

      • W = Wochentag des Starttages, in PHP ist 0=Sonntag, 1=Montag, ..., 6=Samstag
      • die Differenz D der julianischen Tage von Ende- und Anfangsdatum. Dabei im Hinterkopf behalten, dass die Anzahl der Tage im Intervall um 1 größer ist, sofern $start und $ende Teil des betrachteten Zeitraums sind.
      • R = D % 7; // Rest von Intervallänge geteilt durch 7
      • Q = (D - R) / 7; // Integerquotient von D/7. Man könnte auch intdiv() verwenden...

      D bestimmt man am leichtesten, indem man $start und $ende in DateTime-Objekte überführt und die diff-Methode anwendet (alternativ die date_diff-Funktion). Das DateDifference-Objekt, das dabei herauskommt, hat eine Eigenschaft days.

      W bekommt man vom DateTime-Objekt von $start über die format("w") Methode.

      Grundwert der Anzahl Wochenend-Tage im Zeitintervall ist WE=2*Q. Dazu kommen 1 oder 2 Tage, je nach dem, ob am Intervallrand Wochenendtage hinzukommen. Dazu bildet man die Summe S von W und R.

      1. Endet das Intervall an einem Samstag (S=6), wird WE um 1 erhöht
      2. Schließt das Intervall ein ganzes Wochenende ein (S>6), wird WE um 2 erhöht
      3. Beginnt das Intervall an einem Sonntag, wird WE um 1 erhöht

      Fall 1 und Fall 2 schließen sich gegenseitig aus; Fall 3 kann gleichzeitig mit Fall 1 auftreten.

      Klingt kompliziert. Ist es aber nicht 😉. Die Fälle 1-3 musste ich mir allerdings auch mal irgendwann in eine Tabelle vor Augen führen, um festzustellen, dass die 49 möglichen Kombinationen aus Start- und Endetag letztlich auf 3 Prüfungen zurückführbar sind.

      Rolf

      --
      sumpsi - posui - clusi
      1. Hallo,

        leider verstehe ich nicht so recht was ihr meint bzw. wie ich es umsetzten soll. Meine Ansatz ist:

        $startDate = new DateTime('2018-01-01');
        $endDate = new DateTime('2018-01-18');
        
        $diff = $startDate->diff($endDate);
        
        echo $diff->days+1; //18
        

        Warum ist eine +1 Nutzen muss weiß ich nicht. Ohne diese bekomme ich nur 17 Tage.

        $startDate = explode(".","01.01.2018"); 
        $endDate = explode(".","18.01.2018"); 
        
        $startDate= mktime(0,0,0,$startDate[1],$startDate[0],$startDate[2]); 
        $endDate = mktime(0,0,0,$endDate[1],$endDate[0],$endDate[2]); 
        
        setlocale(LC_TIME, "ge","de_DE"); 
        
        while($startDate <= $endDate){ 
           echo strftime("%A" , $startDate)."
        "; 
           $startDate += 3600*24; 
        } 
        

        die Ausgabe ergibt

        Montag  
        Dienstag  
        Mittwoch  
        Donnerstag  
        Freitag  
        Samstag  
        Sonntag  
        Montag  
        Dienstag  
        Mittwoch  
        Donnerstag  
        Freitag  
        Samstag  
        Sonntag  
        Montag  
        Dienstag  
        Mittwoch  
        Donnerstag
        

        Der 01.01.2018 war ein Montag und heute haben wir Donnerstag, daher stimmt die Ausgabe. Habe ich jetzt eine Möglichkeit zu wählen wie viele Samstage und wie viele Sonntage darin vorkommen? Dann könnte ich diese Zahlen von $diff->days+1 wieder abziehen.

        1. Tach!

          leider verstehe ich nicht so recht was ihr meint bzw. wie ich es umsetzten soll. Meine Ansatz ist:

          $startDate = new DateTime('2018-01-01');
          $endDate = new DateTime('2018-01-18');
          
          $diff = $startDate->diff($endDate);
          
          echo $diff->days+1; //18
          

          Warum ist eine +1 Nutzen muss weiß ich nicht. Ohne diese bekomme ich nur 17 Tage.

          Die Differenz zwischen dem ersten (0 Uhr) und dem zweiten (0 Uhr) ist ein Tag, nicht zwei. Deshalb ist auch der 18. nicht 18 sondern 17 Tage vom ersten entfernt. Wenn du den 18 mit berücksichtigen möchtest, musst du dessen Ende nehmen, oder den Anfang vom nächsten Tag.

          $startDate += 3600*24;

          Und an Schalttagen? Rechne bei Tagesabständen nicht mit Sekunden, sondern lass die Mathematik vom System erledigen. strtotime() kann ganze Tage hinzufügen und macht das auch an Schalttagen richtig.

          Du brauchst das auch nicht zu Fuß aufzusetzen, denn DatePeriod gibt dir bereits eine Liste mit dem gewünschten Interval zwischen Start und Ende.

          Der 01.01.2018 war ein Montag und heute haben wir Donnerstag, daher stimmt die Ausgabe. Habe ich jetzt eine Möglichkeit zu wählen wie viele Samstage und wie viele Sonntage darin vorkommen?

          Mein Vorschlag war nun, sie zu zählen, indem du die Liste durchläufst und wenn Wochenendtag ist, dann den Zähler um eins erhöhen. RolfBs Vorschlag beruht hingegen auf Mathematik. Ist etwas komplexer aufzusetzen und zu durchschauen, dafür ist es aber bei größeren Intervallen vermutlich deutlich schneller als eine Liste durchzulaufen.

          dedlfix.

          1. Moin,

            Du brauchst das auch nicht zu Fuß aufzusetzen, denn DatePeriod gibt dir bereits eine Liste mit dem gewünschten Interval zwischen Start und Ende.

            passt das so besser?

            $begin = new DateTime( '2018-01-01' );
            $end = new DateTime( '2018-01-18' );
            $end = $end->modify( '+1 day' ); 
            
            $diff = $begin->diff($end);
            
            $interval = new DateInterval('P1D');
            $daterange = new DatePeriod($begin, $interval ,$end);
            
            $i = 0;
            
            foreach($daterange as $date){
                echo $date->format("d.m.Y") . "<br>";
                $i++;
            }
            
            echo "<br><br>";
            echo $i . " Tage ";
            
            1. Tach!

              passt das so besser?

              Sieht erstmal wie ein richtiger Weg aus. Aber du musst das i++ noch abhängig vom Wochentag machen. Soll ja nur am Wochenende zählen.

              dedlfix.

              1. Moin,

                hab es etwas umgebaut, was sagst du dazu?

                setlocale(LC_TIME, "ge","de_DE"); 
                
                $begin = new DateTime( '2018-01-01' );
                $end = new DateTime( '2018-01-18' );
                $end = $end->modify( '+1 day' ); 
                
                $diff = $begin->diff($end);
                
                $interval = new DateInterval('P1D');
                $daterange = new DatePeriod($begin, $interval ,$end);
                
                $i = 0;
                
                foreach($daterange as $date){
                
                    echo $date->format("d.m.Y") . "<br>";
                    
                    $zeit = strtotime($date->format("Y-m-d"));
                	$d = date("l", $zeit);
                	echo $d . "<br>";
                
                    if ($d != "Saturday" && $d != "Sunday" ) {
                    	$i++;
                    }
                }
                
                echo "<br><br>";
                

                die Ausgabe lautet

                01.01.2018
                Monday
                02.01.2018
                Tuesday
                03.01.2018
                Wednesday
                04.01.2018
                Thursday
                05.01.2018
                Friday
                06.01.2018
                Saturday
                07.01.2018
                Sunday
                08.01.2018
                Monday
                09.01.2018
                Tuesday
                10.01.2018
                Wednesday
                11.01.2018
                Thursday
                12.01.2018
                Friday
                13.01.2018
                Saturday
                14.01.2018
                Sunday
                15.01.2018
                Monday
                16.01.2018
                Tuesday
                17.01.2018
                Wednesday
                18.01.2018
                Thursday
                
                
                14 Tage
                
                1. Tach!

                  foreach($daterange as $date){
                  
                    $zeit = strtotime($date->format("Y-m-d"));
                  	$d = date("l", $zeit);
                  

                  Der (gekürzte) Teil ist etwas umständlich. Die format()-Methode liefert dir bereits den Wert, den date() für einen einfachen Timestamp liefert. $date->format("l") liefert also direkt den Wochentagsnamen. Ich würde aber nicht den nehmen, sondern die Nummer mit w (oder auch N, je nach Vorliebe), die ist dann auch unabhängig von locale-Einstellungen.

                  dedlfix.

          2. Hi,

            $startDate += 3600*24;

            Und an Schalttagen?

            gilt das auch.

            Kritisch sind nicht die Schalttage (29. Februar fast alle 4 Jahre), sondern die Tage mit Zeitumstellung oder mit Schaltsekunden.

            cu,
            Andreas a/k/a MudGuard

            1. Tach!

              Kritisch sind nicht die Schalttage (29. Februar fast alle 4 Jahre), sondern die Tage mit Zeitumstellung oder mit Schaltsekunden.

              Die meinte ich auch. Aber die Schaltsekunden spielen keine Rolle, die gibts nicht im Unix-Timestamp, mit dem ja PHP rechnet.

              dedlfix.

              1. Hallo,

                Schalttage und Schaltsekunden haben ja auch verschiedene Aufgaben und lösen unterschiedliche Probleme.

                Schalttage gleichen Ungenauigkeiten in der Definition der Jahreslänge, also im Mapping von Sonnentagen auf die jeweilige Jahresdefinition, aus. Das Wann und Wie ist kalenderspezifisch.

                Schaltsekunden gleichen Ungenauigkeiten in der Definition der Sekunde UND Rumpeln in der Erdumdrehung aus. Sie werden vom IERS nach Bedarf aufgerufen; zur Zeit ca alle 18 Monate. Ob das tatsächlich weltweit verwendet wird oder ob es Kulturen gibt, die hier ihre Spezialsuppe kochen, weiß ich nicht.

                Rolf

                --
                sumpsi - posui - clusi
                1. Hi,

                  Schaltsekunden […] Ob das tatsächlich weltweit verwendet wird oder ob es Kulturen gibt, die hier ihre Spezialsuppe kochen, weiß ich nicht.

                  Ob man die dann Kulturen nennen kann, weiß ich nicht.

                  Oder dachtest Du an eher an sowas wie Bakterienkulturen?

                  cu,
                  Andreas a/k/a MudGuard

            2. Hallo MudGuard,

              Kritisch sind […] die Tage […] mit Schaltsekunden.

              M.W. werden die nicht berücksichtigt, aber ich lass mich gern korrigieren.

              Bis demnächst
              Matthias

              --
              Rosen sind rot.