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