4.3 Date_Holidays 

Besprochene Version: 0.11.0 | Lizenz: PHP-Lizenz |
Klassendatei(en): Date/Holidays.php |
Die Klasse Date_Holidays gibt Ihnen eine einfache Möglichkeit, das Datum von Feiertagen zu bestimmen. Dabei ist anzumerken, dass die Klasse nicht nur Feiertage, sondern auch die Daten von etwas ungewöhnlichen Terminen wie dem Ende des Zweiten Weltkriegs, dem internationalen Tag der Linkshänder oder dem Tag der Organspende kennt. Die Methoden dieser Klasse liefern im Fehlerfall alle ein PEAR_Error-Objekt zurück, so dass ich bei den einzelnen Methoden nicht jeweils darauf eingehen werde.
Vielleicht erstaunt es Sie ein wenig, aber die meisten christlichen, beweglichen Feiertage orientieren sich an Ostern. Ostern liegt jeweils auf dem Sonntag nach dem ersten Vollmond nach Frühlingsanfang, bezogen auf die nördliche Erdhalbkugel. Dieses Datum lässt sich aber glücklicherweise mit der Gauß’schen [Die Gauß’sche Formel ist nur für die Jahre von 1583 bis 8202 gültig, was aber ausreichend sein sollte. ] Formel berechnen, auf der auch diese Klasse basiert. Da die Klasse leider keine Information darüber liefert, welche Tage echte Feiertage sind und in welchen deutschen Bundesländern diese gültig sind, habe ich diese Information in Tabelle 4.8 ergänzt.
Weitere hilfreiche Daten, bei denen es sich aber nicht um Feiertage handelt, finden Sie in Tabelle 4.9.
Die Klasse wird in der Datei Date/Holidays.php definiert. Die Klasse arbeitet nicht mit einem klassischen Konstruktor, sondern mit einer Factory-Methode. Diese erwartet von Ihnen die Information, für welches Land bzw. welchen Kulturkreis, für welches Jahr und in welcher Sprache die Feiertage standardmäßig beschrieben werden sollen. Sollte ein Fehler auftreten, liefert die Methode ein PEAR_Error-Objekt zurück.
require_once "Date/Holidays.php"; // Neues Objekt instanziieren, deutsche Feiertage fuer 2005 $feiertage = Date_Holidays::factory('Germany', 2005, 'en_EN'); // Fehler aufgetreten? if (true===PEAR::isError($feiertage)) { die ($feiertage->getMessage()); } // Datum des Ostersonntags auslesen $ostern = $feiertage->getHoliday('easter'); if (true===PEAR::isError($ostern)) { die ($ostern->getMessage()); } print_r($ostern->toArray());
Listing 4.7 Auslesen des Datums von Ostersonntag
Der erste Parameter, den die Factory-Methode übergeben bekommt, ist der Treiber-Name. Hierbei handelt es sich um einen Landesnamen oder Ähnliches. Zurzeit sind leider nur die in Tabelle 4.10 aufgeführten Treiber implementiert.
Da die Klasse natürlich immer weiterentwickelt wird, kann es sein, dass inzwischen noch weitere Treiber erstellt wurden. Möchten Sie wissen, welche Treiber in Ihrer Installation vorhanden sind, können Sie in dem Verzeichnis, in dem PEAR installiert ist, das Unterverzeichnis Date/Holidays/Driver öffnen. Hier finden Sie verschiedene PHP-Dateien, die jeweils einem Treiber entsprechen.
Der zweite Parameter der Factory-Methode ist die Jahreszahl, für die die Feiertage berechnet werden sollen. Mit dem dritten Parameter können Sie festlegen, in welcher Sprache die Namen der Feiertage zurückgeliefert werden. Standardmäßig werden die Namen immer auf Englisch zurückgegeben. Möchten Sie die Ausgabe lokalisieren, können Sie das relativ einfach realisieren, wie Sie später noch lesen werden. In den meisten Fällen ist die englische Ausgabe allerdings nicht weiter dramatisch, wie ich denke, da Sie ja wissen, dass Sie das Datum für den Ostersonntag auslesen, wenn Sie die Methode getHoliday() aufrufen. Diese erwartet nämlich den »Internal Name« des Tages, den Sie auslesen wollen, als Parameter. Die wichtigsten internen Namen finden Sie in Tabelle 4.8 und Tabelle 4.9. Die Methode liefert ein Date_Holiday-Objekt zurück, das mithilfe der Methode toArray() in ein Array konvertiert werden kann, wie es in Listing 4.7 gemacht wurde. Das Array hat folgenden Aufbau:
Array ( [internalName] => easter [title] => Easter Sunday [date] => Date Object ( [year] => 2005 [month] => 03 [day] => 27 [hour] => 0 [minute] => 0 [second] => 0 [partsecond] => 0 [tz] => Date_TimeZone Object ( [id] => UTC [longname] => Coordinated Universal Time [shortname] => UTC [hasdst] => [dstlongname] => Coordinated Universal Time [dstshortname] => UTC [offset] => 0 [default] => ) ) )
Wie Sie sehen, enthält der Hash in der ersten Ebene drei Schlüssel. Der erste Wert ist der interne Name, der zweite die eigentliche Bezeichnung des Feiertags, und hinter dem dritten Schlüssel verbirgt sich ein Objekt der Klasse PEAR::Date, das alle relevanten Informationen enthält und auch mit den Methoden der Klasse verarbeitet werden kann.
Zum Auslesen der Informationen stehen auch noch spezialisierte Funktionen zur Verfügung. So können Sie die Bezeichnung eines Feiertags mit getHolidayTitle() auslesen, und das dazugehörige Date-Objekt können Sie durch Aufruf der Methode getHolidayDate() erhalten. Beide Mehoden bekommen den internen Namen übergeben, und die erste akzeptiert zusätzlich auch noch die Möglichkeit, eine Lokalisierung anzugeben.
Da Sie unter Umständen nicht alle internen Feiertagsbezeichner kennen oder vielleicht alle Feiertage auf einmal auslesen wollen, sind auch für diese Fälle Methoden vorgesehen, die Ihnen helfen. Um alle Informationen auszulesen, die ein Treiber kennt, können Sie auf die Methode getHolidays() zurückgreifen. Sie gibt Ihnen ein assoziatives Array zurück, in dem alle Informationen enthalten sind. Die internen Namen der Feiertage agieren hierbei als Schlüssel des Arrays. Die Werte bestehen jeweils aus einzelnen Date_Holiday-Objekten.
Hilfreich kann auch die Methode getInternalHolidayNames() sein, die Ihnen ein Array mit allen internen Namen zur Verfügung stellt, die in den Treibern definiert sind.
4.3.1 Lokalisierung 

Wie schon erwähnt, können Sie die Namen der Feiertage auch an bestimmte Sprachen anpassen, wenn das erforderlich sein sollte. Hierzu können Sie entweder auf bestehende Sprachdateien zurückgreifen oder selbst welche erstellen. Sollten die Sprachdateien nicht verfügbar sein oder ist die gewünschte Sprache nicht enthalten, müssen Sie selbst eine Lokalisierungsdatei anlegen. Diese ist im Stil einer ini-Datei aufgebaut. Für jede Sprache, die Sie zusätzlich nutzen wollen, benötigen Sie eine eigene separate Datei, wobei Sie mehrere Sprachen parallel nutzen können.
Bei diesen Dateien handelt es sich um normale Text-Dateien, bei denen der interne Name, der übersetzt werden soll, jeweils am Anfang einer neuen Zeile steht. Ihm folgen ein Gleichheitszeichen und der Name des Feiertags in der gewünschten Landessprache. Jede Zeile wird dann mit einem Semikolon abgeschlossen. Folgendes Beispiel zeigt die Nutzung:
Datei lang_de.ini:
easter = Ostersonntag; xmasEve = Heiligabend;
easter = dimanche de Pâques; xmasEve = veille de Noël;
Natürlich könnten in den beiden Sprachdateien noch weitere Zeilen folgen, und die Dateien müssten auch nicht die Endung .ini haben. Die Dateien werden folgendermaßen eingebunden:
require_once "Date/Holidays.php"; // Neues Objekt instanziieren, deutsche Feiertage fuer 2005 $feiertage = Date_Holidays::factory('Germany', 2005, 'de_DE'); // Translation Files hinzufuegen $feiertage->addTranslationFile("de.ini","de_DE"); $feiertage->addTranslationFile("fr.ini","fr_FR"); $ostern_de=$feiertage->getHolidayTitle("easter","de_DE"); $ostern_fr=$feiertage->getHolidayTitle("easter","fr_FR"); echo ("$ostern_de <br> $ostern_fr"); /* Ausgabe: Ostersonntag dimanche de Pâques */
Jede neue Sprache muss mit der Methode addTranslationFile() beim System angemeldet werden. Hierbei ist neben dem Dateinamen auch anzugeben, für welchen Sprachraum die Datei zuständig ist. Bei der Angabe des Sprachraums ist es üblich, Angaben wie de_DE zu nutzen, die sich aus den ISO-Codes zusammensetzen. Das de steht hierbei für die deutsche Sprache und das DE für den Sprachraum Deutschland. de_AT würde dann z. B. für Österreich stehen und en_US für die USA. Allerdings sind Sie nicht an diese Codes gebunden, sondern können hier auch eigene Kreationen einbinden, solange Sie diese in Ihrem Code konsistent nutzen.
Auf demselben Weg können Sie auch die bereits erwähnten, fertig definierten Sprachdateien einbinden. Diese befinden sich unterhalb des PEAR-Verzeichnisses im Ordner data/Date_Holidays/lang/. In diesem Ordner befinden sich dann weitere Ordner, die schließlich die Sprachdateien enthalten. Die Sprachdateien für die christlichen Feiertage sind im Ordner Christian zu finden, die für die deutschen Feiertage in German, und die Lokalisierung der UNO-Jahrestage ist im Ordner UNO platziert. Momentan sind Dateien für Deutsch (de_DE.ini), Französisch (fr_FR.ini) und »englisches Englisch« (en_EN.ini) vorhanden. Um also die bestehende deutsche Lokalisierung einzubinden, könnte der Befehl so lauten:
$feiertage->addTranslationFile( "/usr/local/pear/data/Date_Holidays/lang/Christian/de_DE.ini", "de_DE");
Benötigen Sie eine andere Sprache, sollten Sie – bevor Sie anfangen, selbst eine Lokalisierung zu erstellen – prüfen, ob die benötigte Sprache schon implementiert wurde.
4.3.2 Eigene Feiertage hinzufügen 

Um die Klasse möglichst flexibel zu halten, können Sie eigene Treiber einbinden. Das eröffnet Ihnen nicht nur die Möglichkeit, Feiertage für weitere Länder einzubinden, sondern z. B. auch Geburtstage oder andere Jahrestage.
Hierzu müssen Sie eine eigene Treiberdatei erstellen. Diese kann entweder alle relevanten Daten selbst generieren oder auf eine bestehende Treiberdatei aufsetzen und die dort generierten Daten als Basis nutzen. Soll sie alle Daten selbst generieren, nutzen Sie die Klasse Date_Holidays_Driver als Basis-Klasse und erweitern diese. Möchten Sie aber die Feiertage eines bestimmten Landes oder Kulturkreises mit in Ihrer Liste der Feiertage haben, müssen Sie die zutreffende Treiber-Datei in Ihre neue Treiber-Datei einbinden und die darin definierte Klasse erweitern. Im folgenden Beispiel werden die deutschen Feiertage als Grundlage genommen und um einen Geburtstag und den Tag für die Jahresend-Inventur ergänzt. Der Geburtstag fällt auf ein fixes Datum und ist daher unproblematisch. Die Inventur wird in diesem Beispiel allerdings auf den ersten Freitag im Januar gelegt. Hierbei ist zu beachten, dass das Datum variabel ist und dass der erste Januar eines jeden Jahres ein Feiertag ist. Somit kann es passieren, dass die Inventur erst am zweiten Freitag im Februar durchgeführt werden kann, wenn der erste ein Feiertag ist.
// Einbinden der Klassendatei require_once("Date/Holidays/Driver/Germany.php"); // Erweitern der Basis-Klasse class Date_Holidays_Driver_myHolidays extends Date_Holidays_Driver_Germany { // Die Methode _buildHolidays() generiert die Feiertage function _buildHolidays() { // Feiertage der Superklasse konstruieren parent::_buildHolidays(); // Eigene Feiertage hinzufuegen // Eigenen Geburtstag (statischer Feiertag) hinzufuegen $this->_addHoliday('birthday', $this->_year . '-02–05', 'My Birthday'); // Inventur findet immer am ersten Freitag im Januar statt $inventur = $this->_firstFriday(); $this->_addHoliday('stocktaking', $inventur, 'Anual Stocktaking'); // Ist ein Fehler aufgetreten? if (Date_Holidays::errorsOccurred()) { return Date_Holidays::getErrorStack(); } return true; } // Hilfsfunktion zum Berechnen des ersten Freitags function _firstFriday() { $tag = new Date($this->_year . '-01–01'); while (5 != $tag->getDayOfWeek() || 1 === $tag->getDay()) { $tag = $tag->getNextDay(); } return $tag; } }
Listing 4.8 Klasse für eigene Feiertage
Der Name der Klassendatei hängt zwingend von Namen der Klasse ab. In Listing 4.8 heißt die Klasse Date_Holidays_Driver_myHolidays, somit muss der Name der Klassendatei myHolidays.php lauten, und die Datei muss im selben Verzeichnis liegen wie die anderen Treiberdateien. Üblicherweise handelt es sich hierbei um das Verzeichnis Date/Holidays/Driver unterhalb des PEAR-Heimat-Verzeichnisses.
Der Name der Klasse muss zwingend mit Date_Holidays_Driver_ beginnen, und die Klasse benötigt keinen Konstruktor. Allerdings muss eine Methode namens _buildHolidays() vorhanden sein. Diese wird von der Factory-Methode des Pakets aufgerufen, um die Feiertage generieren zu lassen. In diesem Beispiel ist die Klasse eine Erweiterung der Basis-Klasse Date_Holidays_Driver_Germany. Daher sollen natürlich auch die deutschen Feiertage berechnet werden, was die Zeile parent::_buildHolidays(); gewährleistet. Diese Zeile entfällt natürlich, wenn Ihre Klasse alle Tage selbst berechnen soll.
Danach können die gewünschten Feiertage hinzugefügt werden. Die Methode _addHoliday(), die dafür zuständig ist, bekommt drei Parameter übergeben. Der erste ist der »Internal Name«, auf den das eigentliche Datum folgt, also z. B. '2005–01–01', und zu guter Letzt wird der ausgeschriebene Name des Feiertags angegeben. Vielleicht sind Sie darüber gestolpert, dass ich die Namen der Feiertage in dem Beispiel auf Englisch angegeben habe. Das ist nötig, um das System konsistent zu halten, da die anderen Tage vom System auch auf Englisch verwaltet werden. Möchten Sie also eine deutsche Übersetzung der entsprechenden Texte erhalten, müssen Sie die genutzte Sprachdatei entsprechend erweitern.
Wichtig ist noch zu erwähnen, dass das Paket auf PEAR::Date basiert, so dass alle dort enthaltenen Methoden und Funktionen herangezogen werden können, um ein Datum zu berechnen.