Perl-Skript prüft Rechentrick
Datumsarithmetik
von Michael Schilli
Erschienen im Linux-Magazin
2007/12
Gehirnakrobaten rechnen gerne vor Publikum, auf welchen Wochentag ein zugerufenes Datum fällt. Den Trick kann auch Otto Normalrechner mit etwas Übung lernen. Ob er zuverlässig funktioniert, prüft das im Snapshot vorgestellte Skript mit aktueller Perl-Technologie.
Neulich las ich auf dem Weg zur Arbeit im Zug das Buch "Mind Performance Hacks" [2] und stieß auf den Hack Nummer 43, der zeigt, wie jedermann mit etwas Übung den Wochentag zu jedem beliebigen Datum im Kopf ausrechnen kann. Das Verfahren geht zurück auf Lewis Carroll, den Autor des Romans "Alice im Wunderland".
Vier Werte sind der Reihe nach zu berechnen: der Jahreswert, der Monatswert, der Tageswert und ein vierter Wert zur Anpassung. Man addiert diese Werte anschließend, bestimmt den Restwert nach einer Division durch 7 und erhält verblüffenderweise den gewünschten Wochentag als Zahl zwischen 0 (Sonntag) und 6 (Samstag).
Als Beispiel dient der Entstehungstag dieses Textes (4.10.2007). Der Jahreswert bestimmt sich aus der folgenden Formel:
(YY + (YY div 4)) mod 7
Dabei ist »YY« die zweistellige Jahreszahl, also 07 für 2007. Der »div«-Operator führt eine Division ohne Restwert aus, 7 div 4 gleich 1, da 7 geteilt durch 4 den Wert 1 ergibt und der Rest 3 wegfällt. Das Ergebnis wird jetzt noch modulo 7 genommen: 1 modulo 7 ergibt 1. Der Jahreswert ist also 1.
Durch die Jahrhunderte
Zum Monatswert: Der ermittelt sich aus Tabelle 1, die auch Nicht-Gehirnakrobaten mit Hilfe einiger später zu erläuternder mnemotechnischer Tricks im Kopf behalten können. Der Oktober hat laut Tabelle den Monatswert 0. Der Tageswert ist einfach der fortlaufend nummerierte Tag des Monats, für den 4. Oktober also der Wert 4.
|
Wert
|
Monat
|
|
0
|
Januar
|
|
3
|
Februar
|
|
3
|
März
|
|
6
|
April
|
|
1
|
Mai
|
|
4
|
Juni
|
|
6
|
Juli
|
|
2
|
August
|
|
5
|
September
|
|
0
|
Oktober
|
|
3
|
November
|
|
5
|
Dezember
|
Der vierte Wert für die Kopfrechnung ergibt sich aus Tabelle 2, die für Jahreszahlen im 21. Jahrhundert (2000 bis 2099) den Wert 6 angibt. Diese Tabelle braucht man sich nicht ganz zu merken, es genügt, die Werte für 2000 (also 6) und 1900 (0) im Kopf zu behalten. In Schaltjahren wäre vom gefundenen Wert noch 1 abzuziehen, wenn das gesuchte Datum im Januar oder Februar liegt. Da aber 2007 kein Schaltjahr ist, entfällt das hier glücklicherweise.
|
Wert
|
Jahr
|
|
1700
|
4
|
|
1800
|
2
|
|
1900
|
0
|
|
2000
|
6
|
|
2100
|
4
|
|
2200
|
2
|
|
2300
|
0
|
Die vier gefundenen Werte sind also 1 (Jahreswert), 0 (Monatswert), 4 (Tageswert) und 6 (Anpassung). Die Summe ist 11, und 11 modulo 7 ergibt den Wert 4. Ein Blick in Tabelle 3 mit den Wochentagen offenbart, dass - Trommelwirbel - der 4.10.2007 auf einen Donnerstag fällt, und das ist tatsächlich richtig!
|
Wert
|
Wochentag
|
|
0
|
Sonntag
|
|
1
|
Montag
|
|
2
|
Dienstag
|
|
3
|
Mittwoch
|
|
4
|
Donnerstag
|
|
5
|
Freitag
|
|
6
|
Samstag
|
Probe aufs Exempel
Jetzt stellt sich natürlich die Frage, ob diese Berechnung auch tatsächlich für jedes beliebige Datum zum richtigen Wochentag führt. Das Skript in Listing 1 schraubt sich daher zur Probe vom 1.1.1700 an durch sämtliche Tage der Neuzeit, über die Entdeckung Amerikas und den Goldrausch, durch den ersten und den zweiten Weltkrieg bis in die heutige Zeit und auch noch weiter in die Zukunft, bis zum Raumschiff Enterprise in der nächsten Generation mit Jean-Luc Picard auf dem Kommandosessel im 24. Jahrhundert.
01 #!/usr/bin/perl
02 use strict;
03 use warnings;
04 use Test::More qw(no_plan);
05 use DateTime;
06
07 my @MONTH = qw(0 3 3 6 1 4 6 2 5 0 3 5);
08 my %ADJ = qw(1700 4 1800 2 1900 0 2000 6
09 2100 4 2200 2 2300 0);
10
11 my $dt = DateTime->new(
12 year => 1700,
13 month => 1,
14 day => 1,
15 );
16
17 while(1) {
18 my $calc = wday_mindcal(
19 $dt->year, $dt->month, $dt->day);
20
21 is($calc, $dt->wday() % 7, "$dt");
22
23 $dt->add(days => 1);
24
25 last if $dt->year() > 2399;
26 }
27
28 ###########################################
29 sub wday_mindcal {
30 ###########################################
31 my($year, $month, $day) = @_;
32
33 use integer;
34
35 my $year2 = $year % 100;
36 my $cent = $year / 100;
37 my $y = ($year2 + ($year2 / 4)) % 7;
38
39 my $m = $MONTH[$month-1];
40 my $d = $day;
41
42 my $adj = $ADJ{$cent * 100};
43
44 $adj-- if leap_year($year) and
45 $month <= 2;
46
47 return( ($y+$m+$d+$adj) % 7 );
48 }
49
50 ###########################################
51 sub leap_year {
52 ###########################################
53 my($year) = @_;
54
55 return 0 if $year % 4;
56 return 1 if $year % 100;
57 return 0 if $year % 400;
58 return 1;
59 }
|
Das Skript definiert in Zeile 29 die Funktion »wday_mindcal()«, die das vierstellige Jahr, den Monat und den Tag eines Datums entgegennimmt, nach den oben erläuterten Regeln die Jahres- beziehungsweise Monats- und Tages-Anpassungszahlen errechnet und die notwendigen Operationen ausführt, um daraus die Wochentagsnummer zu ermitteln.
Die Monatszahlen stehen im Array »@MONTH«, von Januar bis Dezember. Da Arrays nicht von 1, sondern von 0 an durchnummeriert sind, zieht das Skript vom gesuchten Monat erst 1 ab, bevor es unter dem so erhaltenen Index in den Array hineingreift. Die Anpassungszahlen für die verschiedenen Jahrhunderte stehen im Hash »%ADJ«. Die Zeilen 8 und 9 füllen den Hash, in dem sie ihm eine Liste zuweisen, die abwechselnd Jahrhundertzahlen und die jeweils zugehörigen Anpassungswerte enthält.
Die Vergleichswerte errechnet das CPAN-Modul »DateTime«, das tageweise vom 1.1.1700 bis zum 31.12.2399 hochzählt und bei jedem Schleifendurchgang Tag, Monat und Jahr liefert. Die Endlosschleife ab Zeile 17 ist nicht wirklich endlos, denn Zeile 25 bricht den Reigen ab, falls das Jahr des aktuellen Datums 2399 überschreitet. Ist die Schleife noch nicht am Ende, addiert die Methode »add()« des »DateTime«-Objekts unten einen Tag zum aktuellen Datum - und schon nimmt eine weitere Schleifenrunde ihren Anfang.
| Whitepaper |
|
The Role of Open Source in Data Integration
Obwohl in den letzten Jahren viele technische Fortschritte erzielt werden konnten, verfügen die meisten Datenintegrationsprozesse nach wie vor nur über eine sehr begrenzte Automatisierung. Das vorliegende White Paper von dem Industry Analyst Mark Madson wird zunächst ein grundlegendes Verständnis von Daten Integration vermitteln, die Vorzüge von Open Source Lösungen für Daten Integration erläutern und Ihnen professionelle Empfehlungen geben, damit Sie Ihre Integrationsjobs noch einfacher und produktiver gestalten können.
Download PDF (Registrierung erforderlich)
|
|
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele (Folge 2)
Der zweite Teil des Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele White Papers beleuchtet anhand weiterer ausgewählter Case Studies die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|