iso-8601.ss
(module iso-8601 mzscheme
  
  ; This module contains the beginnings of a generic ISO 8601 date/time parser.
  ; The eventual aim is to have a procedure that turns any ISO 8601 string into
  ; some uniform data structure.
  
  ; This module:
  ;   - WILL support dates, times and date/time combinations;
  ;   - WILL support timezones;
  ;   - WILL support up to 9 optional fractional digits on seconds;
  ;   - WILL NOT support hyphen-prefixed abbreviations;
  ;   - WILL NOT support periods of time.
  
  (require (lib "pregexp.ss"))
  
  ; Calendar date formats:
  
  (define px:ccyy-mm-dd #px"^(\\d{4})-(\\d{2})-(\\d{2})$") ; 4 2 2
  (define px:ccyymmdd   #px"^(\\d{4})(\\d{2})(\\d{2})$")   ; 8
  (define px:ccyy-mm    #px"^(\\d{4})-(\\d{2})$")          ; 4 2
  (define px:ccyy       #px"^(\\d{4})$")                   ; 4
  (define px:cc         #px"^(\\d{2})$")                   ; 2
  
  (define px:yy-mm-dd   #px"^(\\d{2})-(\\d{2})-(\\d{2})$") ; 2 2 2
  (define px:yymmdd     #px"^(\\d{2})(\\d{2})(\\d{2})$")   ; 6

  ; Ordinal date formats:
  
  (define px:ccyy-ddd   #px"^(\\d{4})-(\\d{3})$")          ; 4 3
  (define px:ccyyddd    #px"^(\\d{4})(\\d{3})$")           ; 7
  
  (define px:yy-ddd     #px"^(\\d{2})-(\\d{3})$")          ; 2 3
  (define px:yyddd      #px"^(\\d{2})(\\d{3})$")           ; 5
  
  ; Week/day formats:
  
  (define px:ccyy-www-d #px"^(\\d{4})-W(\\d{2})-(\\d)$")   ; W 4 2 1
  (define px:ccyywwwd   #px"^(\\d{4})W(\\d{2})(\\d)$")     ; W 4 3
  (define px:ccyy-www   #px"^(\\d{4})-W(\\d{2})$")         ; W 4 2
  (define px:ccyywww    #px"^(\\d{4})W(\\d{2})$")          ; W 4 2
  
  (define px:yy-www-d   #px"^(\\d{2})-W(\\d{2})-(\\d)$")   ; W 2 2 1
  (define px:yywwwd     #px"^(\\d{2})W(\\d{2})(\\d)$")     ; W 2 3
  (define px:yy-www     #px"^(\\d{2})-W(\\d{2})$")         ; W 2 2
  (define px:yywww      #px"^(\\d{2})W(\\d{2})$")          ; W 2 2
  
  ; Local time of day:
  
  (define px:hh:mm:ss   #px"^(\\d{2}):(\\d{2}):(\\d{2})$") ; 2 2 2
  (define px:hhmmss     #px"^(\\d{2})(\\d{2})(\\d{2})$")   ; 6
  (define px:hh:mm      #px"^(\\d{2}):(\\d{2})$")          ; 2 2
  (define px:hhmm       #px"^(\\d{2})(\\d{2})$")           ; 4
  (define px:hh         #px"^(\\d{2})$")                   ; 2
  
  ; Utility procedures ---------------------------
  
  ;; read-digit : string integer -> (U integer #f)
  ;(define (read-digit str pos)
  ;  (let ([chr (string-ref str pos)])
  ;    (if (char-numeric? chr)
  ;        (char->integer chr)
  ;        #f)))
  
  ;; read-number : string integer integer -> (U integer #f)
  ;(define (read-number str start end)
  ;  (let loop ([accum 0] [pos start])
  ;    (if (< pos end)
  ;        (let ([digit (read-digit str pos)])
  ;          (loop (+ (* 10 accum) digit) (add1 pos)))
  ;        accum)))
  
  ;; read-fraction : string integer integer integer -> (U integer #f)
  ;(define (read-fraction input start end normalize-to)
  ;  ; First read the digits that are available:
  ;  (let loop ([accum 0] [pos start])
  ;    (if (< pos end)
  ;        (let ([digit (read-digit input pos)])
  ;          (loop (+ (* 10 accum) digit) (add1 pos)))
  ;        ; Normalize the result:
  ;        (let loop ([accum accum] [pos (- pos start)])
  ;          (if (< pos normalize-to)
  ;              (loop (* 10 accum) (add1 pos))
  ;              accum)))))
  
  ; Parsing dates --------------------------------
  
  ;; parse-normal-date : string (list-of integer) -> date
  ;(define (parse-normal-date str format)
  ;  (match format
  ;    [(list 4 2 2)
  ;     ]
  ;    [(list 8)
  ;     ]
  ;    [(list 4 2)
  ;     ]
  ;    [(list 4)
  ;     ]
  ;    [(list 2)
  ;     ]
  ;    [(list 2 2 2)
  ;     ]
  ;    [(list 6)
  ;     ]
  ;    [(list 4 3)
  ;     ]
  ;    [(list 7)
  ;     ]))
  
  ;(define parse-date
  ;  (case-lambda
  ;    [(str) (parse-date str (date-format str))]
  ;    [(str format)
  ;     (case (car format)
  ;       [(#\D) ; Normal date format
  ;        (parse-normal-date str (cdr format))]
  ;       [(#\W) ; Week date format
  ;        (parse-week-date str (cdr format))])]))

  )