(.using [library [lux "*" [abstract [equivalence {"+" Equivalence}] [order {"+" Order}] [enum {"+" Enum}] [codec {"+" Codec}] [monad {"+" Monad do}]] [control [pipe {"+" case>}] ["[0]" try {"+" Try}] ["[0]" exception {"+" exception:}] ["<>" parser ["<[0]>" text {"+" Parser}]]] [data ["[0]" text ("[1]#[0]" monoid)]] [math [number ["n" nat ("[1]#[0]" decimal)]]] [type abstract]]] [/ ["[0]" duration {"+" Duration}]]) (template [ ] [(def: .public Nat (.nat (duration.ticks )))] [milli_seconds duration.milli_second duration.second] [seconds duration.second duration.minute] [minutes duration.minute duration.hour] [hours duration.hour duration.day] ) (def: limit Nat (.nat (duration.millis duration.day))) (exception: .public (time_exceeds_a_day [time Nat]) (exception.report ["Time (in milli-seconds)" (n#encoded time)] ["Maximum (in milli-seconds)" (n#encoded (-- limit))])) (def: separator ":") (def: section_parser (Parser Nat) (<>.codec n.decimal (.exactly 2 .decimal))) (def: millis_parser (Parser Nat) (<>.either (|> (.at_most 3 .decimal) (<>.codec n.decimal) (<>.after (.this "."))) (# <>.monad in 0))) (template [ ] [(exception: .public ( [value Nat]) (exception.report ["Value" (n#encoded value)] ["Minimum" (n#encoded 0)] ["Maximum" (n#encoded (-- ))])) (def: (Parser Nat) (do <>.monad [value ] (if (n.< value) (in value) (<>.lifted (exception.except [value])))))] [..hours hour_parser invalid_hour ..section_parser] [..minutes minute_parser invalid_minute ..section_parser] [..seconds second_parser invalid_second ..section_parser] ) (abstract: .public Time Nat (def: .public midnight Time (:abstraction 0)) (def: .public (of_millis milli_seconds) (-> Nat (Try Time)) (if (n.< ..limit milli_seconds) {try.#Success (:abstraction milli_seconds)} (exception.except ..time_exceeds_a_day [milli_seconds]))) (def: .public millis (-> Time Nat) (|>> :representation)) (implementation: .public equivalence (Equivalence Time) (def: (= param subject) (n.= (:representation param) (:representation subject)))) (implementation: .public order (Order Time) (def: &equivalence ..equivalence) (def: (< param subject) (n.< (:representation param) (:representation subject)))) (`` (implementation: .public enum (Enum Time) (def: &order ..order) (def: succ (|>> :representation ++ (n.% ..limit) :abstraction)) (def: pred (|>> :representation (case> 0 ..limit millis millis) -- :abstraction)))) (def: .public parser (Parser Time) (let [millis (: (-> Duration Nat) (|>> duration.millis .nat)) hour (millis duration.hour) minute (millis duration.minute) second (millis duration.second) millis (millis duration.milli_second)] (do [! <>.monad] [utc_hour ..hour_parser _ (.this ..separator) utc_minute ..minute_parser _ (.this ..separator) utc_second ..second_parser utc_millis ..millis_parser] (in (:abstraction ($_ n.+ (n.* utc_hour hour) (n.* utc_minute minute) (n.* utc_second second) (n.* utc_millis millis))))))) ) (def: (padded value) (-> Nat Text) (if (n.< 10 value) (text#composite "0" (n#encoded value)) (n#encoded value))) (def: (positive space duration) (-> Duration Duration Duration) (if (duration.negative? duration) (duration.merged space duration) duration)) (def: (millis_format millis) (-> Nat Text) (cond (n.= 0 millis) "" (n.< 10 millis) ($_ text#composite ".00" (n#encoded millis)) (n.< 100 millis) ($_ text#composite ".0" (n#encoded millis)) ... (n.< 1,000 millis) ($_ text#composite "." (n#encoded millis)))) (type: .public Clock (Record [#hour Nat #minute Nat #second Nat #milli_second Nat])) (def: .public (clock time) (-> Time Clock) (let [time (|> time ..millis .int duration.of_millis) [hours time] [(duration.ticks duration.hour time) (duration.framed duration.hour time)] [minutes time] [(duration.ticks duration.minute time) (duration.framed duration.minute time)] [seconds millis] [(duration.ticks duration.second time) (duration.framed duration.second time)]] [#hour (.nat hours) #minute (.nat minutes) #second (.nat seconds) #milli_second (|> millis (..positive duration.second) duration.millis .nat)])) (def: .public (time clock) (-> Clock (Try Time)) (|> ($_ duration.merged (duration.up (value@ #hour clock) duration.hour) (duration.up (value@ #minute clock) duration.minute) (duration.up (value@ #second clock) duration.second) (duration.of_millis (.int (value@ #milli_second clock)))) duration.millis .nat ..of_millis)) (def: (format time) (-> Time Text) (let [(^open "_[0]") (..clock time)] ($_ text#composite (..padded _#hour) ..separator (..padded _#minute) ..separator (..padded _#second) (..millis_format _#milli_second)))) (implementation: .public codec (Codec Text Time) (def: encoded ..format) (def: decoded (.result ..parser)))