# 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))