#lang scheme/base
(require srfi/19
scheme/contract
mzlib/trace
)
(define (leap-year? year)
(or (and (= (modulo year 4) 0)
(not (= (modulo year 100) 0)))
(= (modulo year 400) 0)))
(define (doomsday y)
(modulo (+ 2 (floor (+ y (/ y 4) (- (/ y 100)) (/ y 400)))) 7))
(define month-days '((0 . 0) (1 . 31) (2 . 28) (3 . 31) (4 . 30) (5 . 31) (6 . 30)
(7 . 31) (8 . 31) (9 . 30) (10 . 31) (11 . 30) (12 . 31)))
(define (year-day d)
(define (helper month leap?)
(map (lambda (kv)
(cond ((>= (car kv) month) 0)
((not leap?) (cdr kv))
(else
(if (= (car kv) 2) 29 (cdr kv)))))
month-days))
(apply + (date-day d) -1
(helper (date-month d) (leap-year? (date-year d)))))
(define (normalize-year/month/day year month day)
(define (month-day-helper)
(if (and (= month 2) (leap-year? year))
'(2 . 29)
(assoc month month-days)))
(define (helper month/day)
(if (> day (cdr month/day)) (list year (add1 month) (- day (cdr month/day)))
(list year month day)))
(helper (month-day-helper)))
(define (week-day d)
(modulo (+ (doomsday (date-year d)) (year-day d) -2)
7))
(define (assoc/cdr key lst)
(let ((val (assoc key lst)))
(if (not val) val (cdr val))))
(define (month->num month (number-ok? #f))
(define (helper mon)
(cond ((symbol? mon)
(helper (symbol->string mon)))
((string? mon)
(string-downcase mon))
(else #f)))
(if (and number-ok? (number? month))
(let ((it (modulo month 12)))
(if (= it 0) 12 it))
(assoc/cdr (helper month)
'(("jan" . 1)
("january" . 1)
("feb" . 2)
("feburary" . 2)
("mar" . 3)
("march" . 3)
("april" . 4)
("apr" . 4)
("may" . 5)
("jun" . 6)
("june" . 6)
("jul" . 7)
("july" . 7)
("aug" . 8)
("august" . 8)
("sep" . 9)
("sept" . 9)
("september" . 9)
("oct" . 10)
("october" . 10)
("nov" . 11)
("november" . 11)
("dec" . 12)
("december" . 12)))))
(define (weekday->num week (number-ok? #f))
(define (helper week)
(cond ((symbol? week)
(helper (symbol->string week)))
((string? week)
(string-downcase week))
(else #f)))
(if (and number-ok? (number? week))
(modulo week 7)
(assoc/cdr (helper week)
'(("sun" . 0)
("sunday" . 0)
("mon" . 1)
("monday" . 1)
("tu" . 2)
("tue" . 2)
("tues" . 2)
("tuesday" . 2)
("wed" . 3)
("wednesday" . 3)
("thu" . 4)
("thur" . 4)
("thurs" . 4)
("thursday" . 4)
("fri" . 5)
("friday" . 5)
("sat" . 6)
("saturday" . 6)))))
(define (m->s m) (* m 60))
(define (h->m h) (* h 60))
(define (h->s h (m 0) (s 0))
(+ s (m->s (+ (h->m h) m))))
(provide/contract
(leap-year? (-> integer? boolean?))
(doomsday (-> integer? (integer-in 0 6)))
(year-day (-> date? (integer-in 0 366)))
(week-day (-> date? (integer-in 0 6)))
(normalize-year/month/day (-> integer? (integer-in 1 12) (integer-in 0 31)
(listof integer?)))
(month->num (->* (any/c)
(boolean?)
(or/c #f number?)))
(weekday->num (->* (any/c)
(boolean?)
(or/c #f number?)))
(h->s (->* (exact-integer?)
(exact-integer? exact-integer?)
exact-integer?))
)