Thank God It’s Friday!
June 24, 2011
The function for Gauss’ method is shown below. We changed the decimals to fractions so that all the calculations would be done in exact arithmetic.
(define (day-of-week year month day) ; gauss method
(let* ((yr (if (< month 3) (- year 1) year))
(m (if (< month 3) (+ month 10) (- month 2)))
(y (modulo yr 100)) (c (quotient yr 100)))
(list-ref '(sun mon tue wed thu fri sat)
(modulo (+ day (floor (- (* 13/5 m) 1/5)) y
(quotient y 4) (quotient c 4) (- (* 2 c))) 7))))
> (day-of-week 2011 6 24)
fri
Here is our version of Sakamoto’s method:
(define (day-of-week year month day) ; sakamoto method
(let ((t (vector 0 3 2 5 0 3 5 1 4 6 2 4))
(y (if (< month 3) (- year 1) year)))
(list-ref '(sun mon tue wed thu fri sat)
(modulo (+ day y (quotient y 4) (- (quotient y 100))
(quotient y 400) (vector-ref t (- month 1))) 7))))
> (day-of-week 2011 6 24)
fri
Conway’s method is more of a challenge. Here is the calculation of the anchor day:
(define (anchor year)
(let ((c (+ (quotient year 100) 1)))
(list-ref '(sun mon tue wed thu fri sat)
(modulo (+ (* 5 c) (quotient (- c 1) 4) 4) 7))))
> (anchor 2011)
tue
Given an anchor day, here is the function to calculate the doomsday:
(define (doomsday year)
(let* ((days '(sun mon tue wed thu fri sat))
(y (modulo year 100))
(q (quotient y 12))
(r (remainder y 12))
(x (quotient r 4))
(c (+ (quotient year 100) 1))
(anchor (+ (* 5 c) (quotient (- c 1) 4) 4)))
(list-ref days (modulo (+ q r x anchor) 7))))
> (doomsday 2011)
mon
It is easier for a computer, though perhaps not for a mentalist, to calculate the doomsday like this:
(define (doomsday year)
(list-ref '(sun mon tue wed thu fri sat)
(modulo (+ 2 year (quotient year 4)
(- (quotient year 100)) (quotient year 400)) 7)))
> (doomsday 2011)
mon
You can run the program at http://programmingpraxis.codepad.org/BeTpy03N, which also includes the code for the method from the Standard Prelude and Zeller’s congruence for comparison. Note that Zeller’s congruence is essentially the same as Gauss’ method.
[…] today’s Programming Praxis exercise, our goal is to implement three functions related to dates: two ways to […]
My Haskell solution (see http://bonsaicode.wordpress.com/2011/06/24/programming-praxis-thank-god-it%E2%80%99s-friday/ for a version with comments):
data Weekday = Sun | Mon | Tue | Wed | Thu | Fri | Sat deriving (Enum, Eq, Show) gauss :: Int -> Int -> Int -> Weekday gauss y m d = toEnum $ mod (d + floor (2.6 * fromIntegral (mod (m - 2) 12) - 0.2) + y' + div y' 4 + div c 4 - 2*c) 7 where (c,y') = divMod (if m < 3 then y - 1 else y) 100 sakamoto :: Int -> Int -> Int -> Weekday sakamoto y m d = toEnum $ mod (y + div y 4 - div y 100 + div y 400 + [0,3,2,5,0,3,5,1,4,6,2,4] !! (m - 1) + d) 7 conway :: Int -> Weekday conway y = toEnum $ mod (q + r + div r 4 + 5*(c+1) + div c 4 + 4) 7 where (c, (q,r)) = (div y 100, divMod (mod y 100) 12)My Python submission (basically just a translation of the Scheme solution):
#!/usr/bin/env python from fractions import Fraction DAYS = {0: "Sunday", 1: "Monday", 2: "Tuesday", 3: "Wednesday", 4: "Thursday", 5: "Friday", 6: "Saturday"} def gauss(year, month, day): yr = (year - 1) if (month < 3) else year m = (month + 10) if (month < 3) else (month - 2) c, y = divmod(yr, 100) return DAYS[sum((day, int((Fraction('13/5') * m) - Fraction('1/5')), y, (y / 4), (c / 4), - 2 * c)) % 7] def sakamoto(year, month, day): t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] y = (year - 1) if (month < 3) else year return DAYS[sum((day, y, (y / 4), - (y / 100), (y / 400), t[month - 1])) % 7] def doomsday(year): return DAYS[sum((2, year, year / 4, - (year / 100), year / 400)) % 7] if __name__ == "__main__": print gauss(2011, 6, 24) print sakamoto(2011, 6, 24) print doomsday(2011)Python version
Tried all the formulas from Wikipedia for conway’s method. And went all the way to calculate the day of the week.
dow = "Sun Mon Tue Wed Thu Fri Sat".split() def leap(year): return bool(not year % 400 or not year % 4 and year % 100) def gauss(year, month, day): c, y = divmod(year if month > 2 else (year - 1), 100) m = (month - 3)%12 + 1 return dow[(day + int(2.6*m - 0.2) + y + y//4 + c//4 - 2*c) % 7] def sakamoto(year, month, day): t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] y = year if month > 2 else (year - 1) return dow[(day + y + y//4 - y//100 + y//400 + t[month - 1]) % 7] def conway(year, month, day): c, y = divmod(year, 100) # original formula #c += 1 #anchor = (5*c + (c-1)//4 + 4)%7 # modified formula-don't add 1 to first 2 digits of year. anchor = (5*c + c//4 + 2)%7 # original conway formula #doomsday = (anchor + y//12 + y%12 + y%12//4)%7 # alternative from wikipedia. I think this is easiest to do in head. doomsday = (anchor + y + y//4)%7 # odd+11 rule from wikipedia #y = (y+11) if y&1 else y #y = y//2 #y = (y+11) if y&1 else y #y = 7 - y%7 #doomsday = (anchor + y)%7 #computer formula from wikipedia #doomsday = (2 + y + y//4 - y//100 + y//400)%7 nearest = (10, 28, 0, 4, 9, 6, 11, 8, 5, 10, 7, 12)[month - 1] if month < 3 and leap(year): nearest += 1 return dow[(doomsday + day - nearest) % 7]Here’s my solution in Java:
package dayofweek; public class DayOfWeek { private static final int[] SAKAMOTO_TABLE = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; private static void checkDate(int d, int m, int y) { if (m < 1 || m > 12) throw new NumberFormatException("Bad month"); if (d < 0 || d > 31) throw new NumberFormatException("Bad day"); if (m == 2 && (d > 29 || d > 28 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0))) throw new NumberFormatException("Bad day"); if (y < 0) throw new NumberFormatException("Bad year"); } public static int gauss(int d, int m, int y) { checkDate(d, m, y); if (m >= 3 && m <= 12) m -= 2; else if (m < 3 && m > 0) { m += 10; y--; } int c = y / 100; y %= 100; return (d + (int) Math.floor(2.6 * (double) m - 0.2) + y + y / 4 + c / 4 - 2 * c) % 7; } public static int sakamoto(int d, int m, int y) { checkDate(d, m, y); if (m < 3 && m > 0) y--; return (y + y / 4 - y / 100 + y / 400 + SAKAMOTO_TABLE[m - 1] + d) % 7; } public static int conway(int d, int m, int y) { checkDate(d, m, y); int c = y / 100 + 1; int anchor = ((5 * c + (c - 1) / 4) % 7 + 4) % 7; int yy = y % 100; int quo = yy / 12, rem = yy % 12; int doomsday = ((quo + rem + rem / 4) % 7 + anchor) % 7; int doom = 0; if (m == 4 || m == 6 || m == 8 || m == 10 || m == 12) doom = m; else if (m == 2) { if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) doom = 29; else doom = 28; } else if (m == 3) doom = 0; else if (m == 3) { if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) doom = 11; else doom = 10; } else switch (m) { case 5: doom = 9; break; case 7: doom = 11; break; case 9: doom = 5; break; case 11: doom = 7; break; } if (d > doom) return ((d - doom) % 7 + doomsday) % 7; else return ((doomsday - (doom - d) % 7) + 7) % 7; } public static String intToDate(int d) { switch (d) { case 0: return "Sunday"; case 1: return "Monday"; case 2: return "Tuesday"; case 3: return "Wednesday"; case 4: return "Thursday"; case 5: return "Friday"; case 6: return "Saturday"; } throw new NumberFormatException("Bad day of week"); } public static void main(String[] args) { System.out.println(intToDate(gauss(24, 6, 2011))); System.out.println(intToDate(sakamoto(24, 6, 2011))); System.out.println(intToDate(conway(11, 5, 2010))); } }