Wochentag aus Datum errechnen

Oct 07, 2003 26 Replies

Fuer welche Daten ist die Formel nicht anwendbar?

Der gregorianische Kalender zeichnet sich doch dadurch aus, dass es alle vier Jahre ein Schaltjahr gibt. Volle Jahrhunderte sind nur dann ein Schaltjahr, wenn sie durch 400 teilbar sind. Weitere Regeln gibt es nicht; die immer wieder diskutierten zusaetzlichen Schalttage jenseits des Jahres

3000 waeren eine Korrektur, sie sind AFAIK nicht Bestandteil des gregorianischen Kalenders.

Und genau den beschriebenen Sachverhalt gibt die Funktion wieder. Das ist eigentlich relativ leicht nachzuvollziehen. Ich habe nun etwas "reverse engineering" probiert. Sehen wir uns die Funktion nochmals an:

int dayofweek(int y, int m, int d) { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; }

Die Formel berechnet im Prinzip eine fortlaufende Tagesnummer. Da wir nur am Modulo-7-Ergebnis dieser Nummer interessiert sind, koennen wir der Einfachheit halber auch schon einzelne Summanden modulo 7 rechnen.

Fall 1: Das Datum liegt im Jaenner oder Februar:

Wir berechnen zuerst die Tage aller bisher vergangenen Jahre (daher y -=

1) seit einem (imaginaeren) Jahr 0. Das waere unter Beruecksichtigung der Schaltjahre:

d1 = 365*y + y/4 - y/100 + y/400 = = 364*y + y + y/4 - y/100 + y/400

364*y ist immer durch 7 teilbar, liefert also zum Ergebnis modulo 7 keinen Beitrag und wird daher weggelassen.

d1' = y + y/4 - y/100 + y/400

Als naechstes kommen die Tage, die in den bisher verganganen Monaten des aktuellen Jahres liegen. Wenn das gesuchte Datum im Jaenner liegt, sind das natuerlich d2 = 0, im Februar waeren es d2 = 31 (naemlich der gesamte Jaenner). Dies koennen wir wieder modulo 7 rechnen und kommen auf den naechsten Summanden

d2'[1, 2] = {0, 3}

Dies sind genau die Werte, die im Array t[] stehen. Dann addieren wir noch den Tag (d) dazu und erhalten den Wochentag

w = (d1' + d2' + d) % 7

Fall 2: Das Datum liegt in den Monaten Maerz bis Dezember:

In diesem Fall muessen wir bei der Berechnung der Schalttage das laufende Jahr mit einbeziehen, da der Februar ja bereits vorbei ist. Wir dekrementieren y diesmal also nicht und rechnen

d1 = 365*y + y/4 - y/100 + y/400 - 365

Die Subtraktion von 365 Tagen ist erforderlich, da wir hier das vollstaendige aktuelle Jahr (und nicht nur den eventuellen Schalttag) mitgerechnet haben, was natuerlich um 365 Tage zuviel sind. Wie zuvor vereinfachen wir wieder:

d1' = y + y/4 - y/100 + y/400 - 1

Jetzt kommen wieder die Tage, die in den bisher verganganen Monaten des aktuellen Jahres liegen. Fuer ein Datum in den Monaten Maerz...Dezember waeren dies

d2[3...12] = {59, 90, 120, 151, 181, 212, 243, 273, 304, 334}

Bei d1' haben wir ein stoerendes "-1" stehen, dieses packen wir in das d2 hinein und erhalten fuer d1" nun dieselbe Formel wie im Fall 1.

d1" = y + y/4 - y/100 + y/400 d2'[3...12] = {58, 89, 119, 150, 180, 211, 242, 272, 303, 333}

Das d2' koennen wir nun aus den bekannten Gruenden wieder modulo 7 rechnen:

d2"[3...12] = {2, 5, 0, 3, 5, 1, 4, 6, 2, 4}

Auch dies sind genau die Werte, die im Array t[] stehen. Nach Addition des Tages (d) erhalten wir wieder den Wochentag:

w = (d1" + d2" + d) % 7

Ich wuesste nicht, fuer welche Daten des gregorianischen Kalenders dieses Verfahren falsche Ergebnisse liefern sollte.

Gruesse, Pascal Le Bail, Wien Meine Absenderadresse ist derzeit inaktiv. Siehe .

: 364*y ist immer durch 7 teilbar, liefert also zum Ergebnis modulo 7 keinen : Beitrag und wird daher weggelassen.

: d1' = y + y/4 - y/100 + y/400 Die Division durch 100 und 400 kann man auch noch vereinfachen. Da nämlich 100 mod 7 == 2 ist, folgt y/100 mod 7 == y/2 mod 7 oder y * 4 mod 7.

400 mod 7==1 also vereinfacht sich das Ganze zu d1' = y + y/4 + y*4 + y oder d1' = y (1+2+4+1) oder y

Wie man leicht durch Einsetzen eines Wertes fuer y feststellen kann, sind diese Ausdruecke *nicht* gleich. Z.B. fuer y = 2003: (2003 / 100) mod 7 = 6 (2003 / 2) mod 7 = 0 (2003 * 4) mod 7 = 4

Oder habe ich da etwas falsch verstanden?

Vermutlich nicht. Es waere ja auch sehr seltsam, wenn die gesamte Information darueber, welche Jahre Schaltjahre sind, aus der Formel herausfiele...

Gruesse, Pascal Le Bail, Wien Meine Absenderadresse ist derzeit inaktiv. Siehe .

:> Pascal Le Bail wrote: :>> d1' = y + y/4 - y/100 + y/400

:> Die Division durch 100 und 400 kann man auch noch vereinfachen. Da :> nämlich 100 mod 7 == 2 ist, folgt :> y/100 mod 7 == y/2 mod 7 oder y * 4 mod 7.

: Wie man leicht durch Einsetzen eines Wertes fuer y feststellen kann, sind : diese Ausdruecke *nicht* gleich. Z.B. fuer y = 2003: : (2003 / 100) mod 7 = 6 : (2003 / 2) mod 7 = 0 : (2003 * 4) mod 7 = 4

: Oder habe ich da etwas falsch verstanden? Sowohl du als auch ich. Gemeint mit /100 war wohl int(y/100), d.h. der ganzzahlige Wert der Jahrhunderte. Aber eine Modula Division kann man durch Multiplikation mit der Inversen ersetzen. (Mathematiker mögen die unklare Bezeichnung überlesen). Die Inverse zu n ist definiert als die Zahl i, für die n*i==1 modulo wird. Genauso wie für rationale Zahlen n * (1/n)==1 ist. Da 2*4==1 mod 7 ist, ist die Modula Inverse zu 2 also 4. Ebenso wie bei rationalen Zahlen ist die Division durch 0 undefiniert, es gibt also zu 0 keine Inverse. Der Denkfehler bei 2003/2 ist, daß du zuerst 2003/2=1001.5 ermittelt, dann den ganzzahligen Wert genommen hast und danach modula 7 ermittelt hast. Du hast also die falsche Division verwendet und nicht die für Modula definierte.

Natuerlich. Das sollte eigentlich aus der Herleitung der Formel hervorgehen. Ich hatte es nur irrtuemlich nicht hingeschrieben, da ich von der C-Funktion des OP ausging, und dort war aus dem Kontext klar, dass nur mit ganzen Zahlen gerechnet wird, auch wenn kein "int" dort steht. Ich habe also den Uebergang von der C-Syntax zu einer "mathematischen" Schreibweise unterlassen.

Ich wuerde nicht sagen, dass dies ein Denkfehler war. Denn gemaess der Herleitung der Formel ist ja tatsaechlich eine Integer-Division erforderlich, und diese habe ich angewendet.

Nein, ich habe die richtige verwendet, dies aber falsch angeschrieben. Statt "y/4" muesste es also "int(y/4)" heissen etc. Anderenfalls kann die Formel, um die es hier geht, keine richtigen Ergebnisse liefern.

Gruesse, Pascal Le Bail, Wien Meine Absenderadresse ist derzeit inaktiv. Siehe .

"Alex Loipführer" schrieb:

Hier eine Variante:

byte WOCHENTAG_BERECHNUNG(void) {

word wt_MOTAGE[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; byte wt_TAG = dat.tag; /* tag, monat, jahr: das Datum zum berechnen */ byte wt_MONAT = dat.monat; word wt_JAHR = dat.jahr; word wt_DAJVAR = wt_JAHR - 1980; byte wt_IST = 0;

if(!wt_MONAT) wt_MONAT = 1; wt_IST = (365 * wt_DAJVAR + (wt_DAJVAR >> 2) + wt_MOTAGE[wt_MONAT - 1] + wt_TAG - (wt_MONAT > 2 ? 0 : (wt_JAHR % 4 == 0)) + 2) % 7; return(wt_IST); }

liefert 0-6 (So-Sa) als Wochentag zurück.

Gruß Matthias

:> : Oder habe ich da etwas falsch verstanden? Sowohl du als auch ich. :> Gemeint mit /100 war wohl int(y/100), d.h. der ganzzahlige Wert der :> Jahrhunderte.

: Natuerlich. Das sollte eigentlich aus der Herleitung der Formel : hervorgehen. Ich hatte es nur irrtuemlich nicht hingeschrieben, da ich von : der C-Funktion des OP ausging, und dort war aus dem Kontext klar, dass nur : mit ganzen Zahlen gerechnet wird, auch wenn kein "int" dort steht. Ich : habe also den Uebergang von der C-Syntax zu einer "mathematischen" : Schreibweise unterlassen.

:> Der Denkfehler bei 2003/2 ist, daß du zuerst 2003/2=1001.5 ermittelt, :> dann den ganzzahligen Wert genommen hast und danach modula 7 ermittelt :> hast.

: Ich wuerde nicht sagen, dass dies ein Denkfehler war. Denn gemaess der : Herleitung der Formel ist ja tatsaechlich eine Integer-Division : erforderlich, und diese habe ich angewendet.

:> Du hast also die falsche Division verwendet. Das bezog sich einzig auf das Beispiel 2003/2. Wenn alle Operationen modulo durchgeführt werden sollen, dürfen nur die Operationen verwendet werden, die innerhalb des Rings definiert sind. Die ganzzahlige Division gehört nicht dazu. Du hast 2003/2=1001 berechnet und davon Modulo 7 gebildet. Meine Erläuterungen zur Modulo Inversen bezogen sich auf die vollständige Berechnung innerhalb des Rings. Deshalb kamst du auf einen anderen Wert als 2003*4 mod 7. Aber lassen wir es dabei. Ich wollte nur auf mögliche Vereinfachungen durch Wegfall von Divisionen hinweisen.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required