(.using [library [lux "*" ["@" target] [abstract [equivalence {"+" Equivalence}] [order {"+" Order}] [enum {"+" Enum}] [codec {"+" Codec}] [monad {"+" Monad do}]] [control [io {"+" IO io}] ["[0]" maybe] ["[0]" try] ["[0]" exception {"+" exception:}] ["<>" parser ["<[0]>" text {"+" Parser}]]] [data ["[0]" text ("[1]#[0]" monoid)]] [math [number ["i" int] ["f" frac]]] [type abstract]]] ["[0]" // {"+" Time} ["[0]" duration {"+" Duration}] ["[0]" year {"+" Year}] ["[0]" month {"+" Month}] ["[0]" day {"+" Day}] ["[0]" date {"+" Date}]]) (abstract: .public Instant Int (def: .public of_millis (-> Int Instant) (|>> :abstraction)) (def: .public millis (-> Instant Int) (|>> :representation)) (def: .public (span from to) (-> Instant Instant Duration) (duration.of_millis (i.- (:representation from) (:representation to)))) (def: .public (after duration instant) (-> Duration Instant Instant) (:abstraction (i.+ (duration.millis duration) (:representation instant)))) (def: .public (relative instant) (-> Instant Duration) (|> instant :representation duration.of_millis)) (def: .public (absolute offset) (-> Duration Instant) (|> offset duration.millis :abstraction)) (implementation: .public equivalence (Equivalence Instant) (def: (= param subject) (# i.equivalence = (:representation param) (:representation subject)))) (implementation: .public order (Order Instant) (def: &equivalence ..equivalence) (def: (< param subject) (# i.order < (:representation param) (:representation subject)))) (`` (implementation: .public enum (Enum Instant) (def: &order ..order) (~~ (template [] [(def: (|>> :representation (# i.enum ) :abstraction))] [succ] [pred] )))) ) (def: .public epoch Instant (..of_millis +0)) (def: millis_per_day (duration.ticks duration.milli_second duration.day)) (def: (date_time instant) (-> Instant [Date Duration]) (let [offset (..millis instant) bce? (i.< +0 offset) [days day_time] (if bce? (let [[days millis] (i./% ..millis_per_day offset)] (case millis +0 [days millis] _ [(-- days) (i.+ ..millis_per_day millis)])) (i./% ..millis_per_day offset))] [(date.of_days days) (duration.of_millis day_time)])) (template [ ] [(def: Text )] ["T" date_suffix] ["Z" time_suffix] ) (def: (clock_time duration) (-> Duration Time) (|> (if (# duration.order < duration.empty duration) (duration.merged duration.day duration) duration) duration.millis .nat //.of_millis try.trusted)) (def: (format instant) (-> Instant Text) (let [[date time] (..date_time instant) time (..clock_time time)] ($_ text#composite (# date.codec encoded date) ..date_suffix (# //.codec encoded time) ..time_suffix))) (def: parser (Parser Instant) (do [! <>.monad] [days (# ! each date.days date.parser) _ (.this ..date_suffix) time (# ! each //.millis //.parser) _ (.this ..time_suffix)] (in (|> (if (i.< +0 days) (|> duration.day (duration.up (.nat (i.* -1 days))) duration.inverse) (duration.up (.nat days) duration.day)) (duration.merged (duration.up time duration.milli_second)) ..absolute)))) (implementation: .public codec (Codec Text Instant) (def: encoded ..format) (def: decoded (.result ..parser))) (def: .public now (IO Instant) (io (..of_millis (for [@.old ("jvm invokestatic:java.lang.System:currentTimeMillis:") @.jvm (|> ("jvm member invoke static" [] "java.lang.System" "currentTimeMillis" []) ("jvm object cast") (: (Primitive "java.lang.Long")) (:as Int)) @.js (let [date ("js object new" ("js constant" "Date") [])] (|> ("js object do" "getTime" date []) (:as Frac) "lux f64 i64")) @.python (let [time ("python import" "time")] (|> ("python object do" "time" time) (:as Frac) (f.* +1,000.0) "lux f64 i64")) @.lua (|> ("lua constant" "os.time") "lua apply" (:as Int) (i.* +1,000)) @.ruby (let [% ("ruby constant" "Time") % ("ruby object do" "now" %)] (|> ("ruby object do" "to_f" %) (:as Frac) (f.* +1,000.0) "lux f64 i64")) @.php (|> ("php constant" "time") "php apply" (:as Int) (i.* +1,000)) @.scheme (|> ("scheme constant" "current-second") (:as Int) (i.* +1,000) ("scheme apply" ("scheme constant" "exact")) ("scheme apply" ("scheme constant" "truncate"))) @.common_lisp (|> ("common_lisp constant" "get-universal-time") "common_lisp apply" (:as Int) (i.* +1,000)) ])))) (template [ ] [(def: .public ( instant) (-> Instant ) (let [[date time] (..date_time instant)] (|> )))] [date Date (|>)] [time Time ..clock_time] ) (def: .public (day_of_week instant) (-> Instant Day) (let [offset (..relative instant) days (duration.ticks duration.day offset) day_time (duration.framed duration.day offset) days (if (and (duration.negative? offset) (not (duration.neutral? day_time))) (-- days) days) ... 1970/01/01 was a Thursday y1970m0d0 +4] (case (|> y1970m0d0 (i.+ days) (i.% +7) ... This is done to turn negative days into positive days. (i.+ +7) (i.% +7)) +0 {day.#Sunday} +1 {day.#Monday} +2 {day.#Tuesday} +3 {day.#Wednesday} +4 {day.#Thursday} +5 {day.#Friday} +6 {day.#Saturday} _ (undefined)))) (def: .public (of_date_time date time) (-> Date Time Instant) (|> (date.days date) (i.* (duration.millis duration.day)) (i.+ (.int (//.millis time))) ..of_millis))