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.

Pages: 1 2

5 Responses to “Thank God It’s Friday!”

  1. […] today’s Programming Praxis exercise, our goal is to implement three functions related to dates: two ways to […]

  2. 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)
    
  3. Graham said

    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)
    
  4. Mike said

    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]
    
  5. razvan said

    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)));
        }
    
    }
    

Leave a comment