Derzeit online: ca. 101 User  
Spenden via PayPal PHP für dich ist auch als Buch erhältlich - im handlichen Format, zum Nachschlagen, Mitnehmen oder gemütlichen nebenbei Lesen. Mit noch mehr Informationen, zusätzlichen Beispielen und Übungsaufgaben.
Jetzt in der aktualisierten Version bei Amazon oder BoD bestellen!


Schnipsel

PHP - Ein kleiner Kalender mit date, strtotime, einer for-Schleife und ein paar ifs

Vor einigen Jahren habe ich das erste Mal einen Kalender mit PHP erstellt. Der Code war höchst umständlich und kompliziert, weil ich damit gekämpft habe, die erste bzw. letzte Woche richtig darzustellen. Um genau zu sein, war der Code so umständlich, dass ich ihn jahrelang nicht mehr anrühren wollte und den Kalender einfach jedes Mal kopiert habe, wenn ich einen benötigt habe. Jetzt habe ich mich aber doch damit beschäftigt und siehe da: So ein Kalender kann auch ganz einfach und simpel sein.

So soll er am Ende aussehen:
November 2017
Mo Di Mi Do Fr Sa So
30 31 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 1 2 3


Wir beginnen damit, dass wir mit setlocale die Zeit- und Datumsformatierung auf Deutsch setzen, um später deutsche Monatsnamen ausgeben zu können. Außerdem speichern wir das aktuelle Datum in der Variable kal_datum. Dadurch hätten wir später die Möglichkeit, den Kalender über andere Quellen mit einem Startdatum zu füttern, das wir dann einfach als kal_datum definieren würden.
  1. <?php
  2. setlocale(LC_TIME, "de_DE.utf8");
  3. $kal_datum = time();
Im nächsten Schritt berechnen wir einige Variablen. Dafür benötigen wir die Funktionen date und mktime. Für date kann es hilfreich sein, die Erklärung bei php.net aufzurufen, da dort alle Formate erklärt sind. Bei mktime beachte bitte die korrekte Reihenfolge der Angaben in Form von Stunde, Minute, Sekunde, Monat, Tag, Jahr. Außerdem musst du daran denken, dass mktime die Angaben ohne führende Null geliefert haben möchte.

Nun aber zu den Variablen:
  1. $kal_tage_gesamt = date("t", $kal_datum);
  2. $kal_start_timestamp = mktime(0,0,0,date("n",$kal_datum),1,date("Y",$kal_datum));
  3. $kal_start_tag = date("N", $kal_start_timestamp);
  4. $kal_ende_tag = date("N", mktime(0,0,0,date("n",$kal_datum),$kal_tage_gesamt,date("Y",$kal_datum)));
  5. ?>
  1. Die erste Variable kal_tage_gesamt beinhaltet die Kalendertage des ausgewählten Monats. Die Funktion date liefert zusammen mit der Formatangabe t direkt das gewünschte Ergebnis.
  2. Bei der zweiten Variable kal_start_timestamp wird es jetzt etwas komplizirter. Diese soll den Timestamp des ersten Tages des angezeigten Monats enthalten. Wir verwenden dafür mktime.
    • Die ersten drei übergebenen Werte legen die Uhrzeit fest und werden einfach auf 0 gesetzt.
    • Der vierte Wert übergibt den Monat unseres in kal_datum als Timestamp gespeicherten Datums. Dafür übergeben wir an dieser Stelle den Wert mit Hilfe der Funktion date("n",$kal_datum). Die Formatangabe n sorgt dafür, dass der Monat ohne führende Null angegeben wird, die Grundlage ist der ausgewählte Monat kal_datum.
    • Der fünfte Wert ist der gewünschte Tag. Da wir den ersten Tag des Monats haben wollen, geben wir einfach 1 an.
    • Der sechste wert wird wiederum über die Funktion date ermittelt. Dieses Mal fragen wir allerdings nicht den Monat ab, sondern das Jahr. Da es vierstellig übergeben werden soll, verwenden wir als Formatangabe Y.
  3. Die dritte Variable ist wiederum recht einfach: In kal_start_tag speichern wir den Wochentag, des ersten Tages unseres Monats, das heißt, ob unser Monat an einem Montag, Dienstag, Mittwoch etc. beginnt. PHP bietet dafür die Formatangabe N für die date-Funktion. Diese liefert Werte von 1 bis 7, wobei 1 Montag und 7 Sonntag entspricht. Wir müssen also nur date mit dem in kal_start_timestamp gespeicherten Timestamp des Monatsanfangs füttern.
  4. Unsere letzte Variable kal_ende_tag macht genau das gleiche wie die dritte Variable. Allerdings geben wir an dieser Stelle keine Variable als Quelle für date an, sondern berechnen den letzten Tag über mktime. Diese Berechnung funktioniert ähnlich wie die Berechnung des ersten Tages, mit dem Unterschied, dass als 5. Wert (also der für den Tag) nicht 1 übergeben wird, sondern die Variable kal_tage_gesamt, die ja die Anzahl der Tages unseres Monats enthält und damit zugleich den letzten Tag des Monats.
  5. In der letzten Zeile beenden wir dann vorübergehend PHP mit ?>, da wir als nächstes direkt HTML-Code verwenden.
Das ist jetzt vielleicht ein bisschen viel auf einmal, aber wenn du dir die Funktionen date und mktime genau ansiehst, wirst du sicher keine großen Probleme damit haben. Wenn du dich übrigens fragst, wieso wir bei der ersten Berechnung des Wochentages die Datumsvariable zuerst als Timestamp speichern und nicht wie bei der zweiten Berechnung direkt angeben: Das liegt daran, dass wir diesen Timestamp später noch einmal verwenden und ihn nicht jedes Mal extra berechnen wollten :-)

Im nächsten Schritt kümmern wir uns um das Grundgerüst unseres Kalenders. Wir verwenden dafür eine Tabelle und legen zunächst die Tabelle, eine Überschrift und den Tabellenkopf fest.
  1. <table class="kalender">
  2. <caption><?php echo strftime("%B %Y", $kal_datum); ?></caption>
  3. <thead>
  4. <tr>
  5. <th>Mo</th>
  6. <th>Di</th>
  7. <th>Mi</th>
  8. <th>Do</th>
  9. <th>Fr</th>
  10. <th>Sa</th>
  11. <th>So</th>
  12. </tr>
  13. </thead>
In Zeile 9 beginnt unsere Tabelle, der wir auch direkt die Klasse kalender zuweisen, um sie mittels CSS gestalten zu können. Alternativ kannst du Formatierungen natürlich auch direkt vornehmen. Danach folgt die Überschrift der Tabelle im caption-Tag. Die Überschrift selbst geben wir mit PHP aus. strftime sorgt dafür, dass die Ausgabe auf Deutsch erfolgt. In unsere Fall soll das der Monat und das Jahr sein und wir verwenden daher die Formatierungsoptionen %B %Y. Danach folgt der Tabellenkopf, mit 7 Spalten, die unsere Wochentage repräsentieren.

Im nächsten Schritt leiten wir den eigentlich Tabelleninhalt ein.
  1. <tbody>
Danach kommt endlich die Tagesanzeige unseres Kalenders. Das wird jetzt wieder ein ganzer Code-Brocken, aber ich erkläre ihn gleich Stück für Stück näher.
  1. <?php
  2. for($i = 1; $i <= $kal_tage_gesamt+($kal_start_tag-1)+(7-$kal_ende_tag); $i++)
  3. {
  4. $kal_anzeige_akt_tag = $i - $kal_start_tag;
  5. $kal_anzeige_heute_timestamp = strtotime($kal_anzeige_akt_tag." day", $kal_start_timestamp);
  6. $kal_anzeige_heute_tag = date("j", $kal_anzeige_heute_timestamp);
  7. if(date("N",$kal_anzeige_heute_timestamp) == 1)
  8. echo "<tr>\n";
  9. if(date("dmY", $kal_datum) == date("dmY", $kal_anzeige_heute_timestamp))
  10. echo " <td class=\"kal_aktueller_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  11. elseif($kal_anzeige_akt_tag >= 0 AND $kal_anzeige_akt_tag < $kal_tage_gesamt)
  12. echo " <td class=\"kal_standard_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  13. else
  14. echo " <td class=\"kal_vormonat_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  15. if(date("N",$kal_anzeige_heute_timestamp) == 7)
  16. echo " </tr>\n";
  17. }
  18. ?>
Zunächst beginnen wir wieder mit PHP-Code und leiten ihn daher mit dem üblichen <?php ein. Danach folgt eine for-Schleife:
  1. for($i = 1; $i <= $kal_tage_gesamt+($kal_start_tag-1)+(7-$kal_ende_tag); $i++)
Um alle Tage ausgeben zu können, verwenden wir zunächst eine ganz normale for-Schleife. Als Schleifen-Variable definieren wir das "klassische" i und weisen ihm den Wert 1 zu. Die Variable erhöhen wir später mit der letzten Anweisung innerhalb der Schleife nach jedem Durchlauf um eins. Das eigentlich Interessante ist aber die Bedingung der Schleife. Anstatt hier einen fixen Wert anzugeben, orientieren wir uns an den für unseren Kalender notwendigen Tagen.

Wir berechnen dafür die Anzahl der Tage, die ausgegeben werden sollen. Diese setzen sich zusammen aus den Tagen, die der Monat insgesamt hat - gespeichert in der Variable kal_tage_gesamt -, dazu addieren wir die Tage, die vor dem ersten Wochentag liegen und aus dem Vormonat stammen. Dafür verwenden wir den in der Variable kal_start_tag gespeicherten Wert und ziehen davon 1 ab. Um das deutlicher zu erklären: Wenn der erste Tag des Monats ein Mittwoch ist, hat die Variable kal_start_tag den Wert 3. Unsere Woche hat vor Mittwoch noch zwei Tage, nämlich Montag und Dienstag. Wenn wir also von der Variable kal_start_tag 1 abziehen kommen wir genau auf diesen Wert. Denke das ruhig mit ein paar anderen Wochentagen durch, dann verstehst du es sicher schnell.

Bisher haben wir also die gesamten Tage des Monats plus die Tage aus dem Vormonat. Jetzt fehlen zur Anzeige vollständiger Wochen aber noch die Tag, die erst in den nächsten Monat fallen. Diese müssen auch hinzu addiert werden. Auf die notwendige Anzahl kommen wir, indem wir von 7 (also den Tagen einer Woche) den Wert aus kal_ende_tag abziehen. Dieser Wert entspricht dem letzten Wochentag unseres Monats. Ist also der letzte Tag ein Sonntag, würde das Ergebnis 7 - 7 = 0 lauten, was auch absolut richtig ist: Wenn der letzte Tag ein Sonntag ist, müssen keine weiteren Tage angezeigt werden. Wäre der letzte Tag allerdings ein Dienstag, würde die Rechnung 7 - 2 = 5 lauten. Es würden also noch 5 weitere Tage angezeigt werden.

Jetzt kommen wir endlich zur eigentlichen Bedingung; diese lautet, dass die Variable i kleiner oder gleich dem eben errechneten Wert sein soll.

Alles klar soweit? Wenn nicht, ist das auch nicht schlimm, denn die Berechnung ist zwar nicht schwierig, aber man muss sie in Ruhe durchdenken, um sie zu verstehen. Lass dir also ruhig Zeit, die weiteren Erklärungen laufen nicht davon.

Wieder da? Fein. Dann machen wir jetzt mit dem Inhalt der Schleife weiter und legen zunächst ein paar Variablen an, die für jeden Schleifendurchlauf neu berechnet werden sollen.
  1. $kal_anzeige_akt_tag = $i - $kal_start_tag;
  2. $kal_anzeige_heute_timestamp = strtotime($kal_anzeige_akt_tag." day", $kal_start_timestamp);
  3. $kal_anzeige_heute_tag = date("j", $kal_anzeige_heute_timestamp);
  1. Die erste Variable, die ich kal_anzeige_akt_tag genannt habe, berechnet wie weit wir vom 1. des Monats entfernt sind. Im ersten Schleifendurchlauf zeigt die Variable i 1 an.
    Wenn unser erster Tag des Monats ein Montag wäre, hätten wir also die Rechnung 1 - 1 = 0, wir würden uns also am 1. des Monats befinden. Wäre der erste Tag des Monats aber z. B. ein Mittwoch, so würde die Rechnung 1 - 3 = -2 lauten. Wir wären also 2 Tage vor dem ersten.
    Mit jedem Schleifendurchlauf ändert sich das natürlich, da die Variable i immer um 1 erhöht wird. Das ist aber auch so beabsichtigt, da wir jedes Mal einen anderen Tag des Monats ausgeben wollen.
    Die Variable kal_anzeigte_akt_tag benötigen wir gleich für die zweite Berechnung.
  2. Als nächstes brauchen wir den Timestamp des angezeigten Tages. Bisher wissen wir nur, dass der angezeigte Tag sich eine bestimmte Anzahl von Tagen vom 1. des Monats entfernt befindet. Um daraus einen brauchbaren Timestamp zu machen nutzen wir die Funktion strtotime.
    Die Funktion bekommt zwei Werte übergeben. Der zweite Wert ist ein Timestamp, aufgrund dessen sich ein neues Datum berechnet. Was das für ein Datum sein soll, legt der erste Wert fest. Denkbar ist z. B. +1 day um den nächsten Tag angezeigt zu bekommen, oder -3 week um drei Wochen zurück zu gehen.
    Wir verwenden jetzt die eben berechnete Variable kal_anzeige_akt_tag und den Timestamp des ersten Tages des Monats, der in der Variable kal_start_timestamp gespeichert ist, um den Timestamp des aktuell anzuzeigenden Tages zu bekommen. Für den ersten Wert für strtotime setzen wir also mit $kal_anzeige_akt_tag." day" eine Angabe im Stil von "-1 day" oder "+3 day" zusammen. Wichtig ist der Punkt zwischen der Variable und der Textangabe, sowie das Leerzeichen vor dem Wort day. Der zweite Wert ist die eben erwähnte Variable kal_start_timestamp
  3. Die dritte Variable kal_anzeige_heute_tag liefert jetzt den eigentlichen Tageswert. Bisher wissen wir ja nur, dass wir z. B. 2 Tage vor dem 1. des Monats sind und was dieser Tag für einen Timestamp hat. Mit date("j") bekommen wir jetzt den Tag (ohne führende Null) geliefert. Befinden wir uns also im November und sind gerade einen Tag vor dem ersten, würde die Variable den Wert 31 (= 31. Oktober) bekommen.
Durch all diese Berechnungen kennen wir jetzt den genauen Tag des aktuellen Schleifendurchlaufes, nicht nur als Zahlenwert, sondern auch als Timestamp. Bevor wir aber tatsächlich unsere Tage anzeigen lassen können, müssen wir die Tabellenzeile beginnen.
  1. if(date("N",$kal_anzeige_heute_timestamp) == 1)
  2. echo " <tr>\n";?>
Hier hilft uns wieder die Formatierungsoption N der date-Funktion, die wir schon am Anfang verwendet haben, um den Wochentag des ersten und letzten Tages des Monats zu ermitteln. Wir überprüfen einfach mit einer weiteren if-Anweisung ob der Wochentag des anzuzeigenden Tages ein Montag ist (also den Wert 1 hat). Ist dies der Fall, so beginnen wir die Zeile mittels "<tr>". Die Leerzeichen und \n sind auch an dieser Stelle nur aus optischen Gründen für den HTML-Code und können auch weggelassen werden. Danach können wir endlich unsere Tage anzeigen lassen. Wir benötigen dafür drei if-Abfragen:
  1. if(date("dmY", $kal_datum) == date("dmY", $kal_anzeige_heute_timestamp))
  2. echo " <td class=\"kal_aktueller_tag\">",$kal_anzeige_heute_tag,"</td>\n";
Die erste if-Abfrage überprüft, ob das Kalenderdatum (in unserem Fall entspricht das dem aktuellen Datum), das in der Variable kal_datum gespeichert ist, dem Wert des in diesem Schleifendurchlauf angezeigten Tages entspricht. Da Timestamps nicht nur auf das Datum, sondern auch auf die Uhrzeit bezogen sind, verwenden wir zum Überprüfen die Werte Tag-Monat-Jahr, die wir mit Hilfe von date und der Formatierungsoption dmY bekommen. Das liefert also z. B. 22112017 als Ergebnis.

Wenn diese Werte identisch sind, entspricht der angezeigte Tag dem aktuellen Tag und wir geben nicht nur die Tabellenzelle und den anzuzeigenden Tag - über die Variable kal_anzeige_heute_tag - aus, sondern verwenden in der ausgegebenen Tabellenzelle auch noch die Klasse kal_aktueller_tag, um diesen Tag mittels CSS besonders hervor heben zu können. Die Leerzeichen vor dem <td> sind nur dafür da, den Code in HTML ordentlich eingerückt zu haben. Den gleichen Zweck erfüllt auch das \n am Ende.
  1. elseif($kal_anzeige_akt_tag >= 0 AND $kal_anzeige_akt_tag < $kal_tage_gesamt)
  2. echo " <td class=\"kal_standard_tag\">",$kal_anzeige_heute_tag,"</td>\n";
Danach folgt eine elseif-Anweisung. Ist der angezeigte Tag also nicht der aktuelle Tag wird diese Bedingung überprüft.

Hier geht es darum, heraus zu finden, ob der Tag, der gerade durch die Schleife angezeigt werden soll, ein Tag des aktuellen Monats ist, oder aus dem Vormonat stammt. Wir überprüfen also, ob kal_anzeige_akt_tag - also die Variable in der enthalten ist, wie weit wir vom 1. des Monats entfernt sind - größer oder gleich 0 ist und ob der selbe Wert kleiner als die Gesamtanzahl der Tage des Monats ist.

Am 1. des Monats enhält kal_anzeigte_akt_tag den Wert 0, das trifft also auf "<= 0" zu. Am letzten Tag entspricht der Wert dadurch allerdings nicht dem letzten Tag des Monats, sondern ist 1 kleiner, wodurch wir nur auf "kleiner" und nicht auf "kleiner gleich" überprüfen.

Wenn die Prüfung dieser elseif-Anweisung positiv verläuft, der anzuzeigende Tag also ein Tag aus dem aktuellen Monat ist, dann geben wir wiederum eine Tabellenzelle aus, die dieses Mal neben dem in kal_anzeige_heute_tag gespeicherten Tag auch noch die Klasse kal_standard_tag beinhaltet.
  1. else
  2. echo " <td class=\"kal_vormonat_tag\">",$kal_anzeige_heute_tag,"</td>\n";
Die dritte "Überprüfung" betrifft jetzt die Tage, die außerhalb des aktuellen Monats liegen. Da wir alle anderen Tage bereits abgefangen haben, müssen wir an dieser Stelle nichts mehr wirkliche überprüfen, sondern können einfach den else-Teil der if-Anweisung nutzen, um eine weitere Tabellenzelle auszugeben. Der Tabellenzelle verpassen wir die Klasse kal_vormonat_tag (obwohl sie auch Tage aus dem nächsten Monat beinhalten kann).

Wenn du die Tage aus nicht aktuellen Monaten nicht angezeigt bekommen möchtest, gibt du an dieser Stelle ebenfalls eine Tabellenzelle aus, allerdings nicht mit dem Wert aus der Variable kal_anzeige_heute_tag, sondern einfach mit einem leeren Feld, also einfach &nbsp; als Zelleninhalt.

Da die Schleife so lange ausgeführt wird, bis der letzte Sonntag des Monats bzw. der erste Sonntag des folgenden Monats angezeigt wird, müssen wir jetzt eigentlich nichts weiter tun. Oder zumindest fast. Bisher hat unser Kalender nämlich noch das Problem, das Tabellenzelle nach Tabellenzelle erzeugt wird, aber die Zeile kein Ende findet. Wir müssen also noch an der richtigen Stelle die Tabellenzeile beenden (der Anfang der einzelnen Zeile befindet sich am Anfang der Schleife).
  1. if(date("N",$kal_anzeige_heute_timestamp) == 7)
  2. echo " </tr>\n";?>
Auch hier hilft uns die Formatierungsoption N der date-Funktion. Dieses Mal überprüfen wir mit einer weiteren if-Anweisung ob der Wochentag des anzuzeigenden Tages ein Sonntag ist (also den Wert 7 hat). Ist dies der Fall, so schließen wir die Zeile mittels "</tr>".Die Leerzeichen und \n sind auch an dieser Stelle nur aus optischen Gründen für den HTML-Code und können auch weggelassen werden.

Danach können wir die Schleife und auch den PHP-Teil beenden, denn unsere Tage sind jetzt vollständig ausgegeben. Was noch fehlt ist das Beenden des Tabellen-Bodys und natürlich der Tabelle selbst.
  1. }
  2. ?>
  3. </tbody>
  4. </table>
Und schon ist unser Kalender fertig und funktionstüchtig und lässt sich außerdem mittels CSS ganz einfach formatieren. Hier noch einmal der vollständige Code:
  1. <?php
  2. setlocale(LC_TIME, "de_DE.utf8");
  3. $kal_datum = time();
  4. $kal_tage_gesamt = date("t", $kal_datum);
  5. $kal_start_timestamp = mktime(0,0,0,date("n",$kal_datum),1,date("Y",$kal_datum));
  6. $kal_start_tag = date("N", $kal_start_timestamp);
  7. $kal_ende_tag = date("N", mktime(0,0,0,date("n",$kal_datum),$kal_tage_gesamt,date("Y",$kal_datum)));
  8. ?>
  9. <table class="kalender" style="text-align: center; ">
  10. <caption><?php echo utf8_decode(strftime("%B %Y", $kal_datum)); ?></caption>
  11. <thead>
  12. <tr>
  13. <th>Mo</th>
  14. <th>Di</th>
  15. <th>Mi</th>
  16. <th>Do</th>
  17. <th>Fr</th>
  18. <th>Sa</th>
  19. <th>So</th>
  20. </tr>
  21. </thead>
  22. <tbody>
  23. <?php
  24. for($i = 1; $i <= $kal_tage_gesamt+($kal_start_tag-1)+(7-$kal_ende_tag); $i++)
  25. {
  26. $kal_anzeige_akt_tag = $i - $kal_start_tag;
  27. $kal_anzeige_heute_timestamp = strtotime($kal_anzeige_akt_tag." day", $kal_start_timestamp);
  28. $kal_anzeige_heute_tag = date("j", $kal_anzeige_heute_timestamp);
  29. if(date("N",$kal_anzeige_heute_timestamp) == 1)
  30. echo " <tr>\n";
  31. if(date("dmY", $kal_datum) == date("dmY", $kal_anzeige_heute_timestamp))
  32. echo " <td class=\"kal_aktueller_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  33. elseif($kal_anzeige_akt_tag >= 0 AND $kal_anzeige_akt_tag < $kal_tage_gesamt)
  34. echo " <td class=\"kal_standard_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  35. else
  36. echo " <td class=\"kal_vormonat_tag\">",$kal_anzeige_heute_tag,"</td>\n";
  37. if(date("N",$kal_anzeige_heute_timestamp) == 7)
  38. echo " </tr>\n";
  39. }
  40. ?>
  41. </tbody>
  42. </table>


Autor: Claudia Unkelbach
© 2001 - 2017 Claudia Unkelbach
Gießener Straße 75, 35396 Gießen