SELFHTML aktuell Artikel Programmiertechnik | |
Programmiertechnik:
| |
Antje Hofmann | |
E-Mail: | ah@pc-anfaenger.de |
---|---|
Homepage-URL: | http://pc-anfaenger.de/ |
Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!
Viele Gemeinschaften versammeln sich in regelmäßigen Abständen. Verhältnismäßig problemlos ist die Bestimmung eines solches Termins, wenn er z.B. alle vier Wochen montags stattfindet.
Nicht immer ist es aber gewünscht, dass der Versammlungstermin auf den gleichen Wochentag fällt,
da Überschneidungen die Teilnahme einiger Mitglieder unmöglich machen würden. Ideal ist es,
wenn der Wochentag des Versammlungstermins regelmäßig wechselt.
Hin und wieder ist dabei gewünscht,
dass ein Versammlungstermin nur auf einem Montag, Dienstag, Mittwoch oder Donnerstag
fällt. Die automatische Ausgabe solcher Termine ist die Aufgabe der folgenden Scripts.
Die gewünschten Termine sollen im Rhythmus von ungefähr vier Wochen (28 Tage) und jeweils an einem anderen Wochentag stattfinden. Das bedeutet, es sind nicht exakt 28 Tage die zwischen 2 Terminen vergehen. Sichergestellt werden soll aber auch, dass über mehrere Monate der Rhythmus eingehalten wird und der durchschnittliche Abstand 28 Tage beträgt.
Eine solche Verschiebung wird erreicht, indem man nicht 28 Tage sondern 27 oder 29 Tage zwischen zwei Terminen vergehen lässt und beim Erreichen des ersten ungültigen Tages den Unterschied (plus 4 Tage bzw. minus 4 Tage) ausgleicht. Der Unterschied entspricht jeweils der Anzahl der erlaubt Wochentage.
Die folgende Tabelle zeigt, welche Termine sich ergeben können.
Termine 27 Tage | Differenz | Termine 29 Tage | Differenz |
---|---|---|---|
Mi, 07.08.2002 Di, 03.09.2002 Mo, 30.09.2002 Do, 31.10.2002 Mi, 27.11.2002 |
27 27 31 27 |
Mi, 07.08.2002 Do, 05.09.2002 Mo, 30.09.2002 Di, 29.10.2002 Mi, 27.11.2002 |
29 25 29 29 |
Ausgehend von diesen Überlegungen ist es dann nur erforderlich, die jeweiligen Termine zu berechnen und alle ab dem aktuellen Tagesdatum auszugeben.
Die folgenden Beispiele verwenden jeweils die Standardmodule der einzelnen Programmiersprachen. Die logische Umsetzung ist in allen drei Sprachen die gleiche. Jedoch unterscheiden sie sich im Umgang mit den jeweiligen Datumsfunktionen erheblich voneinander.
In allen drei Beispielen werden die globalen Variablen starttag
, abstand
,
sprung
, anzahl
und heuteText
definiert.
Die Variable starttag
enthält einen beliebigen Tag in üblicher Notation, der als Starttag für die
Terminübersicht gilt. Dieser Tag muss nicht unbedingt ein gültiger Veranstaltungstag sein,
da in diesem Fall der nächste gültige Veranstaltungstag ermittelt wird. Nicht geprüft wird, ob es sich um ein
gültiges Datum handelt.
Die Variable abstand
enthält die Abstände zwischen
den einzelnen Terminen und die Variable sprung
enthält die Anzahl der Tage, um die an
einem ungültigen Tag weitergezählt wird.
Die Variable anzahl
legt fest, wie viele Termine ausgegeben werden und
in der Variablen heuteText
ist der Text gespeichert, der ausgegeben wird, wenn am aktuellen Tag ein Termin stattfindet.
Die Definition als globale Variable ist nicht unbedingt notwendig, da die Variablen
an die Funktion als Parameter übergeben werden. Sie wurden für die Beispiele
aus Übersichtgründen definiert.
Anzeigebeispiel: So sieht's aus
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Bowlingtermine</title> <script type="text/javascript"> <!-- gueltig fuer Browser ab Version 4, Internet Explorer ab Version 5 starttag = "29.04.2002"; abstand = 27; sprung = 4; anzahl = 10; heuteText = "<b>Heute 20.00 Uhr Bowling im Sportcenter.<\/b>"; function fuehrendeNull(wert) { if(wert < 10) return "0" + wert; else return wert; } function formatAusgabe(datum) { var woche = new Array('So','Mo','Di','Mi','Do','Fr','Sa') return woche[datum.getDay()] + ", " + fuehrendeNull(datum.getDate()) + "." + fuehrendeNull((datum.getMonth()+1)) + "." + datum.getFullYear(); } function erzeugeTermine(starttag,abstand,sprung,anzahl,heuteText) { starttag = starttag.split(".") var datum = new Date(starttag[2],parseInt(starttag[1],10)-1,parseInt(starttag[0],10)) var heute = new Date(); var termine = new Array(); while (termine.length < anzahl) { wochentag=datum.getDay(); if (wochentag > 4 || wochentag == 0) datum.setDate(datum.getDate() + sprung); if (heute.getDate() == datum.getDate() && heute.getMonth() == datum.getMonth() && heute.getFullYear() == datum.getFullYear()) termine[termine.length] = heuteText; else if (datum > heute) termine[termine.length] = formatAusgabe(datum); datum.setDate(datum.getDate() + abstand); } return "<tt>" + termine.join("<br>") + "<\/tt>"; } //--> </script> </head> <body> <tt>Unsere Bowlingtermine:</tt> <br><br> <script type="text/javascript"> <!-- document.write(erzeugeTermine(starttag,abstand,sprung,anzahl,heuteText)); //--> </script> </body> </html>
Innerhalb der Funktion erzeugeTermine()
wird der Starttag mittels der
Methode split()
in seine Bestandteile zerlegt. Anschließend wird aus diesen
Werten ein neues Datumsobjekt gebildet. Dieses steht als Eingangswert für die folgenden Datumsberechnungen
zur Verfügung.
Im weiteren Verlauf wird in der Variablen heute
das aktuelle Datum gespeichert und das Array termine
initialisiert.
Dieses Array dient dazu, die ermittelten Termine zu speichern.
In der folgenden while-Schleife wird mit der Methode getDay()
der für diesen Tag gültige Wochentag ermittelt und in der Variablen wochentag
abgelegt.
In JavaScript beginnt die Woche immer mit dem Sonntag, wobei die Zählung bei 0 beginnt. Deshalb
ist es ausreichend zu prüfen, ob der Wochentag größer als 4 oder gleich 0 ist.
Ist die Bedingung erfüllt, so handelt es sich um einen ungültigen Termin und zum aktuellen Datum
werden 4 Tage hinzuaddiert.
Verwendet wird dazu die JavaScriptmethode setDate()
, der als Parameter
den mittels der Methode getDate()
ermittelten Monatstag vermehrt um 4 (Inhalt der
Variable sprung
) übergeben wird. Die Methode setDate()
verarbeitet dabei
auch Angaben, die größer als die möglichen Monatstage sind und zählt intern einfach weiter.
Danach wird geprüft, ob das heutige Datum mit dem Wert in der Variablen datum
übereinstimmt. Tritt dieser
Fall ein, so findet am heutigen Tag eine Veranstaltung statt und im Array termine
wird der Inhalt der Variablen heuteText
abgelegt. Gilt diese Bedingung nicht,
so wird geprüft, ob das in der Variablen datum
gespeicherte Datum größer
als das heutige Datum ist. Tritt dieser Fall ein, so ist ein neuer gültiger
Termin gefunden und zum Array termine
wird ein neues Element hinzugefügt.
Am Ende der Schleife wird mittels setDate()
das in der Variablen datum
gespeicherte
Datum um 27 (Inhalt der Variable abstand
) Tage erhöht und ein neuer Durchlauf
mit dem geänderten Datumsobjekt beginnt.
Abgebrochen wird die Schleife genau dann, wenn die Anzahl der im Array termine
gespeicherten Elemente mit der festgelegten Anzahl übereinstimmt.
Die Formatierung der Termine wurde in die Funktion formatAusgabe()
ausgelagert.
Diese Funktion erhält als Parameter das Datumsobjekt übergeben. Innerhalb der Funktion ist
ein Array woche
definiert. In diesem Array sind die gewünschten Schreibweisen für
die Wochentage gespeichert. Der im Dateobjekt gespeicherte Wochentag entspricht dabei der Position
des Wochentages im Array. Die Funktion fuehrendeNull()
fügt
gegebenenfalls eine führende Null vor dem Datumswert an.
Am Ende der Funktion erzeugeTermine()
werden die Elemente des Arrays termine
mittels der Methode join()
in eine Zeichenkette umgewandelt, zurückgegeben und
mittels document.write()
ausgegeben.
<?php $starttag = "29.04.2002"; $abstand = 27; $sprung = 4; $anzahl = 10; $heuteText = "<b>Heute 20.00 Uhr Bowling im Sportcenter.</b>"; function formatausgabe($datumarray) { $woche = array('So','Mo','Di','Mi','Do','Fr','Sa'); return $woche[$datumarray["wday"]].", ".sprintf("%02d.%02d.%d",$datumarray["mday"],$datumarray["mon"],$datumarray["year"]); } function erzeugeTermine($starttag,$abstand,$sprung,$anzahl,$heuteText) { $starttag = explode(".",$starttag); $datum = mktime(0,0,0,$starttag[1],$starttag[0],$starttag[2]); $heute = time(); $heutearray = getdate($heute); $termine = array(); while (count($termine) < $anzahl) { $datumarray = getdate($datum); if ($datumarray["wday"] > 4 || $datumarray["wday"] == 0) { $datum = mktime(0,0,0,$datumarray["mon"],$datumarray["mday"]+$sprung,$datumarray["year"]); $datumarray = getdate($datum); } if ($datumarray["yday"] == $heutearray["yday"] && $datumarray["year"] == $heutearray["year"]) $termine[] = $heuteText; elseif ($datum > $heute) $termine[] = formatAusgabe($datumarray); $datum = mktime(0,0,0,$datumarray["mon"],$datumarray["mday"]+$abstand,$datumarray["year"]); } return "<tt>".join("<br>",$termine)."</tt>"; } ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Bowlingtermine</title> </head> <body> <tt>Unsere Bowlingtermine:</tt> <br><br> <?php echo erzeugeTermine($starttag,$abstand,$sprung,$anzahl,$heuteText); ?> </body> </html>
Innerhalb der Funktion erzeugeTermine()
wird der Starttag mittels der
Funktion explode()
in seine Bestandteile zerlegt. Anschließend wird aus diesen
Werten unter Verwendung der Funktion mktime()
der UNIX-Zeitstempel für dieses
Datum ermittelt. Dieser steht als Eingangswert für die folgenden Datumsberechnungen
zur Verfügung.
Im weiteren Verlauf wird in der Variablen $heute
der UNIX-Zeitstempel des aktuellen Datums gespeichert
und in der Variablen $heutearray
mittels der Funktion getDdate()
die Datums- und Zeitangaben als assoziatives Array gespeichert.
Das Array $termine
dient dazu, die ermittelten Termine zu speichern.
In der folgenden while-Schleife wird mit der Funktion getdate()
die Variable
$datumarray
erzeugt. Über dieses assoziatives Array ist es möglich,
Angaben wie den Wochentag des Datums zu ermitteln.
In PHP beginnt die Woche immer mit dem Sonntag, wobei die Zählung bei 0 beginnt. Deshalb
ist es ausreichend zu prüfen, ob der Wochentag ($datumarray["wday"]
)
größer als 4 oder gleich 0 ist.
Ist die Bedingung erfüllt, so handelt es sich um einen ungültigen Termin und zum aktuellen Datum
werden 4 Tage hinzuaddiert.
PHP korrigiert wie auch JavaScript fehlerhafte Datumsangaben. Wird ein ungültiger Tag an die
Funktion mktime()
übergeben, so wird das Datum korrigiert und automatisch in
das nächste gültige Datum verwandelt. Diese Eigenschaft wird auch in der Funktion erzeugeTermine()
angewendet und zum Monatstag ($datumarray["mday"]
) einfach die zusätzlichen Tage (Inhalt der
Variable $sprung
) addiert.
Danach wird geprüft, ob das heutige Datum mit dem Wert in der Variablen $datum
übereinstimmt. Tritt dieser
Fall ein, so findet am heutigen Tag eine Veranstaltung statt und im Array $termine
wird der Inhalt der Variablen $heuteText
abgelegt. Gilt diese Bedingung nicht,
so wird geprüft, ob das in der Variablen $datum
gespeicherte Datum größer
als das heutige Datum ist. Tritt dieser Fall ein, so ist ein neuer gültiger
Termin gefunden und zum Array $termine
wird ein neues Element hinzugefügt.
Am Ende der Schleife wird mittels mktime()
in der Variablen $datum
ein neues
um 27 (Inhalt der Variable $abstand
) Tage größeres Datum als UNIX-Zeitstempel gespeichert
und ein neuer Durchlauf mit dem geänderten Datum beginnt.
Abgebrochen wird die Schleife genau dann, wenn die Anzahl der im Array $termine
gespeicherten Elemente mit der festgelegten Anzahl übereinstimmt.
Die Formatierung der Termine wurde in die Funktion formatAusgabe()
ausgelagert.
Diese Funktion erhält als Parameter das assoziative Array mit den Datumswerten übergeben.
Innerhalb der Funktion ist ein Array $woche
definiert. In diesem Array sind die gewünschten Schreibweisen für
die Wochentage gespeichert. Der in $datumarray["wday"]
gespeicherte Wochentag entspricht dabei der Position
des Wochentages im Array. Die Formatierung des Datums erfolgt mittels der Funktion sprintf()
.
Die Angabe %d
formatiert den Wert des Argumentes als Integer-Wert. Die Angabe %02d
formatiert den übergebenen Wert als Integer-Wert und falls erforderlich mit führender Null.
Am Ende der Funktion erzeugeTermine()
werden die Elemente des Arrays $termine
mittels der Methode join()
in eine Zeichenkette umgewandelt, zurückgegeben und
mittels echo
ausgegeben.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); use Time::Local; my $starttag = "29.04.2002"; my $abstand = 27; my $sprung = 4; my $anzahl = 10; my $heuteText = "<b>Heute 20.00 Uhr Bowling im Sportcenter.</b>"; sub formatausgabe { my $mday = shift; my $mon = shift; my $year = shift; my $wday = shift; my @woche = qw(So Mo Di Mi Do Fr Sa); return $woche[$wday].", ".sprintf("%02d.%02d.%d",$mday,($mon+1),($year+1900)); } sub erzeugeTermine { my $starttag = shift; my $abstand = shift; my $sprung = shift; my $anzahl = shift; my $heuteText = shift; $abstand = $abstand*60*60*24; $sprung = $sprung*60*60*24; my @starttag = split(/\./,$starttag); my $datum = timelocal(0,0,0,$starttag[0],$starttag[1]-1,$starttag[2]); my ($mday, $mon, $year, $wday, $yday) = (localtime($datum))[3,4,5,6,7]; my $heute = time(); my ($heute_year,$heute_yday ) = (localtime($heute))[5,7]; my @termine; while (@termine < $anzahl) { if ($wday > 4 || $wday == 0) { $datum += $sprung; ($mday, $mon, $year, $wday, $yday) = (localtime($datum))[3,4,5,6,7]; } if ($year == $heute_year && $heute_yday == $yday) {push(@termine,$heuteText);} else { if ($datum > $heute) {push(@termine,formatausgabe($mday,$mon,$year,$wday));} } $datum += $abstand; ($mday, $mon, $year, $wday, $yday) = (localtime($datum))[3,4,5,6,7]; } return "<tt>".join("<br>",@termine)."</tt>"; } print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Bowlingtermine</title></head><body>\n"; print "<tt>Unsere Bowlingtermine:</tt><br><br>\n"; print erzeugeTermine($starttag,$abstand,$sprung,$anzahl,$heuteText); print "</body></html>\n";
Perl korrigiert im Gegensatz zu PHP keine fehlerhaften Datumsangaben. Für diese Sprache
ist es deshalb notwendig, direkt mit dem UNIX-Zeitstempel zu rechnen. Der UNIX-Zeitstempel
enthält die Anzahl der Sekunden, die seit dem 1.1.1970, 0.00 Uhr bis zum Aufrufsaugenblick vergangen
sind. Da es notwendig ist, mit Sekundenwerten zu arbeiten, wird am Anfang der Subroutine
erzeugeTermine()
die Werte in den Variablen $abstand
und $sprung
in Sekunden umgerechnet.
Innerhalb der Subroutine erzeugeTermine()
wird der Starttag mittels der
Funktion split()
in seine Bestandteile zerlegt. Anschließend wird aus diesen
Werten unter Verwendung der Funktion timelocal()
der UNIX-Zeitstempel für dieses
Datum ermittelt. Dieser steht als Eingangswert für die folgenden Datumsberechnungen
zur Verfügung. Anschließend werden mittels der Funktion localtime()
die Variablen
Monatstag $mday
, Monat $mon
, Jahr $year
, Wochentag
$wday
und Tag des Jahres $yday
mit Werten belegt.
Im weiteren Verlauf wird in der Variablen $heute
der UNIX-Zeitstempel des aktuellen Datums gespeichert
und für den heutigen Tag die Elemente Tag des Jahres $heute_yday
und Jahr $heute_year
mittels localtime()
ermittelt.
Das Array @termine
dient dazu, die ermittelten Termine zu speichern.
In der folgenden while-Schleife wird zuerst geprüft, ob der Termin auf einen ungültigen Wochentag
fällt. In Perl beginnt die Woche immer mit dem Sonntag, wobei die Zählung bei 0 beginnt. Deshalb
ist es ausreichend zu prüfen, ob der Wochentag $wday
größer als 4 oder gleich 0 ist.
Ist die Bedingung erfüllt, so handelt es sich um einen ungültigen Termin und zum aktuellen Zeitstempel
werden 4 Tage hinzuaddiert. Anschließend wird Liste der Datumsangaben aktualisiert.
Danach wird geprüft, ob das heutige Datum mit dem Wert in der Variablen $datum
übereinstimmt. Tritt dieser
Fall ein, so findet am heutigen Tag eine Veranstaltung statt und im Array @termine
wird der Inhalt der Variablen $heuteText
abgelegt. Gilt diese Bedingung nicht,
so wird geprüft, ob das in der Variablen $datum
gespeicherte Datum größer
als das heutige Datum ist. Tritt dieser Fall ein, so ist ein neuer gültiger
Termin gefunden und zum Array @termine
wird ein neues Element hinzugefügt.
Am Ende der Schleife wird zur Variablen $datum
der Inhalt der Variablen abstand
addiert
und die Liste mit der Datumsangaben wiederum aktualisiert. Anschließend
beginnt ein neuer Durchlauf mit dem geänderten Zeitstempel.
Abgebrochen wird die Schleife genau dann, wenn die Anzahl der im Array @termine
gespeicherten Elemente mit der festgelegten Anzahl übereinstimmt.
Die Formatierung der Termine wurde in die Subroutine formatAusgabe()
ausgelagert.
Diese Subroutine erhält als Parameter den Tag, den Monat, das Jahr und den Wochentag übergeben.
Innerhalb dieser Subroutine ist ein Array @woche
definiert. In diesem Array sind die gewünschten Schreibweisen für
die Wochentage gespeichert. Der in $wday
gespeicherte Wochentag entspricht dabei der Position
des Wochentages im Array. Die Formatierung des Datums erfolgt mittels der Funktion sprintf()
.
Die Angabe %d
formatiert den Wert des Argumentes als Integer-Wert. Die Angabe %02d
formatiert den übergebenen Wert als Integer-Wert und falls erforderlich mit führender Null.
Am Ende der Subroutine erzeugeTermine()
werden die Elemente des Arrays @termine
mittels der Methode join()
in eine Zeichenkette umgewandelt, zurückgegeben und
mittels print
ausgegeben.
<script type="text/javascript"> <!-- document.write("<tt>Unsere Bowlingtermine:<\/tt><br><br>"); document.write(erzeugeTermine(starttag,abstand,sprung,anzahl,heuteText)); //--> </script> <noscript> <iframe src="termin.php"> <a href="termin.php">Unsere Bowlingtermine</a> </iframe> </noscript>
Es ist sehr oft sinnvoll eine Reihe von Aufgaben an den Clientrechner abzugeben. Auch Terminkalender wie im Beispiel müssen nicht in jedem Fall serverseitig generiert werden. Das Beispiel zeigt, wie auf einfache Art und Weise JavaScript und serverseitige Anwendung gekoppelt werden können.
Hat ein Besucher JavaScript eingeschaltet, so wird der Terminkalender mittels JavaScript generiert. Ist aus irgendeinem Grunde JavaScript deaktiviert, so wirkt der Noscript-Bereich. Innerhalb dieses Bereiches ist ein Iframe definiert, welche das serverseitige Script lädt. Browser, die keine Iframes kennen, erhalten stattdessen einen Verweis zur entsprechenden Seite.
Die folgenden Stellen werden empfohlen, um die obigen Beispiele besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.
SELFHTML: Date-Objekt
SELFHTML: Stringzerlegung mit split()
SELFHTML: Array als Zeichenkette zurückgeben
PHP-Manuel: getdate()
PHP-Manuel: mktime()
PHP-Manuel: Stringzerlegung mit explode()
PHP-Manuel: join()
SELFHTML: Listen bzw. Arrays (Variablen) in Perl
SELFHTML: Funktionen für Datum und Uhrzeit in Perl
SELFHTML: Zeichenkette in mehrere Zeichenketten aufsplitten
SELFHTML: Liste in Zeichenkette verwandeln