From c00d94fa5c9e6b3b8d25f49d0f2d341ff61fa35b Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Fri, 19 Aug 2022 18:54:31 -0400 Subject: Added support for time-zoned time. --- stdlib/source/library/lux/meta/macro/expansion.lux | 10 +- stdlib/source/library/lux/world/time/instant.lux | 4 +- stdlib/source/library/lux/world/time/solar.lux | 214 +++++++++++++++++++++ stdlib/source/test/lux/meta/macro.lux | 2 + stdlib/source/test/lux/meta/macro/expansion.lux | 89 +++++++++ stdlib/source/test/lux/world/time.lux | 4 +- stdlib/source/test/lux/world/time/solar.lux | 61 ++++++ 7 files changed, 377 insertions(+), 7 deletions(-) create mode 100644 stdlib/source/library/lux/world/time/solar.lux create mode 100644 stdlib/source/test/lux/meta/macro/expansion.lux create mode 100644 stdlib/source/test/lux/world/time/solar.lux (limited to 'stdlib') diff --git a/stdlib/source/library/lux/meta/macro/expansion.lux b/stdlib/source/library/lux/meta/macro/expansion.lux index 62c4e6905..37bc6bf4c 100644 --- a/stdlib/source/library/lux/meta/macro/expansion.lux +++ b/stdlib/source/library/lux/meta/macro/expansion.lux @@ -123,10 +123,12 @@ (do ///.monad [location ///.location output ( token) - .let [_ ("lux io log" (all text#composite (symbol#encoded macro_name) " " (location.format location))) - _ (list#each (|>> code.format "lux io log") - output) - _ ("lux io log" "")]] + .let [_ ("lux io log" (all text#composite + (symbol#encoded macro_name) " " (location.format location) + (|> output + (list#each (|>> code.format (all text#composite text.\n text.\t))) + text.together) + text.\n))]] (in (if omit? (list) output))) diff --git a/stdlib/source/library/lux/world/time/instant.lux b/stdlib/source/library/lux/world/time/instant.lux index db4d3ffa6..b07bd9652 100644 --- a/stdlib/source/library/lux/world/time/instant.lux +++ b/stdlib/source/library/lux/world/time/instant.lux @@ -127,7 +127,7 @@ //.of_millis try.trusted)) -(def (format instant) +(def .public (format instant) (-> Instant Text) (let [[date time] (..date_time instant) time (..clock_time time)] @@ -135,7 +135,7 @@ (at date.codec encoded date) ..date_suffix (at //.codec encoded time) ..time_suffix))) -(def parser +(def .public parser (Parser Instant) (do [! <>.monad] [days (at ! each date.days date.parser) diff --git a/stdlib/source/library/lux/world/time/solar.lux b/stdlib/source/library/lux/world/time/solar.lux new file mode 100644 index 000000000..0eff5ee00 --- /dev/null +++ b/stdlib/source/library/lux/world/time/solar.lux @@ -0,0 +1,214 @@ +(.require + [library + [lux (.except) + ["[0]" ffi] + [abstract + [monad (.only do)]] + [control + ["[0]" io (.only IO) (.use "[1]#[0]" functor)]] + [data + [text + ["%" \\format (.only Format)]]] + [math + [number + ["i" int] + ["f" frac]]] + [meta + ["@" target] + [type + [primitive (.except)]]]]] + ["[0]" // + ["[1]" instant] + ["[0]" duration (.only Duration)]]) + +(type .public Zone + Text) + +(primitive .public Instant + (Record + [#utc //.Instant + #zone Zone + #offset Duration]) + + (with_template [ ] + [(def .public + (-> Instant ) + (|>> representation (the )))] + + [universal #utc //.Instant] + [zone #zone Zone] + [offset #offset Duration] + ) + + (for @.jvm (these (ffi.import java/lang/String + "[1]::[0]") + + (ffi.import java/util/TimeZone + "[1]::[0]" + ("static" getDefault [] "io" java/util/TimeZone) + (getID [] java/lang/String) + (getOffset [long] int)) + ) + @.js (these (ffi.import ResolvedOptions + "[1]::[0]" + (timeZone Text)) + + (ffi.import DateTimeFormat + "[1]::[0]" + (resolvedOptions [] ResolvedOptions)) + + (ffi.import Intl + "[1]::[0]" + ("static" DateTimeFormat [] "io" DateTimeFormat)) + + (ffi.import Date + "[1]::[0]" + (new []) + (getTimezoneOffset [] ffi.Number)) + ) + @.lua (these (ffi.import os/date + "[1]::[0]" + (hour Int) + (min Int)) + + (ffi.import os + "[1]::[0]" + ("static" date [Text] "io" os/date))) + @.python (these (ffi.import datetime/timedelta + "[1]::[0]" + (new [Int])) + + (ffi.import datetime/timezone + "[1]::[0]" + (new [datetime/timedelta])) + + (ffi.import datetime/utcoffset + "[1]::[0]" + (total_seconds [] Frac)) + + (ffi.import datetime/datetime + "[1]::[0]" + ("static" now [datetime/timezone] "io" datetime/datetime) + (utcoffset [] datetime/utcoffset) + (astimezone [] datetime/datetime) + (tzinfo datetime/timezone) + (tzname [] Text)) + ) + @.ruby (these (ffi.import time/Time + "[1]::[0]" + ("static" now [] "io" time/Time) + (zone Text) + ("static" zone_offset [Text] "io" Int))) + ) + + (def .public local_zone + (IO Zone) + (do io.monad + [_ (in [])] + (for @.jvm (|> (java/util/TimeZone::getDefault) + (io#each (|>> java/util/TimeZone::getID + ffi.of_string))) + @.js (|> (Intl::DateTimeFormat []) + (io#each (|>> DateTimeFormat::resolvedOptions + ResolvedOptions::timeZone))) + @.lua (in "") + @.python (|> (datetime/timedelta::new +0) + datetime/timezone::new + datetime/datetime::now + (io#each (|>> datetime/datetime::astimezone + datetime/datetime::tzname))) + @.ruby (|> (time/Time::now []) + (io#each time/Time::zone)) + ))) + + (def .public now + (IO Instant) + (do [! io.monad] + [_ (in [])] + (for @.jvm (do ! + [zone (java/util/TimeZone::getDefault) + utc //.now] + (in (abstraction + [#utc utc + #zone (ffi.of_string (java/util/TimeZone::getID zone)) + #offset (|> zone + (java/util/TimeZone::getOffset (|> utc //.millis ffi.as_long)) + ffi.of_int + duration.of_millis)]))) + @.js (do ! + [zone ..local_zone + utc //.now] + (in (abstraction + [#utc utc + #zone zone + #offset (|> (Date::new []) + Date::getTimezoneOffset + f.int + (i.* -60,000) + duration.of_millis)]))) + @.lua (do ! + [zone ..local_zone + univeral (os::date ["*t"]) + solar (os::date ["!*t"]) + utc //.now] + (in (abstraction + [#utc utc + #zone zone + #offset (|> (i.- (os/date::hour solar) + (os/date::hour univeral)) + (i.* +60) + (i.+ (i.- (os/date::min solar) + (os/date::min univeral))) + (i.* +60,000) + duration.of_millis)]))) + @.python (do ! + [tz_now (|> (datetime/timedelta::new +0) + datetime/timezone::new + datetime/datetime::now + (at ! each datetime/datetime::astimezone)) + offset (|> tz_now + datetime/datetime::tzinfo + datetime/datetime::now + (at ! each (|>> datetime/datetime::utcoffset + datetime/utcoffset::total_seconds + f.int + (i.* +1000) + duration.of_millis))) + utc //.now] + (in (abstraction + [#utc utc + #zone (datetime/datetime::tzname tz_now) + #offset offset]))) + @.ruby (do ! + [zone ..local_zone + seconds (time/Time::zone_offset [zone]) + utc //.now] + (in (abstraction + [#utc utc + #zone zone + #offset (duration.of_millis (i.* +1000 seconds))]))) + ))) + + (def (padded it) + (-> Int Text) + (if (i.< +10 it) + (%.format "0" (%.nat (.nat it))) + (%.nat (.nat it)))) + + (def (sign it) + (-> Int Text) + (if (i.< +0 it) + "-" + "+")) + + (def .public (format it) + (Format Instant) + (let [it (representation it)] + (%.format (//.format (//.after (the #offset it) (the #utc it))) + (let [hours (|> (the #offset it) + (duration.ticks duration.hour)) + minutes (|> (the #offset it) + (duration.framed duration.hour) + (duration.ticks duration.minute))] + (%.format (sign hours) (padded (i.abs hours)) ":" (padded minutes)))))) + ) diff --git a/stdlib/source/test/lux/meta/macro.lux b/stdlib/source/test/lux/meta/macro.lux index 270040d6d..d3bc360d4 100644 --- a/stdlib/source/test/lux/meta/macro.lux +++ b/stdlib/source/test/lux/meta/macro.lux @@ -31,6 +31,7 @@ ["[0]" expansion]]] ["[0]" / ["[1][0]" context] + ["[1][0]" expansion] ["[1][0]" local] ["[1][0]" syntax] ["[1][0]" template] @@ -250,6 +251,7 @@ ..test|expansion /context.test + /expansion.test /local.test /syntax.test /template.test diff --git a/stdlib/source/test/lux/meta/macro/expansion.lux b/stdlib/source/test/lux/meta/macro/expansion.lux new file mode 100644 index 000000000..fb552a8fe --- /dev/null +++ b/stdlib/source/test/lux/meta/macro/expansion.lux @@ -0,0 +1,89 @@ +(.require + [library + [lux (.except) + [abstract + [monad (.only do)]] + [control + ["[0]" try] + ["[0]" exception]] + [data + ["[0]" text (.only) + ["%" \\format]] + [collection + ["[0]" list]]] + [math + [number + ["n" nat]]] + ["[0]" meta (.only) + ["[0]" static] + ["[0]" code (.use "[1]#[0]" equivalence) + ["<[1]>" \\parser]] + [macro + [syntax (.only syntax)]]] + [test + ["_" property (.only Test)]]]] + [\\library + ["[0]" /]]) + +(use "code_list#[0]" (list.equivalence code.equivalence)) + +(def dup + (syntax (_ [times .nat + what .any]) + (when times + 0 (in (list what)) + _ (let [it (` (..dup (, (code.nat (-- times))) (, what)))] + (in (list it it)))))) + +(def .public test + Test + (<| (_.covering /._) + (`` (all _.and + (,, (with_template [ <0> <1>] + [(_.coverage [] + (and (<| static.expansion + (do meta.monad + [it ( (` (..dup 0 )))] + (in (list (code.bit (code_list#= <0> + it)))))) + (<| static.expansion + (do meta.monad + [it ( (` (..dup 1 )))] + (in (list (code.bit (code_list#= <1> + it))))))))] + + [/.single [] (list (` [])) (list (` (..dup 0 [])) (` (..dup 0 [])))] + [/.complete [] (list (` [])) (list (` []) (` []))] + [/.total (..dup 0 []) (list (` [])) (list (` []) (` []))] + )) + (_.coverage [/.one] + (and (<| static.expansion + (do meta.monad + [it (/.one (` (..dup 0 [])))] + (in (list (code.bit (code#= (` []) + it)))))) + (<| static.expansion + (do meta.monad + [it (meta.try (/.one (` (..dup 1 []))))] + (in (list (code.bit (when it + {try.#Failure _} + true + + {try.#Success _} + false)))))))) + (_.coverage [/.log_single!] + (exec + (,, (/.log_single! "omit" (..dup 0 []))) + (,, (/.log_single! (..dup 0 []))) + true)) + (_.coverage [/.log_complete!] + (exec + (,, (/.log_complete! "omit" (..dup 1 []))) + (,, (/.log_complete! (..dup 1 []))) + true)) + (_.coverage [/.log_total!] + (exec + (,, (/.log_total! "omit" (..dup 1 (..dup 0 [])))) + (,, (/.log_total! (..dup 1 (..dup 0 [])))) + true)) + )))) diff --git a/stdlib/source/test/lux/world/time.lux b/stdlib/source/test/lux/world/time.lux index 4ca58afca..68e6e478c 100644 --- a/stdlib/source/test/lux/world/time.lux +++ b/stdlib/source/test/lux/world/time.lux @@ -28,7 +28,8 @@ ["[1][0]" duration] ["[1][0]" instant] ["[1][0]" month] - ["[1][0]" year]] + ["[1][0]" year] + ["[1][0]" solar]] [\\library ["[0]" / (.only) ["[0]" duration]]]) @@ -155,4 +156,5 @@ /instant.test /month.test /year.test + /solar.test ))))) diff --git a/stdlib/source/test/lux/world/time/solar.lux b/stdlib/source/test/lux/world/time/solar.lux new file mode 100644 index 000000000..f1da38370 --- /dev/null +++ b/stdlib/source/test/lux/world/time/solar.lux @@ -0,0 +1,61 @@ +(.require + [library + [lux (.except) + [abstract + [monad (.only do)] + [\\specification + ["$[0]" equivalence] + ["$[0]" order] + ["$[0]" codec]]] + [control + ["[0]" try] + ["[0]" exception] + ["[0]" io]] + [data + ["[0]" bit (.use "[1]#[0]" equivalence)] + ["[0]" text (.use "[1]#[0]" equivalence) + ["%" \\format (.only format)]]] + [math + ["[0]" random (.only Random)] + [number + ["n" nat] + ["i" int]]] + [meta + ["@" target]] + [test + ["_" property (.only Test)]]]] + [\\library + ["[0]" / (.only) + [// + ["[0]" duration] + ["[0]" instant]]]]) + +(def .public test + Test + (<| (_.covering /._) + (_.for [/.Instant]) + (do random.monad + [_ (in [])]) + (all _.and + (_.for [/.Zone] + (_.coverage [/.local_zone] + (io.run! (do io.monad + [zone /.local_zone] + (in (for @.lua + ... Lua doesn't natively support getting the time-zone ID. + (text.empty? zone) + + ... else + (not (text.empty? zone)))))))) + (_.coverage [/.now /.zone] + (io.run! (do io.monad + [zone /.local_zone + it /.now] + (in (text#= zone (/.zone it)))))) + (_.coverage [/.format /.universal /.offset] + (io.run! (do io.monad + [zone /.local_zone + it /.now] + (in (text.starts_with? (instant.format (instant.after (/.offset it) (/.universal it))) + (/.format it)))))) + ))) -- cgit v1.2.3