[Back to DATETIME SWAG index] [Back to Main SWAG index] [Original]
======================================
Datumsarithmetik mit den PC und Pascal
======================================
Dieser Informationstext soll allen Programmierern helfen, die mit
der Berechnung von Tagesdaten oder der Logik der kirchlichen Feier-
tage kaempfen. Diese Text kann frei verteilt werden, solange die
folgenden Informationen nicht daraus entfernt werden.
Copyright (c) 1992 fuer diese Zusammenstellung und den erlaeuternden
Text sowie evtl. erfolgte Korrekturen by Armin Hanisch (2:246/41)
Ich moechte allen danken, die durch ihre e-Mails und sonstige Bei-
traege, insbesondere der Veroeffentlichung von Sources diese Zu-
sammenstellung erst moeglich gemacht haben. Nach Informationsstand
des Autors haben alle Autoren diese Routinen als Public Domain frei-
gegeben. Ich uebernehme allerdings weder dafuer noch fuer die korekte
Berechnung irgendwelcher Daten eine Garantie. Ich habe allerdings
einige Stunden an Testarbeit investiert, die Daten stimmen!
Sources und Alogrithmen von folgenden Personen wurden ausgewertet,
zur Verfuegung gestellt und teilweise korrigert oder an einen fuer
diesen Text einheitlichen Stil bzw. Datentyp angepasst:
Armin Hanisch - Feiertagsberechnungen
Bernd Strehuber - Feiertagsberechnungen
Carley Phillips - Jul. Berechnungen
Jeff Duntemann - Wochentagsberechnung
Judson McClendon - Osterberechnung
Martin Austermeier - Tagesberechnungen
Paul Schlyter - Osterberechnung
Pit Biernath - Jul. Berechnungen
Scott Bussinger - Jul. Berechnungen
OSTERBERECHNUNGEN:
==================
Dieser Algorithmus basiert nicht auf der Berechnung von Gauss und
kommt ohne Ausnahmen aus (lt. Paul Schlyter). Werte ueber 31 be-
zeichnen den Tag im April-31, Werte darunter bezeichnen den Tag
im Maerz.
FUNCTION Easter(year : INTEGER) : INTEGER;
VAR a, b, c, d, e, f, g, h, i, k, l, m : INTEGER;
BEGIN
a := year MOD 19;
b := year DIV 100;
c := year MOD 100;
d := b DIV 4;
e := b MOD 4;
f := ( b + 8 ) DIV 25;
g := ( b - f + 1 ) DIV 3;
h := ( 19 * a + b - d - g + 15 ) MOD 30;
i := c DIV 4;
k := c MOD 4;
l := ( 32 + 2 * e + 2 * i - h - k ) MOD 7;
m := ( a + 11 * h + 22 * l ) DIV 451;
Easter := h + l - 7 * m + 22;
END{FUNC};
Eine weitere Moeglichkeit, Ostern sehr schnell zu berechnen, besteht
darin, den auf das juedische Passahfest folgenden Sonntag zu berechnen.
Der sog. Passah-Vollmond wird berechnet, in dem das Jahr durch 19 ge-
teilt wird und der Rest mit der folgenden Tabelle verglichen wird:
0: Apr 14 5: Apr 18 10: Mrz 25 15: Mrz 30
1: Apr 03 6: Apr 08 11: Apr 13 16: Apr 17
2: Mrz 23 7: Mrz 28 12: Apr 02 17: Apr 07
3: Apr 11 8: Apr 16 13: Mrz 22 18: Mrz 27
4: Mrz 31 9: Apr 05 14: Apr 10
Faellt dieses Datum auf einen Sonntag, ist Ostern der naechste Sonntag!
Beispiel: 1992 MOD 19 = 16, daraus folgt 17.04., der naechste Sonntag
ist dann der 19. April (Ostersonntag)
FEIERTAGE:
==========
Massgebend fuer die kirchlichen Feiertage ist sowohl das Osterdatum
als auch der 1. Advent, der Beginn des Krichenjahres. Wie man Ostern
berechnet, wurde oben erlaeutert. Hier nun also die Berechnungen der
restlichen Feiertage.
Aschermittwoch: 40 Tage vor dem Ostersonntag,
dann zurückgehen bis zum Mittwoch
Bsp.: result := GetOstern;
Dec(result,40);
WHILE DayOfWeek(result) <> 3 DO
Dec(result);
Palmsonntag: Der Sonntag vor dem Ostersonntag, die Berechnung
ist damit trivial.
Weisser Sonntag: Der Sonnrtag nach Ostern, ebenfalls simpel.
Christi Himmelfahrt: 39 Tage nach dem Ostersonntag oder anders gesagt,
der zweite Donnerstag vor Pfingsten.
Pfingsten: 49 Tage nach dem Ostersonntag.
Fronleichnam: 60 Tage nach dem Ostersonntag.
Maria Himmelfahrt: Fest am 15. August (nicht ueberall Feiertag!)
1. Advent: Vom 24.12. zurück bis zum nächsten Sonntag,
dann noch drei Wochen zurück.
Bsp.: result := MakeDate(24,12,year);
WHILE DayOfWeek(result) <> 0 DO
Dec(result);
Dec(result,21);
Buss- und Bettag: Der vorvorige Mittwoch vor dem 1. Advent, also
vom 1. Advent aus den Mittwoch suchen, dann noch
eine Woche zurück.
Bsp: <adventberechnung> <-- wie oben
WHILE DayOfWeek(result) <> 3 DO
Dec(result);
Dec(result,7);
Hl. drei Köinige: Fest am 06.01.
Allerheiligen: Fest am 01.11.
Tag der Arbeit: Fest am 01.05.
Tag der dt. Einheit: Fest am 03.10. Hier wird im Zuge von Sparmassnahmen
für die einzuführende Pflegeversicherung allerdings
überlegt, diesen Feiertag immer auf den ersten Sonn-
tag im Oktober zu legen, man sollte hier also die
politischen Nachrichten verfolgen!
DATUMSARITHMETIK:
=================
Berechnung eines Schaltjahres
-----------------------------
FUNCTION LeapYear(year : WORD) : BOOLEAN;
BEGIN
LeapYear := ((year MOD 4 = 0) AND (year MOD 100 <> 0))
OR (year MOD 400 = 0);
END;
Berechnung des Wochentages
--------------------------
FUNCTION DayOfWeek(Day,Month,Year: Integer): INTEGER;
VAR century,yr,dw: Integer;
BEGIN
IF Month < 3 THEN BEGIN
Inc(Month,10);
Dec(Year);
END{IF} ELSE
Dec(Month,2);
century := Year div 100;
yr := year mod 100;
dw := (((26*month-2) div 10)+day+yr+(yr div 4)
+(century div 4)-(2*century)) mod 7;
IF dw < 1 THEN Inc(dw,7);
DayOfWeek:=dw;
END{FUNC};
Als Ergebnis erhaelt man den Wochentag in folgender Reiehenfolge:
0=Sonntag, 1=Montag ..... 6=Samstag
Berechnung der Kalenderwoche
----------------------------
Die Woche 1 ist die Woche, die den ersten Donnerstag des Jahres
enthaelt, also mehr als die Haelfte diesem Jahr angehoert.
Ist der 01.01. ein Mo-Mi, dann liegt der 01.01. in der letzten
Woche des vergangenen Jahres. (DIN 1355)
FUNCTION WeekOfYear (Day,Month,Year:WORD) : WORD;
CONST
table1 : ARRAY [0..6] OF ShortInt = ( -1, 0, 1, 2, 3, -3, -2);
table2 : ARRAY [0..6] OF ShortInt = ( -4, 2, 1, 0, -1, -2, -3);
VAR
doy1 ,
doy2 : INTEGER;
BEGIN
doy1 := DayofYear (Day,Month,Year) + table1[DayOfWeek (1,1,Year)];
doy2 := DayofYear (Day,Month,Year) + table2[DayOfWeek(Day,Month,Year)];
IF doy1 <= 0 THEN WeekOfYear := WeekOfYear(31,12,Year-1)
ELSE IF doy2 >= DayofYear(31,12,Year) THEN WeekOfYear:=1
ELSE WeekOfYear := (doy1-1) DIV 7 + 1;
END;
Berechnung der Tage im Monat
----------------------------
FUNCTION DaysInMonth(month,year : WORD) : INTEGER;
VAR ly : BOOLEAN; { leap year? }
BEGIN
ly := ((year MOD 4 = 0) AND (year MOD 100 <> 0)) OR (year MOD 400 = 0);
IF (month IN [04,06,09,11]) THEN { even month }
DaysInMonth := 30
ELSE
IF month <> 2 THEN { rest except february }
DaysInMonth := 31
ELSE
IF ly THEN { leap year? }
DaysInMonth := 29
ELSE
DaysInMonth := 28;
END{FUNC};
Berechnung des Tages im Jahr
----------------------------
Diese Methode gilt fuer alle Jahre ab 1582, der Einführung des
gregorianischen Kalenders.
FUNCTION DayOfYear (day,month,year : WORD) : INTEGER;
VAR
i, tage : Integer;
BEGIN
tage := 0;
FOR i := 1 TO Pred(month) DO Inc (tage, DaysInMonth(i,year));
Inc (tage,day);
DayOfYear := tage;
END;
Eine andere Methode kommt ohne die Berechnung der Tage im Monat aus
und bezieht ebenfalls Schaltjahre ein. Der Gültigkeitsbereich dieses
Alogorithmus liegt von 1901 bis 2099.
FUNCTION DayNumber(Day,Month,Year : INTEGER ) : INTEGER;
VAR
term1 ,
term2 ,
term3 : INTEGER;
BEGIN
term1 := ( 275 * month ) div 9;
term2 := ( month + 9 ) div 12;
term3 := ( ( year mod 4 ) + 2 ) div 3;
DayNumber := term1 - term2 * ( 1 + term3 ) + day - 30;
END;
Um aus dem Tag im jahr wieder das Datum zu erhalten, kann die folgende
Routine verwendet werden:
FUNCTION YearDayToDMY(GYear,DayNumber : INTEGER; VAR Day,Month,Year : WORD);
CONST
MonthDays : Array [1..12] of integer=
(31,28,31,30,31,30,31,31,30,31,30,31);
VAR
I : integer;
done ,
ly : boolean;
BEGIN
I := 1;
done:=false;
ly := ((Gyear MOD 4 = 0) AND (Gyear MOD 100 <> 0))
OR (Gyear MOD 400 = 0);
IF ly THEN MonthDays[2] := 29; { correct for leap year february }
REPEAT
If DayNumber > MonthDays[i] THEN BEGIN
DayNumber := DayNumber - MonthDays[i];
Inc(i);
END{IF} ELSE BEGIN
year := GYear;
month := i;
day := DayNumber;
done := TRUE;
END{ELSE};
UNTIL (i > 12) OR done;
IF i > 12 THEN BEGIN
year:=GYear;
month:=12;
day:=31;
END{IF};
END;
Berechnung des julianischen Datums
----------------------------------
Diese Routinen dienen der Umwandlung des Datums in eine serielle
julianische Zahl im Bereich von 01.01.1900 bis zum 31.12.2078,
wobei 0 fuer den 01.01.1900 steht (uebringens: 1900 war kein Schalt-
jahr und der 01.01. war ein Montag).
FUNCTION DateOk(day,month,year : WORD) : BOOLEAN;
VAR
ly,ok : BOOLEAN;
maxday : WORD;
BEGIN
ok := (year >= 1900) AND (year <= 2078);
ly := ((year MOD 4 = 0) AND (year MOD 100 <> 0)) OR (year MOD 400 = 0);
IF ok THEN
ok := (month >= 01) AND (month <= 12);
IF ok THEN BEGIN
IF month IN [01,03,05,07,08,10,12] THEN
maxday := 31
ELSE
IF month <> 2 THEN
maxday := 30
ELSE
IF ly THEN
maxday := 29
ELSE
maxday := 28;
ok := (day >= 01) AND (day <= maxday);
END{IF};
DateOK := ok;
END{FUNC};
FUNCTION DMYtoDate(day,month,year : WORD) : WORD;
VAR
jul : Word;
BEGIN
IF NOT DateOK(day,month,year) THEN BEGIN
DMYToDate := $FFFF { signal an invalid date }
END{IF} ELSE BEGIN { convert back to DMY }
IF (Year = 1900) AND (Month < 3) THEN
IF Month = 1 THEN
jul := Pred(Day)
ELSE
jul := Day + 30
ELSE BEGIN
IF Month > 2 THEN
Dec (Month,3)
ELSE BEGIN
Inc (Month,9);
Dec (Year);
END{ELSE};
Dec(year,1900);
jul := ((1461 * LONGINT(Year)) div 4) +
((153 * Month+2) div 5) + Day + 58;
END{ELSE};
END{ELSE};
DMYToDate := jul;
END;
PROCEDURE DateToDMY(jul : WORD; VAR day,month,year: WORD);
VAR
LongTemp ,
Temp : LONGINT;
BEGIN
IF jul <= 58 THEN BEGIN
year := 1900;
IF jul <= 31 THEN BEGIN
month := 1;
day := Succ(jul);
END ELSE BEGIN
month := 2;
day := jul - 30;
END{ELSE}
END{IF} ELSE BEGIN
IF jul < $FF63 THEN BEGIN
LongTemp := (4 * LONGINT(jul-58)) - 1;
year := LongTemp DIV 1461;
temp := ((LongTemp MOD 1461) DIV 4) * 5 + 2;
month := temp DIV 153;
day := ((temp MOD 153) + 5) DIV 5;
Inc(year,1900);
IF month < 10 THEN
Inc(month,3)
ELSE BEGIN
Dec(month,9);
Inc(year);
END{ELSE};
END{IF} ELSE BEGIN { error in date range }
year := 0;
month := 0;
day := 0;
END{ELSE};
END{ELSE};
END;
[Back to DATETIME SWAG index] [Back to Main SWAG index] [Original]