rjchen@phoenix.Princeton.EDU (Raymond Juimong Chen) (04/15/89)
From my personal archives...
PROCEDURE Zeller(d: DATE): WEEKDAY;
(* zeller : date -> Weekday
Pre: InvDate(d) % date after 14-Sep-1752 in UK
Post: true
zeller(d) ==
let m1 == month(d) - 2,
y1 == year(d)
in let m == if m1 < 1 then m1 + 12 else m1,
y2 == if m1 < 1 then y1 - 1 else y1
in let c == y2 truncate 100,
y == y2 mod 100
in weekdays[(((((26 m) - 2) truncate 10) +
(day(d) +
((c truncate 4) +
((5 c) +
((y truncate 4) + y))))) mod 7) + 1];
What's going on?
The first catch in this function is February --- it's an 'odd
man out' as far as the months go, so let's redefine the start of
year as the 1st March, then we need only worry about leap-years
in the 'year' part of the formula. Next:
We want a function which gives a mapping:
1->2, 2->5, 3->7 etc ie the number of days ''extra'' over a 28
day month. This is the '((26 m) -2) truncate 10' term
to this we have to add the offset into the month so that
when we look at it mod 7 we have a mapping to weekday.
Oh by the way this term also gives a constant offset of 2. This
chap Zeller was very clever at hiding it!
Unfortunately it isn't that simple: there is an extra day in
each year (mod 7) so add y (the year in the centuary). Right so
far, but leap years? Well *within* a centuary these are regular
-- every 4 years, hence y truncate 4.
Ok so why emphasise within? well centuaries are only leap-years
if they are divisible by 400. We've already divided by 100 to
get c so our term becomes c truncate 4.
Now about other centuaries: 100 mod 7 = 2, that is to say the -2c
in the original formula. Catch is some programming languages
do strange things on Modulus a negative number (Modula2 in
particular) so let's use a positive term instead: +5c gives the
correct result.
*)
VAR
k, m, C, Y, temp: INTEGER;
BEGIN
k := d.day;
m := INTEGER(ORD(d.month)) -1; (* Really -2, but enums start with 0 *)
(* Which either proves that Modula-2 is innappropriate in this context, or
* that an enumeration is. Unfortunately an enumeration is a requirement,
* for this exercise, and unlike C++ I can't chose the values of types in
* an enumeration in Modula-2. There is of course also the limitation of
* this implementation which prevents DATE being a real obscured DATA Type
* (only pointers)... Modula2 Gah!
*)
temp := d.year;
IF m < 1 THEN
m := m + 12;
temp := temp - 1;
END;
C := temp DIV 100;
Y := temp MOD 100;
RETURN VAL(WEEKDAY,CARDINAL(((26*m - 2) DIV 10 + k + Y
+ Y DIV 4 + C DIV 4 + 5C) MOD 7));
END Zeller;
--
Raymond Chen UUCP: ...allegra!princeton!{phoenix|pucc}!rjchen
BITNET: rjchen@phoenix.UUCP, rjchen@pucc
ARPA: rjchen@phoenix.PRINCETON.EDU, rjchen@pucc.PRINCETON.EDU
"Say something, please! ('Yes' would be best.)" - The Doctor