#lang scheme/base
(require "base.ss" "date.ss" "depend.ss")
(define month (choice (tokens-ci "Jan" (return 1))
(tokens-ci "Feb" (return 2))
(tokens-ci "Mar" (return 3))
(tokens-ci "Apr" (return 4))
(tokens-ci "May" (return 5))
(tokens-ci "Jun" (return 6))
(tokens-ci "Jul" (return 7))
(tokens-ci "Aug" (return 8))
(tokens-ci "Sep" (return 9))
(tokens-ci "Oct" (return 10))
(tokens-ci "Nov" (return 11))
(tokens-ci "Dec" (return 12))))
(define (make-fixed-digits min max)
(seq digits <- (repeat digit min max)
(return (string->number (list->string digits)))))
(define digit1-2 (make-fixed-digits 1 2))
(define digit2 (make-fixed-digits 2 2))
(define digit4 (make-fixed-digits 4 4))
(define time-of-day (tokens hour <- digit1-2
#\:
minute <- digit1-2
second <-
(zero-one (tokens #\:
second <- digit1-2
(return second))
0)
(return (list hour minute second))))
(define date (tokens day <- digit1-2
m <- month
year <- (choice digit4 digit2)
(return (list year m day))))
(define (offset hour (minute 0))
(* 60 (+ (* 60 hour) minute)))
(define zone (choice (tokens-ci "UT" (return 0))
(tokens-ci "GMT" (return 0))
(tokens-ci "EST" (return (offset -5)))
(tokens-ci "EDT" (return (offset -4)))
(tokens-ci "CST" (return (offset -6)))
(tokens-ci "CDT" (return (offset -5)))
(tokens-ci "MST" (return (offset -7)))
(tokens-ci "MDT" (return (offset -6)))
(tokens-ci "PST" (return (offset -8)))
(tokens-ci "PDT" (return (offset -7)))
(tokens-ci "A" (return (offset -1)))
(tokens-ci "B" (return (offset -2)))
(tokens-ci "C" (return (offset -3)))
(tokens-ci "D" (return (offset -4)))
(tokens-ci "E" (return (offset -5)))
(tokens-ci "F" (return (offset -6)))
(tokens-ci "G" (return (offset -7)))
(tokens-ci "H" (return (offset -8)))
(tokens-ci "I" (return (offset -9)))
(tokens-ci "K" (return (offset -10)))
(tokens-ci "L" (return (offset -11)))
(tokens-ci "M" (return (offset -12)))
(tokens-ci "N" (return (offset 1)))
(tokens-ci "O" (return (offset 2)))
(tokens-ci "P" (return (offset 3)))
(tokens-ci "Q" (return (offset 4)))
(tokens-ci "R" (return (offset 5)))
(tokens-ci "S" (return (offset 6)))
(tokens-ci "T" (return (offset 7)))
(tokens-ci "U" (return (offset 8)))
(tokens-ci "V" (return (offset 9)))
(tokens-ci "W" (return (offset 10)))
(tokens-ci "X" (return (offset 11)))
(tokens-ci "Y" (return (offset 12)))
(tokens-ci "Z" (return (offset 0)))
(tokens sign <- (zero-one (choice (seq #\+ (return +))
(seq #\- (return -)))
+)
offset <- (seq hour <- digit2
minute <- digit2
(return (offset hour minute)))
(return (sign offset)))
))
(define weekday (choice (tokens-ci "Sun" (return 0))
(tokens-ci "Mon" (return 1))
(tokens-ci "Tue" (return 2))
(tokens-ci "Wed" (return 3))
(tokens-ci "Thu" (return 4))
(tokens-ci "Fri" (return 5))
(tokens-ci "Sat" (return 6))))
(define (date-helper day time z)
(build-date (car day)
(cadr day)
(caddr day)
(car time)
(cadr time)
(caddr time)
#:tz z))
(define date-time
(tokens (zero-one (tokens w <- weekday #\, (return w)) 0)
day <- date
time <- time-of-day
z <- zone
(return (date-helper day time z))))
(define read-rfc822 (make-reader date-time))
(define (date->rfc822 d)
(date->string d "~a, ~d ~b ~Y ~H:~M:~S ~z"))
(provide/contract
(read-rfc822 Reader/c)
(date->rfc822 (-> date? string?))
(make-fixed-digits (-> exact-nonnegative-integer?
exact-nonnegative-integer?
Parser/c))
(digit1-2 Parser/c)
(digit2 Parser/c)
(digit4 Parser/c)
(offset (->* (integer?)
(integer?)
integer?))
)