(defn
parse-and-validate-timestamp
[s]
(let
[[_
years
months
days
hours
minutes
seconds
fraction
offset-sign
offset-hours
offset-minutes
:as
v]
(re-matches timestamp-regex s)]
(if-not
v
(throw (js/Error. (str "Unrecognized date/time syntax: " s)))
(let
[years
(parse-int years)
months
(or (parse-int months) 1)
days
(or (parse-int days) 1)
hours
(or (parse-int hours) 0)
minutes
(or (parse-int minutes) 0)
seconds
(or (parse-int seconds) 0)
fraction
(or (parse-int (zero-fill-right-and-truncate fraction 3)) 0)
offset-sign
(if (= offset-sign "-") -1 1)
offset-hours
(or (parse-int offset-hours) 0)
offset-minutes
(or (parse-int offset-minutes) 0)
offset
(* offset-sign (+ (* offset-hours 60) offset-minutes))]
[years
(check 1 months 12 "timestamp month field must be in range 1..12")
(check
1
days
(days-in-month months (leap-year? years))
"timestamp day field must be in range 1..last day in month")
(check 0 hours 23 "timestamp hour field must be in range 0..23")
(check
0
minutes
59
"timestamp minute field must be in range 0..59")
(check
0
seconds
(if (= minutes 59) 60 59)
"timestamp second field must be in range 0..60")
(check
0
fraction
999
"timestamp millisecond field must be in range 0..999")
offset]))))