(.using [library [lux (.full) [abstract [monad (.only do)] [equivalence (.only Equivalence)]] [control ["[0]" try (.only Try)] ["[0]" exception (.only exception:)] ["<>" parser ["<[0]>" text (.only Parser)]]] [data [text ["%" format]]] [math [number ["n" nat] ["i" int]]] [time ["[0]" date ("[1]#[0]" equivalence)] ["[0]" year] ["[0]" month]] [type [primitive (.full)]]]]) (def: .public (pad value) (-> Nat Text) (if (n.< 10 value) (%.format "0" (%.nat value)) (%.nat value))) (def: min_year +1,000) (def: max_year +9,999) (exception: .public (year_is_out_of_range [year year.Year]) (exception.report "Minimum" (%.int ..min_year) "Maximum" (%.int ..max_year) "Year" (%.int (year.value year)))) (primitive: .public Date date.Date (def: .public epoch Date (abstraction date.epoch)) (def: .public (date raw) (-> date.Date (Try Date)) (let [year (|> raw date.year year.value)] (if (or (i.< ..min_year year) (i.> ..max_year year)) (exception.except ..year_is_out_of_range [(date.year raw)]) {try.#Success (abstraction raw)}))) (def: .public value (-> Date date.Date) (|>> representation)) (implementation: .public equivalence (Equivalence Date) (def: (= reference subject) (date#= (representation reference) (representation subject)))) (def: .public (format value) (%.Format Date) (%.format (|> value representation date.year year.value .nat %.nat) (|> value representation date.month month.number ..pad) (|> value representation date.day_of_month ..pad))) (def: .public parser (Parser Date) (do <>.monad [year (<>.codec n.decimal (.exactly 4 .decimal)) year (<>.lifted (year.year (.int year))) month (<>.codec n.decimal (.exactly 2 .decimal)) month (<>.lifted (month.by_number month)) day_of_month (<>.codec n.decimal (.exactly 2 .decimal)) date (<>.lifted (date.date year month day_of_month))] (in (abstraction date)))))