Wochentag aus Datum errechnen

  • Thread starter Alex Loipführer
  • Start date
Nicolas Mittelmaier wrote:

Diese Formel ist jedoch nicht für wirklich alle (!) Daten anwendbar.
Insbesondere das 1. März 2000-Problem wird in dem von mir oben
geposteten Artikel beschrieben.
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 <http://swen.pascal.at/>.
 
Pascal Le Bail <news@pascal.at> wrote:
: Nicolas Mittelmaier wrote:

: 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
 
Peter Heitzer wrote:

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?

also vereinfacht sich das Ganze zu d1' = y + y/4 + y*4 + y oder d1' = y
(1+2+4+1) oder y
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 <http://swen.pascal.at/>.
 
Pascal Le Bail <news@pascal.at> wrote:
: Peter Heitzer wrote:

:> 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.
 
Peter Heitzer wrote:

: 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.
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 <http://swen.pascal.at/>.
 
"Alex Loipführer" <Loipe@t-online.de> schrieb:

weiß jemand wie man den Wochentag von einem Datm berechnet?
Ich will es in einem Atmel Microcontroller verwenden. Vielleicht hat ja
schon jemand einen Quelltext.
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
 
Pascal Le Bail <news@pascal.at> wrote:
: Peter Heitzer wrote:

:> : 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.
 

Welcome to EDABoard.com

Sponsor

Back
Top