aboutsummaryrefslogtreecommitdiff
path: root/stdlib
diff options
context:
space:
mode:
authorEduardo Julian2022-08-19 18:54:31 -0400
committerEduardo Julian2022-08-19 18:54:31 -0400
commitc00d94fa5c9e6b3b8d25f49d0f2d341ff61fa35b (patch)
tree4670b0891f851fe5edeb26734e5d010b984f7a45 /stdlib
parentff6914a0e3bd85b2ae49b5bee6028dff8a47568a (diff)
Added support for time-zoned time.
Diffstat (limited to 'stdlib')
-rw-r--r--stdlib/source/library/lux/meta/macro/expansion.lux10
-rw-r--r--stdlib/source/library/lux/world/time/instant.lux4
-rw-r--r--stdlib/source/library/lux/world/time/solar.lux214
-rw-r--r--stdlib/source/test/lux/meta/macro.lux2
-rw-r--r--stdlib/source/test/lux/meta/macro/expansion.lux89
-rw-r--r--stdlib/source/test/lux/world/time.lux4
-rw-r--r--stdlib/source/test/lux/world/time/solar.lux61
7 files changed, 377 insertions, 7 deletions
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 (<func> 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 [<name> <slot> <type>]
+ [(def .public <name>
+ (-> Instant <type>)
+ (|>> representation (the <slot>)))]
+
+ [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 <code>.nat
+ what <code>.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 [<expansion> <inner> <0> <1>]
+ [(_.coverage [<expansion>]
+ (and (<| static.expansion
+ (do meta.monad
+ [it (<expansion> (` (..dup 0 <inner>)))]
+ (in (list (code.bit (code_list#= <0>
+ it))))))
+ (<| static.expansion
+ (do meta.monad
+ [it (<expansion> (` (..dup 1 <inner>)))]
+ (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))))))
+ )))