Easter

It is simple but tedious to calculate the date of Easter in the gregorian calendar. The calculation below comes from the book Astronomical Algorithms by Jean Meeus via [[en.wikipedia.org/wiki/computus]]. (define (easter year) (let* ((a (modulo year 19)) (b (quotient year 100)) (c (modulo year 100)) (d (quotient b 4)) (e (modulo b 4)) (f (quotient (+ b 8) 25)) (g (quotient (+ (- b f) 1) 3)) (h (modulo (- (+ (* 19 a) b 15) d g) 30)) (i (quotient c 4)) (k (modulo c 4)) (l (modulo (- (+ 32 (* 2 e) (* 2 i)) h k) 7)) (m (quotient (+ a (* 11 h) (* 22 l)) 451)) (month (quotient (- (+ h l 114) (* 7 m)) 31)) (day (+ (modulo (- (+ h l 114) (* 7 m)) 31) 1))) (values month day))) Here is an example showing the use of [[easter]]: [[ (let loop ((year 2000)) (call-with-values (lambda () (easter year)) (lambda (month day) (for-each display `(,year ": " ,(if (= month 3) "March" "April") " " ,day)) (newline))) (if (< year 2020) (loop (+ year 1)))) 2000: April 23 2001: April 15 2002: March 31 2003: April 20 2004: April 11 2005: March 27 2006: April 16 2007: April 8 2008: March 23 2009: April 12 2010: April 4 2011: April 24 2012: April 8 2013: March 31 2014: April 20 2015: April 5 2016: March 27 2017: April 16 2018: April 1 2019: April 21 2020: April 12 ]] Various other dates are based on Easter. All can be calculated using the [[easter-add]] and [[easter-sub]] functions: (define (easter-add year days) (call-with-values (lambda () (easter year)) (lambda (m d) (let loop ((m m) (d (+ d days))) (cond ((and (= m 3) (> d 31)) (loop (+ m 1) (- d 31))) ((and (= m 4) (> d 30)) (loop (+ m 1) (- d 30))) ((and (= m 5) (> d 31)) (loop (+ m 1) (- d 31))) ((and (= m 6) (> d 30)) (loop (+ m 1) (- d 30))) (else (values m d))))))) (define (easter-sub year days) (call-with-values (lambda () (easter year)) (lambda (m d) (let ((feb (if (zero? (modulo year 4)) 29 28))) (let loop ((m m) (d (- d days))) (cond ((and (= m 4) (< d 1)) (loop (- m 1) (+ d 31))) ((and (= m 3) (< d 1)) (loop (- m 1) (+ d feb))) ((and (= m 2) (< d 1)) (loop (- m 1) (+ d 31))) (else (values m d)))))))) [[Easter-sub]] uses an invalid test for leap year, but it will work until after I die, so I don't care. (define (mardi-gras year) (easter-sub year 47)) (define (ash-wednesday year) (easter-sub year 46)) (define (palm-sunday year) (easter-sub year 7)) (define (holy-thursday year) (easter-sub year 3)) (define (good-friday year) (easter-sub year 2)) (define (holy-saturday year) (easter-sub year 1)) (define (easter-vigil year) (easter-sub year 1)) (define (divine-mercy year) (easter-add year 7)) (define (ascension year) (easter-add year 39)) (define (pentecost year) (easter-add year 49)) (define (corpus-christi year) (easter-add year 63))