mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Builtin Date_Time, Time_Of_Day, Zone (#3658)
* Builtin Date_Time, Time_Of_Day, Zone Improved polyglot support for Date_Time (formerly Time), Time_Of_Day and Zone. This follows the pattern introduced for Enso Date. Minor caveat - in tests for Date, had to bend a lot for JS Date to pass. This is because JS Date is not really only a Date, but also a Time and Timezone, previously we just didn't consider the latter. Also, JS Date does not deal well with setting timezones so the trick I used is to first call foreign function returning a polyglot JS Date, which is converted to ZonedDateTime and only then set the correct timezone. That way none of the existing tests had to be changes or special cased. Additionally, JS deals with milliseconds rather than nanoseconds so there is loss in precision, as noted in Time_Spec. * Add tests for Java's LocalTime * changelog * Make date formatters in table happy * PR review, add more tests for zone * More tests and fixed a bug in column reader Column reader didn't take into account timezone but that was a mistake since then it wouldn't map to Enso's Date_Time. Added tests that check it now. * remove redundant conversion * Update distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time.enso Co-authored-by: Radosław Waśko <radoslaw.wasko@enso.org> * First round of addressing PR review * don't leak java exceptions in Zone * Move Date_Time to top-level module * PR review Co-authored-by: Radosław Waśko <radoslaw.wasko@enso.org> Co-authored-by: Jaroslav Tulach <jaroslav.tulach@enso.org>
This commit is contained in:
parent
4b9c91626e
commit
d87a32d019
@ -327,6 +327,8 @@
|
||||
- [Support importing module methods][3633]
|
||||
- [Support Autosave for open buffers][3637]
|
||||
- [Support pattern matching on constants][3641]
|
||||
- [Builtin Date_Time, Time_Of_Day and Zone types for better polyglot
|
||||
support][3658]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
@ -369,6 +371,7 @@
|
||||
[3633]: https://github.com/enso-org/enso/pull/3633
|
||||
[3637]: https://github.com/enso-org/enso/pull/3637
|
||||
[3633]: https://github.com/enso-org/enso/pull/3641
|
||||
[3658]: https://github.com/enso-org/enso/pull/3658
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
|
@ -1,481 +1,9 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Time_Of_Day
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import java.time.ZonedDateTime
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
## ALIAS Current Time
|
||||
|
||||
Obtains the current date-time from the system clock in the system timezone.
|
||||
|
||||
> Example
|
||||
Get the current time
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_now = Time.now
|
||||
now : Time
|
||||
now = Time ZonedDateTime.now
|
||||
|
||||
## Obtains an instance of `Time` from a year, month, day, hour, minute,
|
||||
second, nanosecond and timezone.
|
||||
|
||||
Arguments:
|
||||
- month: the month-of-year to represent, from 1 (January) to 12 (December)
|
||||
- day: the day-of-month to represent, from 1 to 31 and must be valid for the
|
||||
year and month
|
||||
- hour: the hour-of-day to represent, from 0 to 23
|
||||
- minute: the minute-of-hour to represent, from 0 to 59
|
||||
- second: the second-of-minute to represent, from 0 to 59
|
||||
- nanosecond: the nano-of-second to represent, from 0 to 999,999,999
|
||||
- zone: the timezone
|
||||
|
||||
Returns a `Time_Error` if the provided time cannot be represented.
|
||||
|
||||
> Example
|
||||
Create a new zoned date time at Unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_new = Time.new 1970 (zone = Zone.utc)
|
||||
|
||||
> Example
|
||||
Get the 5 August 1986 at midnight.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_new = Time.new 1986 8 5
|
||||
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time ! Time_Error
|
||||
new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) =
|
||||
Panic.catch_java Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) java_exception->
|
||||
Error.throw (Time_Error java_exception.getMessage)
|
||||
|
||||
## ALIAS Time from Text
|
||||
|
||||
Obtains an instance of `Time` from a text such as
|
||||
"2007-12-03T10:15:30+01:00 Europe/Paris".
|
||||
|
||||
Arguments:
|
||||
- text: The text representing the time to be parsed.
|
||||
- pattern: The pattern to use for parsing the input text.
|
||||
- locale: The locale in which the pattern should be interpreted.
|
||||
|
||||
? Pattern Syntax
|
||||
For the list of accepted symbols in pattern refer to `Time.format` doc.
|
||||
|
||||
? Default Time Format
|
||||
The text must represent a valid date-time and is parsed using the ISO-8601
|
||||
extended offset date-time format to add the timezone. The section in square
|
||||
brackets is not part of the ISO-8601 standard. The format consists of:
|
||||
|
||||
- The ISO offset date time.
|
||||
- If the zone ID is not available or is a zone offset then the format is
|
||||
complete.
|
||||
- An open square bracket '['.
|
||||
- The zone ID. This is not part of the ISO-8601 standard. Parsing is case
|
||||
sensitive.
|
||||
- A close square bracket ']'.
|
||||
|
||||
This method will return a `Time_Error` if the provided time cannot be parsed
|
||||
using the above format.
|
||||
|
||||
> Example
|
||||
Parse UTC time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Time.parse "2020-10-01T04:11:12Z"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Time.parse "2020-10-01T04:11:12-04:00"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time specifying New York timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Time.parse "2020-10-01T04:11:12-04:00[America/New_York]"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time with nanoseconds.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Time.parse "2020-10-01T04:11:12.177528-04:00"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Time.parse "2020-10-01" . catch Time_Error (_->Time.now)
|
||||
|
||||
> Example
|
||||
Parse "2020-05-06 04:30:20" as Time
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date.parse "2020-05-06 04:30:20" "yyyy-MM-dd HH:mm:ss"
|
||||
|
||||
> Example
|
||||
Parse "06 of May 2020 at 04:30AM" as Time
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse =
|
||||
Date.parse "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
parse : Text -> Text | Nothing -> Locale -> Time ! Time_Error
|
||||
parse text pattern=Nothing locale=Locale.default =
|
||||
result = Panic.recover Any <| case pattern of
|
||||
Nothing -> Time_Utils.parse_time text
|
||||
Text -> Time_Utils.parse_time_format text pattern locale.java_locale
|
||||
_ -> Panic.throw (Time_Error "An invalid pattern was provided.")
|
||||
Time result . map_error <| case _ of
|
||||
Polyglot_Error err -> Time_Error err.getMessage
|
||||
x -> x
|
||||
|
||||
type Time
|
||||
|
||||
## PRIVATE
|
||||
|
||||
A date-time with a timezone in the ISO-8601 calendar system, such as
|
||||
"2007-12-03T10:15:30+01:00 Europe/Paris".
|
||||
|
||||
Arguments:
|
||||
- internal_zoned_date_time: The internal repreentation of the time.
|
||||
|
||||
Time is a representation of a date-time with a timezone. This class
|
||||
stores all date and time fields, to a precision of nanoseconds, and a
|
||||
timezone, with a zone offset used to handle ambiguous local
|
||||
date-times.
|
||||
|
||||
For example, the value "2nd October 2007 at 13:45.30.123456789 +02:00 in
|
||||
the Europe/Paris timezone" can be stored as `Time`.
|
||||
type Time internal_zoned_date_time
|
||||
|
||||
## Get the year portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current year.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_year = Time.now.year
|
||||
year : Integer
|
||||
year self = self . internal_zoned_date_time . getYear
|
||||
|
||||
## Get the month portion of the time as a number from 1 to 12.
|
||||
|
||||
> Example
|
||||
Get the current month.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_month = Time.now.month
|
||||
month : Integer
|
||||
month self = self . internal_zoned_date_time . getMonthValue
|
||||
|
||||
## Get the day portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current day.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_day = Time.now.day
|
||||
day : Integer
|
||||
day self = self . internal_zoned_date_time . getDayOfMonth
|
||||
|
||||
## Get the hour portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current hour.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_hour = Time.now.hour
|
||||
hour : Integer
|
||||
hour self = self . internal_zoned_date_time . getHour
|
||||
|
||||
## Get the minute portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current minute.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_minute = Time.now.minute
|
||||
minute : Integer
|
||||
minute self = self . internal_zoned_date_time . getMinute
|
||||
|
||||
## Get the second portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current second.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_second = Time.now.second
|
||||
second : Integer
|
||||
second self = self . internal_zoned_date_time . getSecond
|
||||
|
||||
## Get the nanosecond portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current nanosecond.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_nanosecond = Time.now.nanosecond
|
||||
nanosecond : Integer
|
||||
nanosecond self = self . internal_zoned_date_time . getNano
|
||||
|
||||
## Get the timezone for the time.
|
||||
|
||||
> Example
|
||||
Get the current timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_zone = Time.now.zone
|
||||
zone : Zone
|
||||
zone self = Zone.Zone self.internal_zoned_date_time.getZone
|
||||
|
||||
## Return the number of seconds from the Unix epoch.
|
||||
|
||||
> Example
|
||||
Get the current number of seconds from the Unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_epoch = Time.now.to_epoch_seconds
|
||||
to_epoch_seconds : Integer
|
||||
to_epoch_seconds self = self . internal_zoned_date_time . toEpochSecond
|
||||
|
||||
## Return the number of milliseconds from the Unix epoch.
|
||||
|
||||
> Example
|
||||
Get the current number of milliseconds from the unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_epoch = Time.now.to_epoch_milliseconds
|
||||
to_epoch_milliseconds : Integer
|
||||
to_epoch_milliseconds self = self . internal_zoned_date_time . toInstant . toEpochMilli
|
||||
|
||||
## Convert this point in time to time of day, discarding the time zone
|
||||
information.
|
||||
|
||||
> Example
|
||||
Convert the current time to a time of day.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_time_of_day = Time.now.time_of_day
|
||||
time_of_day : Time_Of_Day
|
||||
time_of_day self = Time_Of_Day.Time_Of_Day self.internal_zoned_date_time.toLocalTime
|
||||
|
||||
## ALIAS Time to Date
|
||||
|
||||
Convert this point in time to date, discarding the time of day
|
||||
information.
|
||||
|
||||
> Example
|
||||
Convert the current time to a date.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_date = Time.now.date
|
||||
date : Date
|
||||
date self = self.internal_zoned_date_time.toLocalDate
|
||||
|
||||
## ALIAS Change Time Zone
|
||||
|
||||
Convert the time instant to the same instant in the provided time zone.
|
||||
|
||||
Arguments:
|
||||
- zone: The time-zone to convert the time instant into.
|
||||
|
||||
> Example
|
||||
Convert time instance to -04:00 timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
exaomple_at_zone = Time.new 2020 . at_zone (Zone.new -4)
|
||||
at_zone : Zone -> Time
|
||||
at_zone self zone = Time (self.internal_zoned_date_time . withZoneSameInstant zone.internal_zone_id)
|
||||
|
||||
## Add the specified amount of time to this instant to produce a new instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to add to this instant.
|
||||
|
||||
> Example
|
||||
Add 15 years and 3 hours to a zoned date time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_plus = Time.new 2020 + 15.years + 3.hours
|
||||
+ : Duration -> Time
|
||||
+ self amount = Time (self . internal_zoned_date_time . plus amount.internal_period . plus amount.internal_duration)
|
||||
|
||||
## Subtract the specified amount of time from this instant to get a new
|
||||
instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to subtract from this instant.
|
||||
|
||||
> Example
|
||||
Subtract 1 year and 9 months from a zoned date time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_minus = Time.new 2020 - 1.year - 9.months
|
||||
- : Duration -> Time
|
||||
- self amount = Time (self . internal_zoned_date_time . minus amount.internal_period . minus amount.internal_duration)
|
||||
|
||||
## Convert this time to text using the default formatter.
|
||||
|
||||
> Example
|
||||
Convert the current time to text.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_to_text = Time.now.to_text
|
||||
to_text : Text
|
||||
to_text self = Time_Utils.default_time_formatter . format self.internal_zoned_date_time
|
||||
|
||||
## Convert the time to JSON.
|
||||
|
||||
> Example
|
||||
Convert the current time to JSON.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_to_json = Time.now.to_json
|
||||
to_json : Json.Object
|
||||
to_json self = Json.from_pairs [["type", "Time"], ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond], ["zone", self.zone]]
|
||||
|
||||
## Format this time as text using the specified format specifier.
|
||||
|
||||
Arguments:
|
||||
- pattern: The pattern that specifies how to format the time.
|
||||
|
||||
? Pattern Syntax
|
||||
Patterns are based on a simple sequence of letters and symbols. For
|
||||
example, "d MMM uuuu" will format "2011-12-03" as "3 Dec 2011".
|
||||
|
||||
The list of accepted symbols with examples:
|
||||
|
||||
- 'G', era, "AD; Anno Domini"
|
||||
- 'u', year, "2004; 04"
|
||||
- 'y', year-of-era, "2004; 04"
|
||||
- 'D', day-of-year, "189"
|
||||
- 'M/L', month-of-year, "7; 07; Jul; July; J"
|
||||
- 'd', day-of-month, "10"
|
||||
- 'g', modified-julian-day, "2451334"
|
||||
- 'Q/q', quarter-of-year, "3; 03; Q3; 3rd quarter"
|
||||
- 'Y', week-based-year, "1996; 96"
|
||||
- 'w', week-of-week-based-year, "27"
|
||||
- 'W', week-of-month, "4"
|
||||
- 'E', day-of-week, "Tue; Tuesday; T"
|
||||
- 'e/c', localized day-of-week, "2; 02; Tue; Tuesday; T"
|
||||
- 'F', day-of-week-in-month, "3"
|
||||
- 'a', am-pm-of-day, "PM"
|
||||
- 'h', clock-hour-of-am-pm (1-12), "12"
|
||||
- 'K', hour-of-am-pm (0-11), "0"
|
||||
- 'k', clock-hour-of-day (1-24), "24"
|
||||
- 'H', hour-of-day (0-23), "0"
|
||||
- 'm', minute-of-hour, "30"
|
||||
- 's', second-of-minute, "55"
|
||||
- 'S', fraction-of-second, "978"
|
||||
- 'A', milli-of-day, "1234"
|
||||
- 'n', nano-of-second, "987654321"
|
||||
- 'N', nano-of-day, "1234000000"
|
||||
- 'V', time-zone ID, "America/Los_Angeles; Z; -08:30"
|
||||
- 'v', generic time-zone name, "Pacific Time; PT"
|
||||
- 'z', time-zone name, "Pacific Standard Time; PST"
|
||||
- 'O', localized zone-offset, "GMT+8; GMT+08:00; UTC-08:00"
|
||||
- 'X', zone-offset 'Z' for zero, "Z; -08; -0830; -08:30; -083015; -08:30:15"
|
||||
- 'x', zone-offset, "+0000; -08; -0830; -08:30; -083015; -08:30:15"
|
||||
- 'Z', zone-offset, "+0000; -0800; -08:00"
|
||||
- 'p', pad next, "1"
|
||||
- ''', (single quote) escape for text, "'Text'"
|
||||
- '''', (double quote) single quote, "'"
|
||||
- '[', optional section start
|
||||
- ']', optional section end
|
||||
|
||||
The count of pattern letters determines the format.
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"2020-10-08T16:41:13+03:00[Europe/Moscow]".
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "yyyy-MM-dd'T'HH:mm:ssZZZZ'['VV']'"
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"Thursday October 8 4:41 PM".
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEEE MMMM d h:mm a"
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"Thu Oct 8 (16:41)".
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEE MMM d (HH:mm)"
|
||||
format : Text -> Text
|
||||
format self pattern =
|
||||
DateTimeFormatter.ofPattern pattern . format self.internal_zoned_date_time
|
||||
|
||||
## Compares `self` to `that` to produce an ordering.
|
||||
|
||||
Arguments:
|
||||
- that: The other `Time` to compare against.
|
||||
|
||||
> Example
|
||||
Compare two times for their ordering.
|
||||
|
||||
(Time.new 2000).compare_to (Time.new 2001)
|
||||
compare_to : Time -> Ordering
|
||||
compare_to self that =
|
||||
sign = self.internal_zoned_date_time.compareTo that.internal_zoned_date_time
|
||||
Ordering.from_sign sign
|
||||
|
||||
## Compares two Time for equality.
|
||||
== : Time -> Boolean
|
||||
== self that = case that of
|
||||
Time _ -> self.internal_zoned_date_time.equals that.internal_zoned_date_time
|
||||
_ -> False
|
||||
|
||||
type Time_Error
|
||||
|
||||
## UNSTABLE
|
||||
|
||||
An error produced while working with time.
|
||||
An error produced while working with time- and date-related methods.
|
||||
|
||||
Arguments:
|
||||
- error_message: The message for the error.
|
||||
type Time_Error error_message
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Time_Of_Day
|
||||
import Standard.Base.Data.Time.Zone
|
||||
@ -193,7 +194,7 @@ type Date
|
||||
containing the first Thursday of the year. Therefore it is important to
|
||||
properly specify the `locale` argument.
|
||||
week_of_year : Locale.Locale -> Integer
|
||||
week_of_year self locale=Locale.default = Time_Utils.week_of_year self locale.java_locale
|
||||
week_of_year self locale=Locale.default = Time_Utils.week_of_year_localdate self locale.java_locale
|
||||
|
||||
## ALIAS Date to Time
|
||||
|
||||
@ -210,8 +211,8 @@ type Date
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_to_time = Date.new 2020 2 3 . to_time Time_Of_Day.new Zone.utc
|
||||
to_time : Time_Of_Day -> Zone -> Time
|
||||
to_time self time_of_day (zone = Zone.system) = Time.Time (Time_Utils.date_with_time self time_of_day.internal_local_time zone.internal_zone_id)
|
||||
to_time : Time_Of_Day -> Zone -> Date_Time
|
||||
to_time self time_of_day (zone=Zone.system) = self.to_time_builtin time_of_day zone
|
||||
|
||||
## Add the specified amount of time to this instant to get another date.
|
||||
|
||||
@ -226,7 +227,7 @@ type Date
|
||||
example_add = Date.new 2020 + 6.months
|
||||
+ : Duration -> Date
|
||||
+ self amount = if amount.is_time then Error.throw (Time.Time_Error "Date does not support time intervals") else
|
||||
(Time_Utils.date_adjust self 1 amount.internal_period) . internal_local_date
|
||||
(Time_Utils.date_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period) . internal_local_date
|
||||
|
||||
## Subtract the specified amount of time from this instant to get another
|
||||
date.
|
||||
@ -243,7 +244,7 @@ type Date
|
||||
example_subtract = Date.new 2020 - 7.days
|
||||
- : Duration -> Date
|
||||
- self amount = if amount.is_time then Error.throw (Time.Time_Error "Date does not support time intervals") else
|
||||
(Time_Utils.date_adjust self -1 amount.internal_period) . internal_local_date
|
||||
(Time_Utils.date_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period) . internal_local_date
|
||||
|
||||
|
||||
## A Date to Json conversion.
|
||||
@ -304,11 +305,11 @@ type Date
|
||||
(Date.new 2000).compare_to (Date.new 2001)
|
||||
compare_to : Date -> Ordering
|
||||
compare_to self that =
|
||||
sign = Time_Utils.compare_to self that
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
Ordering.from_sign sign
|
||||
|
||||
## Compares two Dates for equality.
|
||||
== : Date -> Boolean
|
||||
== self that =
|
||||
sign = Time_Utils.compare_to self that
|
||||
sign = Time_Utils.compare_to_localdate self that
|
||||
0 == sign
|
||||
|
@ -0,0 +1,483 @@
|
||||
from Standard.Base import all
|
||||
|
||||
from Standard.Base.Data.Time import Time_Error
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Time_Of_Day
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import java.time.ZonedDateTime
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
## ALIAS Current Time
|
||||
|
||||
Obtains the current date-time from the system clock in the system timezone.
|
||||
|
||||
> Example
|
||||
Get the current time
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_now = Date_Time.now
|
||||
now : Date_Time
|
||||
now = @Builtin_Method "Date_Time.now"
|
||||
|
||||
## Obtains an instance of `Date_Time` from a year, month, day, hour, minute,
|
||||
second, nanosecond and timezone.
|
||||
|
||||
Arguments:
|
||||
- month: the month-of-year to represent, from 1 (January) to 12 (December)
|
||||
- day: the day-of-month to represent, from 1 to 31 and must be valid for the
|
||||
year and month
|
||||
- hour: the hour-of-day to represent, from 0 to 23
|
||||
- minute: the minute-of-hour to represent, from 0 to 59
|
||||
- second: the second-of-minute to represent, from 0 to 59
|
||||
- nanosecond: the nano-of-second to represent, from 0 to 999,999,999
|
||||
- zone: the timezone
|
||||
|
||||
Returns a `Time_Error` if the provided time cannot be represented.
|
||||
|
||||
> Example
|
||||
Create a new zoned date time at Unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_new = Date_Time.new 1970 (zone = Zone.utc)
|
||||
|
||||
> Example
|
||||
Get the 5 August 1986 at midnight.
|
||||
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_new = Date_Time.new 1986 8 5
|
||||
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Date_Time ! Time_Error
|
||||
new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) =
|
||||
Panic.catch_java Any (Date_Time.new_builtin year month day hour minute second nanosecond zone) java_exception->
|
||||
Error.throw (Time_Error java_exception.getMessage)
|
||||
|
||||
## ALIAS Time from Text
|
||||
|
||||
Obtains an instance of `Time` from a text such as
|
||||
"2007-12-03T10:15:30+01:00 Europe/Paris".
|
||||
|
||||
Arguments:
|
||||
- text: The text representing the time to be parsed.
|
||||
- pattern: The pattern to use for parsing the input text.
|
||||
- locale: The locale in which the pattern should be interpreted.
|
||||
|
||||
? Pattern Syntax
|
||||
For the list of accepted symbols in pattern refer to `Time.format` doc.
|
||||
|
||||
? Default Date_Time Format
|
||||
The text must represent a valid date-time and is parsed using the ISO-8601
|
||||
extended offset date-time format to add the timezone. The section in square
|
||||
brackets is not part of the ISO-8601 standard. The format consists of:
|
||||
|
||||
- The ISO offset date time.
|
||||
- If the zone ID is not available or is a zone offset then the format is
|
||||
complete.
|
||||
- An open square bracket '['.
|
||||
- The zone ID. This is not part of the ISO-8601 standard. Parsing is case
|
||||
sensitive.
|
||||
- A close square bracket ']'.
|
||||
|
||||
This method will return a `Time_Error` if the provided time cannot be parsed
|
||||
using the above format.
|
||||
|
||||
> Example
|
||||
Parse UTC time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date_Time.parse "2020-10-01T04:11:12Z"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date_Time.parse "2020-10-01T04:11:12-04:00"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time specifying New York timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date_Time.parse "2020-10-01T04:11:12-04:00[America/New_York]"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time with nanoseconds.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date_Time.parse "2020-10-01T04:11:12.177528-04:00"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date_Time.parse "2020-10-01" . catch Time_Error (_->Date_Time.now)
|
||||
|
||||
> Example
|
||||
Parse "2020-05-06 04:30:20" as Date_Time
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse = Date.parse "2020-05-06 04:30:20" "yyyy-MM-dd HH:mm:ss"
|
||||
|
||||
> Example
|
||||
Parse "06 of May 2020 at 04:30AM" as Date_Tme
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_parse =
|
||||
Date_Time.parse "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
parse : Text -> Text | Nothing -> Locale -> Date_Time ! Time_Error
|
||||
parse text pattern=Nothing locale=Locale.default =
|
||||
Panic.catch_java Any handler=(java_exception -> Error.throw (Time_Error java_exception.getMessage)) <|
|
||||
case pattern of
|
||||
Nothing -> Date_Time.parse_builtin text
|
||||
Text -> Time_Utils.parse_datetime_format text pattern locale.java_locale
|
||||
|
||||
type Date_Time
|
||||
|
||||
## PRIVATE
|
||||
|
||||
A date-time with a timezone in the ISO-8601 calendar system, such as
|
||||
"2007-12-03T10:15:30+01:00 Europe/Paris".
|
||||
|
||||
Time is a representation of a date-time with a timezone. This class
|
||||
stores all date and time fields, to a precision of nanoseconds, and a
|
||||
timezone, with a zone offset used to handle ambiguous local
|
||||
date-times.
|
||||
|
||||
For example, the value "2nd October 2007 at 13:45.30.123456789 +02:00 in
|
||||
the Europe/Paris timezone" can be stored as `Time`.
|
||||
@Builtin_Type
|
||||
type Date_Time
|
||||
|
||||
## Get the year portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current year.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_year = Date_Time.now.year
|
||||
year : Integer
|
||||
year self = @Builtin_Method "Date_Time.year"
|
||||
|
||||
## Get the month portion of the time as a number from 1 to 12.
|
||||
|
||||
> Example
|
||||
Get the current month.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_month = Date_Time.now.month
|
||||
month : Integer
|
||||
month self = @Builtin_Method "Date_Time.month"
|
||||
|
||||
## Get the day portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current day.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_day = Date_Time.now.day
|
||||
day : Integer
|
||||
day self = @Builtin_Method "Date_Time.day"
|
||||
|
||||
## Get the hour portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current hour.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_hour = Date_Time.now.hour
|
||||
hour : Integer
|
||||
hour self = @Builtin_Method "Date_Time.hour"
|
||||
|
||||
## Get the minute portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current minute.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_minute = Date_Time.now.minute
|
||||
minute : Integer
|
||||
minute self = @Builtin_Method "Date_Time.minute"
|
||||
|
||||
## Get the second portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current second.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_second = Date_Time.now.second
|
||||
second : Integer
|
||||
second self = @Builtin_Method "Date_Time.second"
|
||||
|
||||
## Get the nanosecond portion of the time.
|
||||
|
||||
> Example
|
||||
Get the current nanosecond.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_nanosecond = Date_Time.now.nanosecond
|
||||
nanosecond : Integer
|
||||
nanosecond self = @Builtin_Method "Date_Time.nanosecond"
|
||||
|
||||
## Get the timezone for the time.
|
||||
|
||||
> Example
|
||||
Get the current timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_zone = Date_Time.now.zone
|
||||
zone : Zone
|
||||
zone self = @Builtin_Method "Date_Time.zone"
|
||||
|
||||
## Return the number of seconds from the Unix epoch.
|
||||
|
||||
> Example
|
||||
Get the current number of seconds from the Unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_epoch = Date_Time.now.to_epoch_seconds
|
||||
to_epoch_seconds : Integer
|
||||
to_epoch_seconds self = @Builtin_Method "Date_Time.to_epoch_seconds"
|
||||
|
||||
## Return the number of milliseconds from the Unix epoch.
|
||||
|
||||
> Example
|
||||
Get the current number of milliseconds from the unix epoch.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_epoch = Date_Time.now.to_epoch_milliseconds
|
||||
to_epoch_milliseconds : Integer
|
||||
to_epoch_milliseconds self = @Builtin_Method "Date_Time.to_epoch_milliseconds"
|
||||
|
||||
## Convert this point in time to time of day, discarding the time zone
|
||||
information.
|
||||
|
||||
> Example
|
||||
Convert the current time to a time of day.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_time_of_day = Date_Time.now.time_of_day
|
||||
time_of_day : Time_Of_Day
|
||||
time_of_day self = self.to_localtime_builtin
|
||||
|
||||
## Returns the number of week of year this date falls into.
|
||||
|
||||
Arguments:
|
||||
- locale: the locale used to define the notion of weeks of year.
|
||||
|
||||
! Locale Dependency
|
||||
Note that this operation is locale-specific. It varies both by the
|
||||
local definition of the first day of week and the definition of the
|
||||
first week of year. For example, in the US, the first day of the week
|
||||
is Sunday and week 1 is the week containing January 1. In the UK on the
|
||||
other hand, the first day of the week is Monday, and week 1 is the week
|
||||
containing the first Thursday of the year. Therefore it is important to
|
||||
properly specify the `locale` argument.
|
||||
week_of_year : Locale.Locale -> Integer
|
||||
week_of_year self locale=Locale.default = Time_Utils.week_of_year_zoneddatetime self locale.java_locale
|
||||
|
||||
## ALIAS Time to Date
|
||||
|
||||
Convert this point in time to date, discarding the time of day
|
||||
information.
|
||||
|
||||
> Example
|
||||
Convert the current time to a date.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_date = Date_Time.now.date
|
||||
date : Date
|
||||
date self = self.to_localdate_builtin
|
||||
|
||||
## ALIAS Change Time Zone
|
||||
|
||||
Convert the time instant to the same instant in the provided time zone.
|
||||
|
||||
Arguments:
|
||||
- zone: The timezone to convert the time instant into.
|
||||
|
||||
> Example
|
||||
Convert time instance to -04:00 timezone.
|
||||
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
example_at_zone = Date_Time.new 2020 . at_zone (Zone.new -4)
|
||||
at_zone : Zone -> Date_Time
|
||||
at_zone self zone = @Builtin_Method "Date_Time.at_zone"
|
||||
|
||||
## Add the specified amount of time to this instant to produce a new instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to add to this instant.
|
||||
|
||||
> Example
|
||||
Add 15 years and 3 hours to a zoned date time.
|
||||
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_plus = Date_Time.new 2020 + 15.years + 3.hours
|
||||
+ : Duration -> Date_Time
|
||||
+ self amount =
|
||||
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period amount.internal_duration
|
||||
|
||||
## Subtract the specified amount of time from this instant to get a new
|
||||
instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to subtract from this instant.
|
||||
|
||||
> Example
|
||||
Subtract 1 year and 9 months from a zoned date time.
|
||||
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_minus = Date_Time.new 2020 - 1.year - 9.months
|
||||
- : Duration -> Date_Time
|
||||
- self amount =
|
||||
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period amount.internal_duration
|
||||
|
||||
## Convert this time to text using the default formatter.
|
||||
|
||||
> Example
|
||||
Convert the current time to text.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_to_text = Date_Time.now.to_text
|
||||
to_text : Text
|
||||
to_text self = @Builtin_Method "Date_Time.to_text"
|
||||
|
||||
## Convert the time to JSON.
|
||||
|
||||
> Example
|
||||
Convert the current time to JSON.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_to_json = Date_Time.now.to_json
|
||||
to_json : Json.Object
|
||||
to_json self = Json.from_pairs [["type", "Time"], ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond], ["zone", self.zone]]
|
||||
|
||||
## Format this time as text using the specified format specifier.
|
||||
|
||||
Arguments:
|
||||
- pattern: The pattern that specifies how to format the time.
|
||||
|
||||
? Pattern Syntax
|
||||
Patterns are based on a simple sequence of letters and symbols. For
|
||||
example, "d MMM uuuu" will format "2011-12-03" as "3 Dec 2011".
|
||||
|
||||
The list of accepted symbols with examples:
|
||||
|
||||
- 'G', era, "AD; Anno Domini"
|
||||
- 'u', year, "2004; 04"
|
||||
- 'y', year-of-era, "2004; 04"
|
||||
- 'D', day-of-year, "189"
|
||||
- 'M/L', month-of-year, "7; 07; Jul; July; J"
|
||||
- 'd', day-of-month, "10"
|
||||
- 'g', modified-julian-day, "2451334"
|
||||
- 'Q/q', quarter-of-year, "3; 03; Q3; 3rd quarter"
|
||||
- 'Y', week-based-year, "1996; 96"
|
||||
- 'w', week-of-week-based-year, "27"
|
||||
- 'W', week-of-month, "4"
|
||||
- 'E', day-of-week, "Tue; Tuesday; T"
|
||||
- 'e/c', localized day-of-week, "2; 02; Tue; Tuesday; T"
|
||||
- 'F', day-of-week-in-month, "3"
|
||||
- 'a', am-pm-of-day, "PM"
|
||||
- 'h', clock-hour-of-am-pm (1-12), "12"
|
||||
- 'K', hour-of-am-pm (0-11), "0"
|
||||
- 'k', clock-hour-of-day (1-24), "24"
|
||||
- 'H', hour-of-day (0-23), "0"
|
||||
- 'm', minute-of-hour, "30"
|
||||
- 's', second-of-minute, "55"
|
||||
- 'S', fraction-of-second, "978"
|
||||
- 'A', milli-of-day, "1234"
|
||||
- 'n', nano-of-second, "987654321"
|
||||
- 'N', nano-of-day, "1234000000"
|
||||
- 'V', timezone ID, "America/Los_Angeles; Z; -08:30"
|
||||
- 'v', generic timezone name, "Pacific Time; PT"
|
||||
- 'z', timezone name, "Pacific Standard Time; PST"
|
||||
- 'O', localized zone-offset, "GMT+8; GMT+08:00; UTC-08:00"
|
||||
- 'X', zone-offset 'Z' for zero, "Z; -08; -0830; -08:30; -083015; -08:30:15"
|
||||
- 'x', zone-offset, "+0000; -08; -0830; -08:30; -083015; -08:30:15"
|
||||
- 'Z', zone-offset, "+0000; -0800; -08:00"
|
||||
- 'p', pad next, "1"
|
||||
- ''', (single quote) escape for text, "'Text'"
|
||||
- '''', (double quote) single quote, "'"
|
||||
- '[', optional section start
|
||||
- ']', optional section end
|
||||
|
||||
The count of pattern letters determines the format.
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"2020-10-08T16:41:13+03:00[Europe/Moscow]".
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Date_Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "yyyy-MM-dd'T'HH:mm:ssZZZZ'['VV']'"
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"Thursday October 8 4:41 PM".
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Date_Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEEE MMMM d h:mm a"
|
||||
|
||||
> Example
|
||||
Format "2020-10-08T16:41:13+03:00[Europe/Moscow]" as
|
||||
"Thu Oct 8 (16:41)".
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_format =
|
||||
Date_Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEE MMM d (HH:mm)"
|
||||
format : Text -> Text
|
||||
format self pattern = @Builtin_Method "Date_Time.format"
|
||||
|
||||
## Compares `self` to `that` to produce an ordering.
|
||||
|
||||
Arguments:
|
||||
- that: The other `Date_Time` to compare against.
|
||||
|
||||
> Example
|
||||
Compare two times for their ordering.
|
||||
|
||||
(Date_Time.new 2000).compare_to (Date_Time.new 2001)
|
||||
compare_to : Time -> Ordering
|
||||
compare_to self that =
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
Ordering.from_sign sign
|
||||
|
||||
## Compares two Date_Time for equality.
|
||||
== : Date_Time -> Boolean
|
||||
== self that =
|
||||
sign = Time_Utils.compare_to_zoneddatetime self that
|
||||
0 == sign
|
@ -5,26 +5,26 @@ import Standard.Base.System
|
||||
|
||||
polyglot java import java.time.Duration as Java_Duration
|
||||
polyglot java import java.time.Period as Java_Period
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
## Create an interval representing the duration between two points in time.
|
||||
|
||||
Arguments:
|
||||
- start_inclusive: The start time of the duration.
|
||||
- end_inclusive: The end time of the duration.
|
||||
- timezone_aware: Should the creation of the interval be timezone-aware.
|
||||
|
||||
> Example
|
||||
An hour interval between two points in time.
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
|
||||
example_between = Duration.between Time.now (Time.new 2010 10 20)
|
||||
between : Time -> Time -> Duration
|
||||
between start_inclusive end_exclusive =
|
||||
example_between = Duration.between Date_Time.now (Date_Time.new 2010 10 20)
|
||||
between : Date_Time -> Date_Time -> Duration
|
||||
between start_inclusive end_exclusive timezone_aware=True =
|
||||
period = Java_Period.ofDays 0 . normalized
|
||||
start = start_inclusive.internal_zoned_date_time
|
||||
end = end_exclusive.internal_zoned_date_time
|
||||
duration = Java_Duration.between start end
|
||||
duration = Time_Utils.duration_between start_inclusive end_exclusive timezone_aware
|
||||
Duration period duration
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
example_now = Time_Of_Day.now
|
||||
now : Time_Of_Day
|
||||
now = Time_Of_Day LocalTime.now
|
||||
now = @Builtin_Method "Time_Of_Day.now"
|
||||
|
||||
## Obtains an instance of `Time_Of_Day` from an hour, minute, second
|
||||
and nanosecond.
|
||||
@ -46,7 +46,7 @@ now = Time_Of_Day LocalTime.now
|
||||
example_epoch = Time_Of_Day.new hour=9 minute=30
|
||||
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error
|
||||
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
|
||||
Panic.catch_java Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) java_exception->
|
||||
Panic.catch_java Any (Time_Of_Day.new_builtin hour minute second nanosecond) java_exception->
|
||||
Error.throw (Time.Time_Error java_exception.getMessage)
|
||||
|
||||
## Obtains an instance of `Time_Of_Day` from a text such as "10:15".
|
||||
@ -112,15 +112,10 @@ new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
|
||||
example_parse = Time_Of_Day.parse "4:30AM" "h:mma"
|
||||
parse : Text -> Text | Nothing -> Locale -> Time_Of_Day ! Time.Time_Error
|
||||
parse text pattern=Nothing locale=Locale.default =
|
||||
result = Panic.recover Any <| case pattern of
|
||||
Nothing -> LocalTime.parse text
|
||||
Text ->
|
||||
formatter = DateTimeFormatter.ofPattern pattern
|
||||
LocalTime.parse text (formatter.withLocale locale.java_locale)
|
||||
_ -> Panic.throw (Time.Time_Error "An invalid pattern was provided.")
|
||||
Time_Of_Day result . map_error <| case _ of
|
||||
Polyglot_Error err -> Time.Time_Error err.getMessage
|
||||
x -> x
|
||||
Panic.catch_java Any handler=(java_exception -> Error.throw (Time.Time_Error java_exception.getMessage)) <|
|
||||
case pattern of
|
||||
Nothing -> Time_Of_Day.parse_builtin text
|
||||
Text -> Time_Utils.parse_time text pattern locale.java_locale
|
||||
|
||||
type Time_Of_Day
|
||||
|
||||
@ -129,12 +124,10 @@ type Time_Of_Day
|
||||
This type is a date-time object that represents a time, often viewed
|
||||
as hour-minute-second.
|
||||
|
||||
Arguments:
|
||||
- internal_local_time: The internal representation of the time of day.
|
||||
|
||||
Time is represented to nanosecond precision. For example, the value
|
||||
"13:45.30.123456789" can be stored in a `Time_Of_Day`.
|
||||
type Time_Of_Day internal_local_time
|
||||
@Builtin_Type
|
||||
type Time_Of_Day
|
||||
|
||||
## Get the hour portion of the time of day.
|
||||
|
||||
@ -145,7 +138,7 @@ type Time_Of_Day
|
||||
|
||||
example_hour = Time_Of_Day.now.hour
|
||||
hour : Integer
|
||||
hour self = self . internal_local_time . getHour
|
||||
hour self = @Builtin_Method "Time_Of_Day.hour"
|
||||
|
||||
## Get the minute portion of the time of day.
|
||||
|
||||
@ -156,7 +149,7 @@ type Time_Of_Day
|
||||
|
||||
example_minute = Time_Of_Day.now.minute
|
||||
minute : Integer
|
||||
minute self = self . internal_local_time . getMinute
|
||||
minute self = @Builtin_Method "Time_Of_Day.minute"
|
||||
|
||||
## Get the second portion of the time of day.
|
||||
|
||||
@ -167,7 +160,7 @@ type Time_Of_Day
|
||||
|
||||
example_second = Time_Of_Day.now.second
|
||||
second : Integer
|
||||
second self = self . internal_local_time . getSecond
|
||||
second self = @Builtin_Method "Time_Of_Day.second"
|
||||
|
||||
## Get the nanosecond portion of the time of day.
|
||||
|
||||
@ -178,7 +171,7 @@ type Time_Of_Day
|
||||
|
||||
example_nanosecond = Time_Of_Day.now.nanosecond
|
||||
nanosecond : Integer
|
||||
nanosecond self = self . internal_local_time . getNano
|
||||
nanosecond self = @Builtin_Method "Time_Of_Day.nanosecond"
|
||||
|
||||
## Extracts the time as the number of seconds, from 0 to 24 * 60 * 60 - 1.
|
||||
|
||||
@ -189,7 +182,7 @@ type Time_Of_Day
|
||||
|
||||
example_to_seconds = Time_Of_Day.now.to_seconds
|
||||
to_seconds : Integer
|
||||
to_seconds self = self . internal_local_time . toSecondOfDay
|
||||
to_seconds self = @Builtin_Method "Time_Of_Day.to_seconds"
|
||||
|
||||
## Combine this time of day with a date to create a point in time.
|
||||
|
||||
@ -204,8 +197,7 @@ type Time_Of_Day
|
||||
|
||||
example_to_time = Time_Of_Day.new 12 30 . to_time (Date.new 2020)
|
||||
to_time : Date -> Zone -> Time
|
||||
to_time self date (zone = Zone.system) =
|
||||
Time.Time (self . internal_local_time . atDate date . atZone zone.internal_zone_id)
|
||||
to_time self date (zone=Zone.system) = self.to_time_builtin date zone
|
||||
|
||||
## Add the specified amount of time to this instant to get a new instant.
|
||||
|
||||
@ -220,7 +212,7 @@ type Time_Of_Day
|
||||
example_plus = Time_Of_Day.new + 3.seconds
|
||||
+ : Duration -> Time_Of_Day
|
||||
+ self amount = if amount.is_date then Error.throw (Time.Time_Error "Time_Of_Day does not support date intervals") else
|
||||
Time_Of_Day (self . internal_local_time . plus amount.internal_duration)
|
||||
Time_Utils.time_adjust self Time_Utils.AdjustOp.PLUS amount.internal_duration
|
||||
|
||||
## Subtract the specified amount of time from this instant to get a new
|
||||
instant.
|
||||
@ -237,7 +229,7 @@ type Time_Of_Day
|
||||
example_minus = Time_Of_Day.now - 12.hours
|
||||
- : Duration -> Time_Of_Day
|
||||
- self amount = if amount.is_date then Error.throw (Time.Time_Error "Time_Of_Day does not support date intervals") else
|
||||
Time_Of_Day (self . internal_local_time . minus amount.internal_duration)
|
||||
Time_Utils.time_adjust self Time_Utils.AdjustOp.MINUS amount.internal_duration
|
||||
|
||||
## Format this time of day as text using the default formatter.
|
||||
|
||||
@ -248,7 +240,7 @@ type Time_Of_Day
|
||||
|
||||
example_to_text = Time_Of_Day.now.to_text
|
||||
to_text : Text
|
||||
to_text self = Time_Utils.default_time_of_day_formatter . format self.internal_local_time
|
||||
to_text self = @Builtin_Method "Time_Of_Day.to_text"
|
||||
|
||||
## A Time_Of_Day to Json conversion.
|
||||
|
||||
@ -308,8 +300,7 @@ type Time_Of_Day
|
||||
|
||||
example_format = Time_Of_Day.new 16 21 10 . format "'hour:'h"
|
||||
format : Text -> Text
|
||||
format self pattern =
|
||||
DateTimeFormatter.ofPattern pattern . format self.internal_local_time
|
||||
format self pattern = @Builtin_Method "Time_Of_Day.format"
|
||||
|
||||
## Compares `self` to `that` to produce an ordering.
|
||||
|
||||
@ -327,11 +318,11 @@ type Time_Of_Day
|
||||
time_1.compare_to time_2
|
||||
compare_to : Time_Of_Day -> Ordering
|
||||
compare_to self that =
|
||||
sign = self.internal_local_time.compareTo that.internal_local_time
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
Ordering.from_sign sign
|
||||
|
||||
## Compares two Time_Of_Day for equality.
|
||||
== : Date -> Boolean
|
||||
== self that = case that of
|
||||
Time_Of_Day _ -> self.internal_local_time.equals that.internal_local_time
|
||||
_ -> False
|
||||
== self that =
|
||||
sign = Time_Utils.compare_to_localtime self that
|
||||
0 == sign
|
||||
|
@ -1,7 +1,10 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
polyglot java import java.time.ZoneId
|
||||
polyglot java import java.time.ZoneOffset
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
## The system default timezone.
|
||||
|
||||
@ -12,7 +15,7 @@ polyglot java import java.time.ZoneOffset
|
||||
|
||||
example_system = Zone.system
|
||||
system : Zone
|
||||
system = Zone ZoneId.systemDefault
|
||||
system = @Builtin_Method "Zone.system"
|
||||
|
||||
## ALIAS Current Time Zone
|
||||
|
||||
@ -58,7 +61,7 @@ utc = parse "UTC"
|
||||
example_new = Zone.new 1 1 50
|
||||
new : Integer -> Integer -> Integer -> Zone
|
||||
new (hours = 0) (minutes = 0) (seconds = 0) =
|
||||
Zone (ZoneOffset.ofHoursMinutesSeconds hours minutes seconds . normalized)
|
||||
Zone.new_builtin hours minutes seconds
|
||||
|
||||
## ALIAS Time Zone from Text
|
||||
|
||||
@ -95,7 +98,9 @@ new (hours = 0) (minutes = 0) (seconds = 0) =
|
||||
|
||||
example_parse = Zone.parse "+03:02:01"
|
||||
parse : Text -> Zone
|
||||
parse text = Zone (ZoneId.of text)
|
||||
parse text =
|
||||
Panic.catch_java Any handler=(java_exception -> Error.throw (Time.Time_Error java_exception.getMessage)) <|
|
||||
Zone.parse_builtin text
|
||||
|
||||
type Zone
|
||||
|
||||
@ -109,7 +114,8 @@ type Zone
|
||||
|
||||
A time zone can be eiter offset-based like "-06:00" or id-based like
|
||||
"Europe/Paris".
|
||||
type Zone internal_zone_id
|
||||
@Builtin_Type
|
||||
type Zone
|
||||
|
||||
## Get the unique timezone ID.
|
||||
|
||||
@ -120,7 +126,7 @@ type Zone
|
||||
|
||||
example_zone_id = Zone.system.zone_id
|
||||
zone_id : Text
|
||||
zone_id self = self.internal_zone_id . getId
|
||||
zone_id self = @Builtin_Method "Zone.zone_id"
|
||||
|
||||
## Convert the time zone to JSON.
|
||||
|
||||
@ -132,3 +138,7 @@ type Zone
|
||||
example_to_json = Zone.system.to_json
|
||||
to_json : Json.Object
|
||||
to_json self = Json.from_pairs [["type", "Zone"], ["id", self.zone_id]]
|
||||
|
||||
## Compares two Zones for equality.
|
||||
== : Zone -> Boolean
|
||||
== self that = Time_Utils.equals_zone self that
|
||||
|
@ -335,8 +335,7 @@ type File
|
||||
example_exists = Examples.csv.creation_time
|
||||
creation_time : Time ! File_Error
|
||||
creation_time self =
|
||||
handle_java_exceptions self <|
|
||||
Time (self.creation_time_builtin)
|
||||
handle_java_exceptions self <| self.creation_time_builtin
|
||||
|
||||
## PRIVATE
|
||||
|
||||
@ -356,8 +355,7 @@ type File
|
||||
example_exists = Examples.csv.last_modified_time
|
||||
last_modified_time : Time ! File_Error
|
||||
last_modified_time self =
|
||||
handle_java_exceptions self <|
|
||||
Time (self.last_modified_time_builtin)
|
||||
handle_java_exceptions self <| self.last_modified_time_builtin
|
||||
|
||||
## PRIVATE
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Network.Http
|
||||
import Standard.Base.System.Platform
|
||||
import Standard.Base.Data.Time.Duration
|
||||
@ -70,7 +70,7 @@ scratch_file =
|
||||
|
||||
## An example duration for experimenting with duration APIs.
|
||||
duration : Duration
|
||||
duration = Duration.between (Time.new 2020 10 20) Time.now
|
||||
duration = Duration.between (Date_Time.new 2020 10 20) Date_Time.now
|
||||
|
||||
## An example amount of JSON as text.
|
||||
json_text : Text
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_now = Time.now
|
||||
example_now = Date_Time.now
|
||||
|
||||
> Example
|
||||
Parse UTC time.
|
||||
@ -21,14 +21,14 @@
|
||||
> Example
|
||||
Convert time instance to -04:00 timezone.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
exaomple_at_zone = Time.new 2020 . at_zone (Zone.new -4)
|
||||
exaomple_at_zone = Date_Time.new 2020 . at_zone (Zone.new -4)
|
||||
|
||||
> Example
|
||||
Convert the current time to a date.
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
example_date = Time.now.date
|
||||
example_date = Date_Time.now.date
|
||||
|
@ -1,6 +1,6 @@
|
||||
from Standard.Base import all
|
||||
|
||||
from Standard.Base.Data.Time import Time
|
||||
from Standard.Base.Data.Time import Date_Time
|
||||
from Standard.Base.Data.Time.Time_Of_Day import Time_Of_Day
|
||||
import Standard.Base.Error.Common as Errors
|
||||
|
||||
@ -67,7 +67,7 @@ type Data_Formatter
|
||||
By default, a warning is issued, but the operation proceeds.
|
||||
If set to `Report_Error`, the operation fails with a dataflow error.
|
||||
If set to `Ignore`, the operation proceeds without errors or warnings.
|
||||
parse : Text -> (Auto|Integer|Number|Date|Time|DateTime|Boolean) -> Problem_Behavior -> Any
|
||||
parse : Text -> (Auto|Integer|Number|Date|Date_Time|Time_Of_Day|Boolean) -> Problem_Behavior -> Any
|
||||
parse self text datatype=Auto on_problems=Problem_Behavior.Report_Warning =
|
||||
parser = case datatype of
|
||||
Auto -> self.make_auto_parser
|
||||
@ -171,14 +171,13 @@ type Data_Formatter
|
||||
|
||||
## PRIVATE
|
||||
make_datatype_parser self datatype = case datatype of
|
||||
Integer -> self.make_integer_parser
|
||||
Decimal -> self.make_decimal_parser
|
||||
Boolean -> self.make_boolean_parser
|
||||
_ ->
|
||||
if datatype == Date then self.make_date_parser else
|
||||
if datatype == Time then self.make_datetime_parser else
|
||||
if datatype == Time_Of_Day then self.make_time_parser else
|
||||
Error.throw (Illegal_Argument_Error "Unsupported datatype: "+datatype.to_text)
|
||||
Integer -> self.make_integer_parser
|
||||
Decimal -> self.make_decimal_parser
|
||||
Boolean -> self.make_boolean_parser
|
||||
Date -> self.make_date_parser
|
||||
Date_Time -> self.make_datetime_parser
|
||||
Time_Of_Day -> self.make_time_parser
|
||||
_ -> Error.throw (Illegal_Argument_Error "Unsupported datatype: "+datatype.to_text)
|
||||
|
||||
## PRIVATE
|
||||
get_specific_type_parsers self =
|
||||
|
@ -1,5 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Runtime.State
|
||||
import Standard.Base.System
|
||||
|
||||
@ -769,7 +769,7 @@ wrap_junit_testsuites config builder ~action =
|
||||
report_pending_group : Text -> Text -> Suite_Config -> (StringBuilder|Nothing) -> Nothing
|
||||
report_pending_group name reason config builder =
|
||||
if config.should_output_junit then
|
||||
builder.append (' <testsuite name="' + (escape_xml name) + '" timestamp="' + (Time.now.format "yyyy-MM-dd'T'HH:mm:ss") + '">\n')
|
||||
builder.append (' <testsuite name="' + (escape_xml name) + '" timestamp="' + (Date_Time.now.format "yyyy-MM-dd'T'HH:mm:ss") + '">\n')
|
||||
builder.append (' <testcase name="' + (escape_xml name) + '"><skipped message="' + (escape_xml reason) + '" /></testcase>\n')
|
||||
builder.append ' </testsuite>\n'
|
||||
|
||||
@ -782,7 +782,7 @@ report_pending_group name reason config builder =
|
||||
Spec.print_report : Suite_Config -> (StringBuilder|Nothing) -> Nothing
|
||||
Spec.print_report self config builder =
|
||||
if config.should_output_junit then
|
||||
builder.append (' <testsuite name="' + (escape_xml self.name) + '" timestamp="' + (Time.now.format "yyyy-MM-dd'T'HH:mm:ss") + '"')
|
||||
builder.append (' <testsuite name="' + (escape_xml self.name) + '" timestamp="' + (Date_Time.now.format "yyyy-MM-dd'T'HH:mm:ss") + '"')
|
||||
builder.append (' tests="' + self.behaviors.length.to_text + '"')
|
||||
builder.append (' disabled="' + self.behaviors.filter (x->(x.is_a Pending)) . length . to_text + '"')
|
||||
builder.append (' errors="' + self.behaviors.filter (x->(x.is_a Failure)) . length . to_text + '"')
|
||||
|
@ -16,6 +16,9 @@ import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.enso.interpreter.epb.node.ContextRewrapExceptionNode;
|
||||
import org.enso.interpreter.epb.node.ContextRewrapNode;
|
||||
|
||||
@ -935,4 +938,54 @@ public class PolyglotProxy implements TruffleObject {
|
||||
leaveOrigin(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTime(
|
||||
@CachedLibrary("this.delegate") InteropLibrary times,
|
||||
@CachedLibrary("this") InteropLibrary node) {
|
||||
Object p = enterOrigin(node);
|
||||
try {
|
||||
return times.isTime(delegate);
|
||||
} finally {
|
||||
leaveOrigin(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalTime asTime(
|
||||
@CachedLibrary("this.delegate") InteropLibrary times,
|
||||
@CachedLibrary("this") InteropLibrary node)
|
||||
throws UnsupportedMessageException {
|
||||
Object p = enterOrigin(node);
|
||||
try {
|
||||
return times.asTime(delegate);
|
||||
} finally {
|
||||
leaveOrigin(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTimeZone(
|
||||
@CachedLibrary("this.delegate") InteropLibrary times,
|
||||
@CachedLibrary("this") InteropLibrary node) {
|
||||
Object p = enterOrigin(node);
|
||||
try {
|
||||
return times.isTimeZone(delegate);
|
||||
} finally {
|
||||
leaveOrigin(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
ZoneId asTimeZone(
|
||||
@CachedLibrary("this.delegate") InteropLibrary times,
|
||||
@CachedLibrary("this") InteropLibrary node)
|
||||
throws UnsupportedMessageException {
|
||||
Object p = enterOrigin(node);
|
||||
try {
|
||||
return times.asTimeZone(delegate);
|
||||
} finally {
|
||||
leaveOrigin(node, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
@ -23,8 +24,7 @@ import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.EnsoDate;
|
||||
import org.enso.interpreter.runtime.data.*;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
@ -260,6 +260,118 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(self)",
|
||||
"!methods.hasSpecialDispatch(self)",
|
||||
"getPolyglotCallType(self, symbol.getName(), interop) == CONVERT_TO_DATE_TIME"
|
||||
})
|
||||
Stateful doConvertDateTime(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
Object self,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary dateDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
var ctx = Context.get(this);
|
||||
try {
|
||||
var hostLocalDate = interop.asDate(self);
|
||||
var hostLocalTime = interop.asTime(self);
|
||||
var hostZonedDateTime = hostLocalDate.atTime(hostLocalTime).atZone(ZoneId.systemDefault());
|
||||
var dateTime = new EnsoDateTime(hostZonedDateTime);
|
||||
Function function = dateDispatch.getFunctionalDispatch(dateTime, symbol);
|
||||
arguments[0] = dateTime;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException | UnsupportedMessageException e) {
|
||||
throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(self)",
|
||||
"!methods.hasSpecialDispatch(self)",
|
||||
"getPolyglotCallType(self, symbol.getName(), interop) == CONVERT_TO_ZONED_DATE_TIME"
|
||||
})
|
||||
Stateful doConvertZonedDateTime(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
Object self,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary dateDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
var ctx = Context.get(this);
|
||||
try {
|
||||
var hostLocalDate = interop.asDate(self);
|
||||
var hostLocalTime = interop.asTime(self);
|
||||
var hostZone = interop.asTimeZone(self);
|
||||
var dateTime = new EnsoDateTime(hostLocalDate.atTime(hostLocalTime).atZone(hostZone));
|
||||
Function function = dateDispatch.getFunctionalDispatch(dateTime, symbol);
|
||||
arguments[0] = dateTime;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException | UnsupportedMessageException e) {
|
||||
throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(self)",
|
||||
"!methods.hasSpecialDispatch(self)",
|
||||
"getPolyglotCallType(self, symbol.getName(), interop) == CONVERT_TO_ZONE"
|
||||
})
|
||||
Stateful doConvertZone(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
Object self,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary dateDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
var ctx = Context.get(this);
|
||||
try {
|
||||
var hostZone = interop.asTimeZone(self);
|
||||
var dateTime = new EnsoZone(hostZone);
|
||||
Function function = dateDispatch.getFunctionalDispatch(dateTime, symbol);
|
||||
arguments[0] = dateTime;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException | UnsupportedMessageException e) {
|
||||
throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(self)",
|
||||
"!methods.hasSpecialDispatch(self)",
|
||||
"getPolyglotCallType(self, symbol.getName(), interop) == CONVERT_TO_TIME_OF_DAY"
|
||||
})
|
||||
Stateful doConvertTimeOfDay(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
Object self,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary dateDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop) {
|
||||
var ctx = Context.get(this);
|
||||
try {
|
||||
var hostLocalTime = interop.asTime(self);
|
||||
var dateTime = new EnsoTimeOfDay(hostLocalTime);
|
||||
Function function = dateDispatch.getFunctionalDispatch(dateTime, symbol);
|
||||
arguments[0] = dateTime;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException | UnsupportedMessageException e) {
|
||||
throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(self)",
|
||||
|
@ -48,6 +48,26 @@ public abstract class HostMethodCallNode extends Node {
|
||||
* Standard.Base.Data.Time.Date} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_DATE,
|
||||
/**
|
||||
* The method call should be handled by converting {@code self} to a {@code
|
||||
* Standard.Base.Data.DateTime} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_ZONED_DATE_TIME,
|
||||
/**
|
||||
* The method call should be handled by converting {@code self} to a {@code
|
||||
* Standard.Base.Data.DateTime} with a system Zone and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_DATE_TIME,
|
||||
/**
|
||||
* The method call should be handled by converting {@code self} to a {@code
|
||||
* Standard.Base.Data.Time.Time_Of_Day} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_TIME_OF_DAY,
|
||||
/**
|
||||
* The method call should be handled by converting {@code self} to a {@code
|
||||
* Standard.Base.Data.Time.Zone} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_ZONE,
|
||||
/** The method call should be handled by dispatching through the {@code Any} type. */
|
||||
NOT_SUPPORTED;
|
||||
|
||||
@ -59,7 +79,13 @@ public abstract class HostMethodCallNode extends Node {
|
||||
* @return true if one can directly pass this object to {@link InteropLibrary}
|
||||
*/
|
||||
public boolean isInteropLibrary() {
|
||||
return this != NOT_SUPPORTED && this != CONVERT_TO_TEXT && this != CONVERT_TO_DATE;
|
||||
return this != NOT_SUPPORTED
|
||||
&& this != CONVERT_TO_TEXT
|
||||
&& this != CONVERT_TO_DATE
|
||||
&& this != CONVERT_TO_DATE_TIME
|
||||
&& this != CONVERT_TO_ZONED_DATE_TIME
|
||||
&& this != CONVERT_TO_TIME_OF_DAY
|
||||
&& this != CONVERT_TO_ZONE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,8 +106,20 @@ public abstract class HostMethodCallNode extends Node {
|
||||
*/
|
||||
public static PolyglotCallType getPolyglotCallType(
|
||||
Object self, String methodName, InteropLibrary library) {
|
||||
if (library.isDate(self) && !library.isTime(self)) {
|
||||
return PolyglotCallType.CONVERT_TO_DATE;
|
||||
if (library.isDate(self)) {
|
||||
if (library.isTime(self)) {
|
||||
if (library.isTimeZone(self)) {
|
||||
return PolyglotCallType.CONVERT_TO_ZONED_DATE_TIME;
|
||||
} else {
|
||||
return PolyglotCallType.CONVERT_TO_DATE_TIME;
|
||||
}
|
||||
} else {
|
||||
return PolyglotCallType.CONVERT_TO_DATE;
|
||||
}
|
||||
} else if (library.isTime(self)) {
|
||||
return PolyglotCallType.CONVERT_TO_TIME_OF_DAY;
|
||||
} else if (library.isTimeZone(self)) {
|
||||
return PolyglotCallType.CONVERT_TO_ZONE;
|
||||
} else if (library.isString(self)) {
|
||||
return PolyglotCallType.CONVERT_TO_TEXT;
|
||||
} else if (library.isMemberInvocable(self, methodName)) {
|
||||
|
@ -76,6 +76,9 @@ public class Builtins {
|
||||
private final BuiltinAtomConstructor projectDescription;
|
||||
private final BuiltinAtomConstructor file;
|
||||
private final BuiltinAtomConstructor date;
|
||||
private final BuiltinAtomConstructor dateTime;
|
||||
private final BuiltinAtomConstructor timeOfDay;
|
||||
private final BuiltinAtomConstructor zone;
|
||||
private final BuiltinAtomConstructor warning;
|
||||
|
||||
/**
|
||||
@ -117,6 +120,15 @@ public class Builtins {
|
||||
date =
|
||||
new BuiltinAtomConstructor(
|
||||
this, org.enso.interpreter.node.expression.builtin.date.Date.class);
|
||||
dateTime =
|
||||
new BuiltinAtomConstructor(
|
||||
this, org.enso.interpreter.node.expression.builtin.date.DateTime.class);
|
||||
timeOfDay =
|
||||
new BuiltinAtomConstructor(
|
||||
this, org.enso.interpreter.node.expression.builtin.date.TimeOfDay.class);
|
||||
zone =
|
||||
new BuiltinAtomConstructor(
|
||||
this, org.enso.interpreter.node.expression.builtin.date.Zone.class);
|
||||
special = new Special(language);
|
||||
warning = new BuiltinAtomConstructor(this, Warning.class);
|
||||
}
|
||||
@ -430,6 +442,33 @@ public class Builtins {
|
||||
return date.constructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code DateTime} atom constructor.
|
||||
*
|
||||
* @return the {@code DateTime} atom constructor
|
||||
*/
|
||||
public AtomConstructor dateTime() {
|
||||
return dateTime.constructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Zone} atom constructor.
|
||||
*
|
||||
* @return the {@code Zone} atom constructor
|
||||
*/
|
||||
public AtomConstructor timeOfDay() {
|
||||
return timeOfDay.constructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Zone} atom constructor.
|
||||
*
|
||||
* @return the {@code Zone} atom constructor
|
||||
*/
|
||||
public AtomConstructor zone() {
|
||||
return zone.constructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Debug} atom constructor. TODO: this is redundant, figure out a way to avoid
|
||||
* createing spurious Debug builtin type
|
||||
|
@ -5,12 +5,14 @@ import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
@ -24,7 +26,7 @@ import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
@ExportLibrary(MethodDispatchLibrary.class)
|
||||
@Builtin(pkg = "date", name = "Date")
|
||||
@Builtin(pkg = "date", name = "Date", stdlibName = "Standard.Base.Data.Time.Date")
|
||||
public final class EnsoDate implements TruffleObject {
|
||||
private final LocalDate date;
|
||||
|
||||
@ -71,6 +73,11 @@ public final class EnsoDate implements TruffleObject {
|
||||
return date.getDayOfMonth();
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "to_time_builtin", description = "Combine this day with time to create a point in time.")
|
||||
public EnsoDateTime toTime(EnsoTimeOfDay timeOfDay, EnsoZone zone) {
|
||||
return new EnsoDateTime(date.atTime(timeOfDay.asTime()).atZone(zone.asTimeZone()));
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isDate() {
|
||||
return true;
|
||||
@ -81,6 +88,16 @@ public final class EnsoDate implements TruffleObject {
|
||||
return date;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTime() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalTime asTime() throws UnsupportedMessageException {
|
||||
throw UnsupportedMessageException.create();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasFunctionalDispatch() {
|
||||
return true;
|
||||
|
@ -0,0 +1,284 @@
|
||||
package org.enso.interpreter.runtime.data;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.dsl.Builtin;
|
||||
import org.enso.interpreter.node.expression.builtin.error.PolyglotError;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
@ExportLibrary(MethodDispatchLibrary.class)
|
||||
@Builtin(pkg = "date", name = "DateTime", stdlibName = "Standard.Base.Data.Time.Date_Time")
|
||||
public final class EnsoDateTime implements TruffleObject {
|
||||
private final ZonedDateTime dateTime;
|
||||
|
||||
public EnsoDateTime(ZonedDateTime dateTime) {
|
||||
this.dateTime = dateTime;
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return current DateTime")
|
||||
public static EnsoDateTime now() {
|
||||
return new EnsoDateTime(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an instance of EnsoDateTime (ZonedDateTime) from a text string.
|
||||
*
|
||||
* <p>Accepts:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Local date time, such as '2011-12-03T10:15:30' adding system dafault timezone.
|
||||
* <li>Offset date time, such as '2011-12-03T10:15:30+01:00' parsing offset as a timezone.
|
||||
* <li>Zoned date time, such as '2011-12-03T10:15:30+01:00[Europe/Paris]' with optional region
|
||||
* id in square brackets.
|
||||
* </ul>
|
||||
*
|
||||
* @param text the string to parse.
|
||||
* @return parsed ZonedDateTime instance wrapped in EnsoDateTime.
|
||||
*/
|
||||
@Builtin.Method(
|
||||
name = "parse_builtin",
|
||||
description = "Constructs a new DateTime from text with optional pattern")
|
||||
@Builtin.Specialize
|
||||
@Builtin.WrapException(
|
||||
from = DateTimeParseException.class,
|
||||
to = PolyglotError.class,
|
||||
propagate = true)
|
||||
public static EnsoDateTime parse(String text) {
|
||||
TemporalAccessor time = TIME_FORMAT.parseBest(text, ZonedDateTime::from, LocalDateTime::from);
|
||||
if (time instanceof ZonedDateTime) {
|
||||
return new EnsoDateTime((ZonedDateTime) time);
|
||||
} else if (time instanceof LocalDateTime) {
|
||||
return new EnsoDateTime(((LocalDateTime) time).atZone(ZoneId.systemDefault()));
|
||||
}
|
||||
throw new DateTimeException("Text '" + text + "' could not be parsed as Time.");
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "new_builtin",
|
||||
description = "Constructs a new Date from a year, month, and day")
|
||||
@Builtin.WrapException(from = DateTimeException.class, to = PolyglotError.class, propagate = true)
|
||||
public static EnsoDateTime create(
|
||||
long year,
|
||||
long month,
|
||||
long day,
|
||||
long hour,
|
||||
long minute,
|
||||
long second,
|
||||
long nanosecond,
|
||||
EnsoZone zone) {
|
||||
return new EnsoDateTime(
|
||||
ZonedDateTime.of(
|
||||
Math.toIntExact(year),
|
||||
Math.toIntExact(month),
|
||||
Math.toIntExact(day),
|
||||
Math.toIntExact(hour),
|
||||
Math.toIntExact(minute),
|
||||
Math.toIntExact(second),
|
||||
Math.toIntExact(nanosecond),
|
||||
zone.asTimeZone()));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the year")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long year() {
|
||||
return dateTime.getYear();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the month")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long month() {
|
||||
return dateTime.getMonthValue();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the day")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long day() {
|
||||
return dateTime.getDayOfMonth();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the hour")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long hour() {
|
||||
return dateTime.getHour();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the minute")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long minute() {
|
||||
return dateTime.getMinute();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the second")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long second() {
|
||||
return dateTime.getSecond();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets the nanosecond")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long nanosecond() {
|
||||
return dateTime.getNano();
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "zone", description = "Gets the zone")
|
||||
public EnsoZone zone() {
|
||||
return new EnsoZone(dateTime.getZone());
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return the number of seconds from the Unix epoch.")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long toEpochSeconds() {
|
||||
return dateTime.toEpochSecond();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return the number of milliseconds from the Unix epoch.")
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public long toEpochMilliseconds() {
|
||||
return dateTime.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "to_localtime_builtin",
|
||||
description = "Return the localtime of this date time value.")
|
||||
public EnsoTimeOfDay toLocalTime() {
|
||||
return new EnsoTimeOfDay(dateTime.toLocalTime());
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "to_localdate_builtin",
|
||||
description = "Return the localdate of this date time value.")
|
||||
public EnsoDate toLocalDate() {
|
||||
return new EnsoDate(dateTime.toLocalDate());
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime in the provided time zone.")
|
||||
public EnsoDateTime atZone(EnsoZone zone) {
|
||||
return new EnsoDateTime(dateTime.withZoneSameInstant(zone.asTimeZone()));
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "to_time_builtin",
|
||||
description = "Combine this day with time to create a point in time.")
|
||||
public EnsoDateTime toTime(EnsoTimeOfDay timeOfDay, EnsoZone zone) {
|
||||
return new EnsoDateTime(
|
||||
dateTime.toLocalDate().atTime(timeOfDay.asTime()).atZone(zone.asTimeZone()));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
public Text toText() {
|
||||
return Text.create(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(dateTime));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
@Builtin.Specialize
|
||||
public Text format(String pattern) {
|
||||
return Text.create(DateTimeFormatter.ofPattern(pattern).format(dateTime));
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isDate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalDate asDate() {
|
||||
return dateTime.toLocalDate();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTime() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalTime asTime() {
|
||||
return dateTime.toLocalTime();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTimeZone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
ZoneId asTimeZone() {
|
||||
return dateTime.getZone();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasFunctionalDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetFunctionalDispatch {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(InteropLibrary my, UnresolvedSymbol symbol) {
|
||||
Context context = Context.get(my);
|
||||
return symbol.resolveFor(context.getBuiltins().dateTime(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {"cachedSymbol == symbol", "function != null"},
|
||||
limit = "3")
|
||||
static Function resolveCached(
|
||||
EnsoDateTime self,
|
||||
UnresolvedSymbol symbol,
|
||||
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||
@CachedLibrary("self") InteropLibrary mySelf,
|
||||
@Cached("doResolve(mySelf, cachedSymbol)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
EnsoDateTime self, UnresolvedSymbol symbol, @CachedLibrary("self") InteropLibrary mySelf)
|
||||
throws MethodDispatchLibrary.NoSuchMethodException {
|
||||
Function function = doResolve(mySelf, symbol);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchMethodException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public final Object toDisplayString(boolean allowSideEffects) {
|
||||
return DateTimeFormatter.ISO_ZONED_DATE_TIME.format(dateTime);
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter TIME_FORMAT =
|
||||
new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
.parseLenient()
|
||||
.optionalStart()
|
||||
.appendZoneOrOffsetId()
|
||||
.optionalEnd()
|
||||
.parseStrict()
|
||||
.optionalStart()
|
||||
.appendLiteral('[')
|
||||
.parseCaseSensitive()
|
||||
.appendZoneRegionId()
|
||||
.appendLiteral(']')
|
||||
.optionalEnd()
|
||||
.toFormatter();
|
||||
}
|
@ -91,15 +91,17 @@ public class EnsoFile implements TruffleObject {
|
||||
@Builtin.Method(name = "creation_time_builtin")
|
||||
@Builtin.WrapException(from = IOException.class, to = PolyglotError.class, propagate = true)
|
||||
@Builtin.ReturningGuestObject
|
||||
public ZonedDateTime getCreationTime() throws IOException {
|
||||
return ZonedDateTime.ofInstant(truffleFile.getCreationTime().toInstant(), ZoneOffset.UTC);
|
||||
public EnsoDateTime getCreationTime() throws IOException {
|
||||
return new EnsoDateTime(
|
||||
ZonedDateTime.ofInstant(truffleFile.getCreationTime().toInstant(), ZoneOffset.UTC));
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "last_modified_time_builtin")
|
||||
@Builtin.WrapException(from = IOException.class, to = PolyglotError.class, propagate = true)
|
||||
@Builtin.ReturningGuestObject
|
||||
public ZonedDateTime getLastModifiedTime() throws IOException {
|
||||
return ZonedDateTime.ofInstant(truffleFile.getLastModifiedTime().toInstant(), ZoneOffset.UTC);
|
||||
public EnsoDateTime getLastModifiedTime() throws IOException {
|
||||
return new EnsoDateTime(
|
||||
ZonedDateTime.ofInstant(truffleFile.getLastModifiedTime().toInstant(), ZoneOffset.UTC));
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "posix_permissions_builtin")
|
||||
|
@ -0,0 +1,163 @@
|
||||
package org.enso.interpreter.runtime.data;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.dsl.Builtin;
|
||||
import org.enso.interpreter.node.expression.builtin.error.PolyglotError;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
@ExportLibrary(MethodDispatchLibrary.class)
|
||||
@Builtin(pkg = "date", name = "TimeOfDay", stdlibName = "Standard.Base.Data.Time.Time_Of_Day")
|
||||
public class EnsoTimeOfDay implements TruffleObject {
|
||||
private LocalTime localTime;
|
||||
|
||||
public EnsoTimeOfDay(LocalTime localTime) {
|
||||
this.localTime = localTime;
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "parse_builtin",
|
||||
description = "Constructs a new DateTime from text with optional pattern")
|
||||
@Builtin.Specialize
|
||||
@Builtin.WrapException(
|
||||
from = DateTimeParseException.class,
|
||||
to = PolyglotError.class,
|
||||
propagate = true)
|
||||
public static EnsoTimeOfDay parse(String text) {
|
||||
return new EnsoTimeOfDay(LocalTime.parse(text));
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "new_builtin", description = "Constructs a new Time_OF_Day from an hour")
|
||||
@Builtin.WrapException(from = DateTimeException.class, to = PolyglotError.class, propagate = true)
|
||||
public static EnsoTimeOfDay create(long hour, long minute, long second, long nanosecond) {
|
||||
return new EnsoTimeOfDay(
|
||||
LocalTime.of(
|
||||
Math.toIntExact(hour),
|
||||
Math.toIntExact(minute),
|
||||
Math.toIntExact(second),
|
||||
Math.toIntExact(nanosecond)));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value of hour")
|
||||
public static EnsoTimeOfDay now() {
|
||||
return new EnsoTimeOfDay(LocalTime.now());
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value of hour")
|
||||
public long hour() {
|
||||
return localTime.getHour();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value minute")
|
||||
public long minute() {
|
||||
return localTime.getMinute();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value second")
|
||||
public long second() {
|
||||
return localTime.getSecond();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value nanosecond")
|
||||
public long nanosecond() {
|
||||
return localTime.getNano();
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Gets a value second")
|
||||
public long toSeconds() {
|
||||
return localTime.toSecondOfDay();
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "to_time_builtin",
|
||||
description = "Combine this time of day with a date to create a point in time.")
|
||||
public EnsoDateTime toTime(EnsoDate date, EnsoZone zone) {
|
||||
return new EnsoDateTime(localTime.atDate(date.asDate()).atZone(zone.asTimeZone()));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
public Text toText() {
|
||||
return Text.create(DateTimeFormatter.ISO_LOCAL_TIME.format(localTime));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
@Builtin.Specialize
|
||||
public Text format(String pattern) {
|
||||
return Text.create(DateTimeFormatter.ofPattern(pattern).format(localTime));
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTime() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalTime asTime() {
|
||||
return localTime;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
final boolean isDate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalDate asDate() throws UnsupportedMessageException {
|
||||
throw UnsupportedMessageException.create();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasFunctionalDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetFunctionalDispatch {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(InteropLibrary my, UnresolvedSymbol symbol) {
|
||||
Context context = Context.get(my);
|
||||
return symbol.resolveFor(context.getBuiltins().timeOfDay(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {"cachedSymbol == symbol", "function != null"},
|
||||
limit = "3")
|
||||
static Function resolveCached(
|
||||
EnsoTimeOfDay self,
|
||||
UnresolvedSymbol symbol,
|
||||
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||
@CachedLibrary("self") InteropLibrary mySelf,
|
||||
@Cached("doResolve(mySelf, cachedSymbol)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
EnsoTimeOfDay self, UnresolvedSymbol symbol, @CachedLibrary("self") InteropLibrary mySelf)
|
||||
throws MethodDispatchLibrary.NoSuchMethodException {
|
||||
Function function = doResolve(mySelf, symbol);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchMethodException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package org.enso.interpreter.runtime.data;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.dsl.Builtin;
|
||||
import org.enso.interpreter.node.expression.builtin.error.PolyglotError;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.zone.ZoneRulesException;
|
||||
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
@ExportLibrary(MethodDispatchLibrary.class)
|
||||
@Builtin(pkg = "date", name = "Zone", stdlibName = "Standard.Base.Data.Time.Zone")
|
||||
public final class EnsoZone implements TruffleObject {
|
||||
private final ZoneId zone;
|
||||
|
||||
public EnsoZone(ZoneId zone) {
|
||||
this.zone = zone;
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Get the unique identifier for your system's current timezone.")
|
||||
public Text zoneId() {
|
||||
return Text.create(this.zone.getId());
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "parse_builtin", description = "Parse the ID producing EnsoZone.")
|
||||
@Builtin.Specialize
|
||||
@Builtin.WrapException(
|
||||
from = ZoneRulesException.class,
|
||||
to = PolyglotError.class,
|
||||
propagate = true)
|
||||
public static EnsoZone parse(String text) {
|
||||
return new EnsoZone(ZoneId.of(text));
|
||||
}
|
||||
|
||||
@Builtin.Method(
|
||||
name = "new_builtin",
|
||||
description =
|
||||
"Obtains an instance of `Zone` using an offset in hours, minutes and seconds from the UTC zone.")
|
||||
@Builtin.WrapException(from = DateTimeException.class, to = PolyglotError.class, propagate = true)
|
||||
public static EnsoZone create(long hours, long minutes, long seconds) {
|
||||
return new EnsoZone(
|
||||
ZoneOffset.ofHoursMinutesSeconds(
|
||||
Math.toIntExact(hours), Math.toIntExact(minutes), Math.toIntExact(seconds)));
|
||||
}
|
||||
|
||||
@Builtin.Method(name = "system", description = "The system default timezone.")
|
||||
public static EnsoZone system() {
|
||||
return new EnsoZone(ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTimeZone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
ZoneId asTimeZone() {
|
||||
return zone;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasFunctionalDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetFunctionalDispatch {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(InteropLibrary my, UnresolvedSymbol symbol) {
|
||||
Context context = Context.get(my);
|
||||
return symbol.resolveFor(context.getBuiltins().zone(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {"cachedSymbol == symbol", "function != null"},
|
||||
limit = "3")
|
||||
static Function resolveCached(
|
||||
EnsoZone self,
|
||||
UnresolvedSymbol symbol,
|
||||
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||
@CachedLibrary("self") InteropLibrary mySelf,
|
||||
@Cached("doResolve(mySelf, cachedSymbol)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
EnsoZone self, UnresolvedSymbol symbol, @CachedLibrary("self") InteropLibrary mySelf)
|
||||
throws MethodDispatchLibrary.NoSuchMethodException {
|
||||
Function function = doResolve(mySelf, symbol);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchMethodException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,10 @@ import org.enso.polyglot.data.TypeGraph;
|
||||
PanicSentinel.class,
|
||||
Warning.class,
|
||||
EnsoFile.class,
|
||||
EnsoDate.class
|
||||
EnsoDate.class,
|
||||
EnsoDateTime.class,
|
||||
EnsoTimeOfDay.class,
|
||||
EnsoZone.class,
|
||||
})
|
||||
public class Types {
|
||||
|
||||
@ -221,6 +224,10 @@ public class Types {
|
||||
graph.insert(ConstantsGen.PANIC, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.REF, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.TEXT, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.DATE, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.DATE_TIME, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.TIME_OF_DAY, ConstantsGen.ANY);
|
||||
graph.insert(ConstantsGen.ZONE, ConstantsGen.ANY);
|
||||
graph.insertWithoutParent(ConstantsGen.PANIC);
|
||||
graph.insertWithoutParent(Constants.THUNK);
|
||||
graph.insertWithoutParent(Constants.UNRESOLVED_SYMBOL);
|
||||
|
@ -33,23 +33,6 @@ class DateTest extends InterpreterTest {
|
||||
consumeOut shouldEqual List("2022-04-01")
|
||||
}
|
||||
|
||||
"send enso date into java" in {
|
||||
val code =
|
||||
s"""import Standard.Base.IO
|
||||
|polyglot java import java.time.LocalTime
|
||||
|import Standard.Base.Data.Time.Date
|
||||
|
|
||||
|main =
|
||||
| ensodate = Date.new 2022 04 01
|
||||
| javatime = LocalTime.of 10 26
|
||||
| javatimedate = javatime.atDate ensodate
|
||||
| javadate = javatimedate . toLocalDate
|
||||
| IO.println javadate
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022-04-01")
|
||||
}
|
||||
|
||||
"check java date has enso methods" in {
|
||||
val code =
|
||||
s"""import Standard.Base.IO
|
||||
|
@ -43,6 +43,9 @@ public record TypeWithKind(String baseType, TypeKind kind) {
|
||||
"org.enso.interpreter.runtime.data.ArrayOverBuffer",
|
||||
"org.enso.interpreter.runtime.data.EnsoFile",
|
||||
"org.enso.interpreter.runtime.data.EnsoDate",
|
||||
"org.enso.interpreter.runtime.data.EnsoDateTime",
|
||||
"org.enso.interpreter.runtime.data.EnsoTimeOfDay",
|
||||
"org.enso.interpreter.runtime.data.EnsoZone",
|
||||
"org.enso.interpreter.runtime.data.ManagedResource",
|
||||
"org.enso.interpreter.runtime.data.Ref",
|
||||
"org.enso.interpreter.runtime.data.text.Text",
|
||||
|
@ -1,12 +1,6 @@
|
||||
package org.enso.base;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.Period;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
@ -41,6 +35,11 @@ public class Time_Utils {
|
||||
.toFormatter();
|
||||
}
|
||||
|
||||
public enum AdjustOp {
|
||||
PLUS,
|
||||
MINUS
|
||||
}
|
||||
|
||||
/** @return default Time formatter. */
|
||||
public static DateTimeFormatter default_time_formatter() {
|
||||
return DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
@ -64,18 +63,71 @@ public class Time_Utils {
|
||||
return date.atTime(time).atZone(zone);
|
||||
}
|
||||
|
||||
public static LocalDate date_adjust(LocalDate date, long add, Period duration) {
|
||||
return add == 1 ? date.plus(duration) : date.minus(duration);
|
||||
public static LocalDate date_adjust(LocalDate date, AdjustOp op, Period duration) {
|
||||
switch (op) {
|
||||
case PLUS:
|
||||
return date.plus(duration);
|
||||
case MINUS:
|
||||
return date.minus(duration);
|
||||
default:
|
||||
throw new DateTimeException("Unknown adjust operation");
|
||||
}
|
||||
}
|
||||
|
||||
public static long week_of_year(LocalDate date, Locale locale) {
|
||||
public static ZonedDateTime datetime_adjust(
|
||||
ZonedDateTime datetime, AdjustOp op, Period period, Duration duration) {
|
||||
switch (op) {
|
||||
case PLUS:
|
||||
return datetime.plus(period).plus(duration);
|
||||
case MINUS:
|
||||
return datetime.minus(period).minus(duration);
|
||||
default:
|
||||
throw new DateTimeException("Unknown adjust operation");
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalTime time_adjust(LocalTime time, AdjustOp op, Duration duration) {
|
||||
switch (op) {
|
||||
case PLUS:
|
||||
return time.plus(duration);
|
||||
case MINUS:
|
||||
return time.minus(duration);
|
||||
default:
|
||||
throw new DateTimeException("Unknown adjust operation");
|
||||
}
|
||||
}
|
||||
|
||||
public static long week_of_year_localdate(LocalDate date, Locale locale) {
|
||||
return WeekFields.of(locale).weekOfYear().getFrom(date);
|
||||
}
|
||||
|
||||
public static int compare_to(LocalDate self, LocalDate that) {
|
||||
public static long week_of_year_zoneddatetime(ZonedDateTime date, Locale locale) {
|
||||
return WeekFields.of(locale).weekOfYear().getFrom(date);
|
||||
}
|
||||
|
||||
public static Duration duration_between(
|
||||
ZonedDateTime start, ZonedDateTime end, boolean timezoneAware) {
|
||||
return timezoneAware
|
||||
? Duration.between(start, end)
|
||||
: Duration.between(start.toLocalDateTime(), end.toLocalDateTime());
|
||||
}
|
||||
|
||||
public static int compare_to_localdate(LocalDate self, LocalDate that) {
|
||||
return self.compareTo(that);
|
||||
}
|
||||
|
||||
public static int compare_to_zoneddatetime(ZonedDateTime self, ZonedDateTime that) {
|
||||
return self.compareTo(that);
|
||||
}
|
||||
|
||||
public static int compare_to_localtime(LocalTime self, LocalTime that) {
|
||||
return self.compareTo(that);
|
||||
}
|
||||
|
||||
public static boolean equals_zone(ZoneId self, ZoneId that) {
|
||||
return self.equals(that);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an instance of ZonedDateTime from a text string.
|
||||
*
|
||||
@ -91,7 +143,7 @@ public class Time_Utils {
|
||||
* @param text the string to parse.
|
||||
* @return parsed ZonedDateTime instance.
|
||||
*/
|
||||
public static ZonedDateTime parse_time(String text) {
|
||||
public static ZonedDateTime parse_datetime(String text) {
|
||||
TemporalAccessor time = TIME_FORMAT.parseBest(text, ZonedDateTime::from, LocalDateTime::from);
|
||||
if (time instanceof ZonedDateTime) {
|
||||
return (ZonedDateTime) time;
|
||||
@ -109,9 +161,10 @@ public class Time_Utils {
|
||||
*
|
||||
* @param text the string to parse.
|
||||
* @param pattern the format string.
|
||||
* @param locale localization config to be uses in the formatter.
|
||||
* @return parsed ZonedDateTime instance.
|
||||
*/
|
||||
public static ZonedDateTime parse_time_format(String text, String pattern, Locale locale) {
|
||||
public static ZonedDateTime parse_datetime_format(String text, String pattern, Locale locale) {
|
||||
TemporalAccessor time =
|
||||
DateTimeFormatter.ofPattern(pattern)
|
||||
.withLocale(locale)
|
||||
@ -123,4 +176,17 @@ public class Time_Utils {
|
||||
}
|
||||
throw new DateTimeException("Text '" + text + "' could not be parsed as Time.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an instance of LocalTime from a text string using a custom string.
|
||||
*
|
||||
* @param text the string to parse.
|
||||
* @param pattern the format string.
|
||||
* @param locale localization config to be uses in the formatter.
|
||||
* @return parsed LocalTime instance.
|
||||
*/
|
||||
public static LocalTime parse_time(String text, String pattern, Locale locale) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return (LocalTime.parse(text, formatter.withLocale(locale)));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.table.data.table;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
@ -120,11 +122,30 @@ public class Column {
|
||||
public static Column fromItems(String name, List<Value> items) {
|
||||
InferredBuilder builder = new InferredBuilder(items.size());
|
||||
for (var item : items) {
|
||||
builder.appendNoGrow(item.isDate() ? item.asDate() : item.as(Object.class));
|
||||
builder.appendNoGrow(convertDateOrTime(item));
|
||||
}
|
||||
return new Column(name, new DefaultIndex(items.size()), builder.seal());
|
||||
}
|
||||
|
||||
private static Object convertDateOrTime(Value item) {
|
||||
if (item.isDate()) {
|
||||
LocalDate d = item.asDate();
|
||||
if (item.isTime()) {
|
||||
LocalDateTime dtime = d.atTime(item.asTime());
|
||||
if (item.isTimeZone()) {
|
||||
return dtime.atZone(item.asTimeZone());
|
||||
} else {
|
||||
return dtime;
|
||||
}
|
||||
} else {
|
||||
return d;
|
||||
}
|
||||
} else if (item.isTime()) {
|
||||
return item.asTime();
|
||||
}
|
||||
return item.as(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the index of this column.
|
||||
*
|
||||
|
@ -31,6 +31,6 @@ public class DateFormatter implements DataFormatter {
|
||||
|
||||
@Override
|
||||
public boolean canFormat(Object value) {
|
||||
return value instanceof LocalDate || (value instanceof Value v && v.isDate());
|
||||
return value instanceof LocalDate || (value instanceof Value v && v.isDate() && !v.isTime());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.table.formatting;
|
||||
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Locale;
|
||||
@ -17,13 +19,19 @@ public class DateTimeFormatter implements DataFormatter {
|
||||
return NULL_REPRESENTATION;
|
||||
}
|
||||
|
||||
if (value instanceof Value v && v.isDate() && v.isTime()) {
|
||||
value = v.asDate().atTime(v.asTime());
|
||||
if (v.isTimeZone()) {
|
||||
value = ((LocalDateTime) value).atZone(v.asTimeZone());
|
||||
}
|
||||
}
|
||||
|
||||
if (value instanceof LocalDateTime date) {
|
||||
return date.format(formatter);
|
||||
}
|
||||
|
||||
// Currently Enso uses ZonedDateTime for the date-time type. This should be revisited along with the Datetime API.
|
||||
if (value instanceof ZonedDateTime date) {
|
||||
return date.toLocalDateTime().format(formatter);
|
||||
return date.format(formatter);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported type for DateTimeFormatter.");
|
||||
@ -31,6 +39,6 @@ public class DateTimeFormatter implements DataFormatter {
|
||||
|
||||
@Override
|
||||
public boolean canFormat(Object value) {
|
||||
return value instanceof LocalDateTime || value instanceof ZonedDateTime;
|
||||
return value instanceof LocalDateTime || value instanceof ZonedDateTime || (value instanceof Value v && v.isDate() && v.isTime());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.table.formatting;
|
||||
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
@ -17,6 +19,10 @@ public class TimeFormatter implements DataFormatter {
|
||||
return NULL_REPRESENTATION;
|
||||
}
|
||||
|
||||
if (value instanceof Value v && v.isTime()) {
|
||||
value = v.asTime();
|
||||
}
|
||||
|
||||
if (value instanceof LocalTime date) {
|
||||
return date.format(formatter);
|
||||
}
|
||||
@ -26,6 +32,6 @@ public class TimeFormatter implements DataFormatter {
|
||||
|
||||
@Override
|
||||
public boolean canFormat(Object value) {
|
||||
return value instanceof LocalTime;
|
||||
return value instanceof LocalTime || (value instanceof Value v && !v.isDate() && v.isTime());
|
||||
}
|
||||
}
|
||||
|
7
test/Table_Tests/data/datetime_sample.csv
Normal file
7
test/Table_Tests/data/datetime_sample.csv
Normal file
@ -0,0 +1,7 @@
|
||||
Serial number,Movement type,Posting date
|
||||
2LMXK1,101,2015-01-05 09:00:00
|
||||
2LMXK1,301,2015-01-05 14:00:00
|
||||
JEMLP3,101,2015-01-06 09:00:00
|
||||
JEMLP3,203,2015-01-07 17:30:00
|
||||
BR83GP,101,2011-01-05 09:00:00
|
||||
BR83GP,301,2011-01-09 15:30:00
|
|
7
test/Table_Tests/data/time_of_day_sample.csv
Normal file
7
test/Table_Tests/data/time_of_day_sample.csv
Normal file
@ -0,0 +1,7 @@
|
||||
Serial number,Movement type,Posting time
|
||||
2LMXK1,101,09:00:00
|
||||
2LMXK1,301,14:00:12
|
||||
JEMLP3,101,09:00:00
|
||||
JEMLP3,203,17:30:00
|
||||
BR83GP,101,09:00:04
|
||||
BR83GP,301,15:30:00
|
|
@ -1,6 +1,8 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Time_Of_Day
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
import Standard.Table
|
||||
from Standard.Table import Column, Data_Formatter, Quote_Style
|
||||
@ -137,14 +139,17 @@ spec =
|
||||
Test.specify "should format dates" <|
|
||||
formatter = Data_Formatter
|
||||
formatter.format (Date.new 2022) . should_equal "2022-01-01"
|
||||
formatter.format (Time.new 1999 . internal_zoned_date_time) . should_equal "1999-01-01 00:00:00"
|
||||
formatter.format (Time_Of_Day.new . internal_local_time) . should_equal "00:00:00"
|
||||
formatter.format (Date_Time.new 1999) . should_equal "1999-01-01 00:00:00"
|
||||
formatter.format (Date_Time.new 1999 zone=Zone.utc) . should_equal "1999-01-01 00:00:00"
|
||||
formatter.format (Date_Time.new 1999 zone=(Zone.parse "America/Los_Angeles")) . should_equal "1999-01-01 00:00:00"
|
||||
formatter.format (Time_Of_Day.new) . should_equal "00:00:00"
|
||||
|
||||
Test.specify "should allow custom date formats" <|
|
||||
formatter = Data_Formatter date_formats=["E, d MMM y", "d MMM y[ G]"] datetime_formats=["dd/MM/yyyy HH:mm"] time_formats=["h:mma"] datetime_locale=Locale.uk
|
||||
formatter = Data_Formatter date_formats=["E, d MMM y", "d MMM y[ G]"] datetime_formats=["dd/MM/yyyy HH:mm [z]"] time_formats=["h:mma"] datetime_locale=Locale.uk
|
||||
formatter.format (Date.new 2022 06 21) . should_equal "Tue, 21 Jun 2022"
|
||||
formatter.format (Time.new 1999 02 03 04 56 11 . internal_zoned_date_time) . should_equal "03/02/1999 04:56"
|
||||
formatter.format (Time_Of_Day.new 13 55 . internal_local_time) . should_equal "1:55pm"
|
||||
formatter.format (Date_Time.new 1999 02 03 04 56 11 zone=Zone.utc) . should_equal "03/02/1999 04:56 UTC"
|
||||
formatter.format (Date_Time.new 1999 02 03 04 56 11 zone=(Zone.parse "America/Los_Angeles")) . should_equal "03/02/1999 04:56 GMT-08:00"
|
||||
formatter.format (Time_Of_Day.new 13 55) . should_equal "1:55pm"
|
||||
|
||||
Test.specify "should act as identity on Text" <|
|
||||
formatter = Data_Formatter
|
||||
|
@ -166,7 +166,7 @@ spec =
|
||||
|
||||
Test.specify 'should allow to always quote text and custom values, but for non-text primitves only if absolutely necessary' <|
|
||||
format = Delimited "," value_formatter=(Data_Formatter thousand_separator='"' date_formats=["E, d MMM y"]) . with_quotes always_quote=True quote_escape='\\'
|
||||
table = Table.new [['The Column "Name"', ["foo","'bar'",'"baz"', 'one, two, three']], ["B", [1.0, 1000000.5, 2.2, -1.5]], ["C", ["foo", My_Type 44, (Date.new 2022 06 21), 42]], ["D", [1,2,3,4000]], ["E", [Nothing, (Time_Of_Day.new 13 55 . internal_local_time), Nothing, Nothing]]]
|
||||
table = Table.new [['The Column "Name"', ["foo","'bar'",'"baz"', 'one, two, three']], ["B", [1.0, 1000000.5, 2.2, -1.5]], ["C", ["foo", My_Type 44, (Date.new 2022 06 21), 42]], ["D", [1,2,3,4000]], ["E", [Nothing, (Time_Of_Day.new 13 55), Nothing, Nothing]]]
|
||||
file = (enso_project.data / "transient" / "quote_always.csv")
|
||||
file.delete_if_exists
|
||||
table.write file format on_problems=Report_Error . should_succeed
|
||||
|
53
test/Table_Tests/src/Table_Time_Of_Day_Spec.enso
Normal file
53
test/Table_Tests/src/Table_Time_Of_Day_Spec.enso
Normal file
@ -0,0 +1,53 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Text.Line_Ending_Style
|
||||
import Standard.Base.Data.Time.Time_Of_Day
|
||||
|
||||
import Standard.Table
|
||||
import Standard.Table.Data.Column
|
||||
import Standard.Table.IO.File_Format
|
||||
from Standard.Table.Data.Data_Formatter as Data_Formatter_Module import Data_Formatter
|
||||
|
||||
import Standard.Test
|
||||
|
||||
from project.Util import all
|
||||
|
||||
spec =
|
||||
c_number = ["Serial number", ["2LMXK1", "2LMXK1", "JEMLP3", "JEMLP3", "BR83GP", "BR83GP"]]
|
||||
c_type = ["Movement type", [101, 301, 101, 203, 101, 301]]
|
||||
c_time = ["Posting time", [Time_Of_Day.new 9 0, Time_Of_Day.new 14 0 12, Time_Of_Day.new 9 0, Time_Of_Day.new 17 30, Time_Of_Day.new 9 0 4, Time_Of_Day.new 15 30]]
|
||||
expected = Table.new [c_number, c_type, c_time]
|
||||
|
||||
Test.group "File.read (Delimited) should work with Time_Of_Days" <|
|
||||
table = (enso_project.data / "time_of_day_sample.csv").read
|
||||
Test.specify "should be able to read in a table with dates" <|
|
||||
table.column_count.should_equal 3
|
||||
table.columns.map (.name) . should_equal ['Serial number','Movement type', 'Posting time']
|
||||
table.row_count.should_equal 6
|
||||
|
||||
Test.specify "should be able to treat a single value as a Time_Of_Days" <|
|
||||
from_column = table.at 'Posting time'
|
||||
from_column.at 5 . hour . should_equal 15
|
||||
from_column.at 5 . minute . should_equal 30
|
||||
from_column.at 5 . should_equal (Time_Of_Day.new 15 30)
|
||||
|
||||
Test.specify "should be able to compare columns and table" <|
|
||||
table.at 'Serial number' . should_equal (Column.from_vector c_number.first c_number.second)
|
||||
table.at 'Movement type' . should_equal (Column.from_vector c_type.first c_type.second)
|
||||
table.at 'Posting time' . should_equal (Column.from_vector c_time.first c_time.second)
|
||||
table.should_equal expected
|
||||
|
||||
Test.group "Should be able to serialise a table with Time_Of_Days to Text" <|
|
||||
Test.specify "should serialise back to input" <|
|
||||
expected_text = normalize_lines <|
|
||||
(enso_project.data / "time_of_day_sample.csv").read_text
|
||||
delimited = Text.from expected format=(File_Format.Delimited "," line_endings=Line_Ending_Style.Unix)
|
||||
delimited.should_equal expected_text
|
||||
|
||||
Test.specify "should serialise dates with format" <|
|
||||
test_table = Table.new [c_time]
|
||||
expected_text = 'Posting time\n09-00-00\n14-00-12\n09-00-00\n17-30-00\n09-00-04\n15-30-00\n'
|
||||
data_formatter = Data_Formatter . with_datetime_formats time_formats=["HH-mm-ss"]
|
||||
delimited = Text.from test_table format=(File_Format.Delimited "," value_formatter=data_formatter line_endings=Line_Ending_Style.Unix)
|
||||
delimited.should_equal expected_text
|
||||
|
||||
main = Test.Suite.run_main spec
|
52
test/Table_Tests/src/Table_Time_Spec.enso
Normal file
52
test/Table_Tests/src/Table_Time_Spec.enso
Normal file
@ -0,0 +1,52 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Text.Line_Ending_Style
|
||||
import Standard.Base.Data.Time
|
||||
|
||||
import Standard.Table
|
||||
import Standard.Table.Data.Column
|
||||
import Standard.Table.IO.File_Format
|
||||
from Standard.Table.Data.Data_Formatter as Data_Formatter_Module import Data_Formatter
|
||||
|
||||
import Standard.Test
|
||||
|
||||
from project.Util import all
|
||||
|
||||
spec =
|
||||
c_number = ["Serial number", ["2LMXK1", "2LMXK1", "JEMLP3", "JEMLP3", "BR83GP", "BR83GP"]]
|
||||
c_type = ["Movement type", [101, 301, 101, 203, 101, 301]]
|
||||
c_date = ["Posting date", [Date_Time.new 2015 1 5 9 0, Date_Time.new 2015 1 5 14 0, Date_Time.new 2015 1 6 9 0, Date_Time.new 2015 1 7 17 30, Date_Time.new 2011 1 5 9 0, Date_Time.new 2011 1 9 15 30]]
|
||||
expected = Table.new [c_number, c_type, c_date]
|
||||
|
||||
Test.group "File.read (Delimited) should work with Date_Time" <|
|
||||
table = (enso_project.data / "datetime_sample.csv").read
|
||||
Test.specify "should be able to read in a table with dates" <|
|
||||
table.column_count.should_equal 3
|
||||
table.columns.map (.name) . should_equal ['Serial number','Movement type', 'Posting date']
|
||||
table.row_count.should_equal 6
|
||||
|
||||
Test.specify "should be able to treat a single value as a Date_Time" <|
|
||||
from_column = table.at 'Posting date'
|
||||
from_column.at 5 . year . should_equal 2011
|
||||
from_column.at 5 . should_equal (Date_Time.new 2011 1 9 15 30)
|
||||
|
||||
Test.specify "should be able to compare columns and table" <|
|
||||
table.at 'Serial number' . should_equal (Column.from_vector c_number.first c_number.second)
|
||||
table.at 'Movement type' . should_equal (Column.from_vector c_type.first c_type.second)
|
||||
table.at 'Posting date' . should_equal (Column.from_vector c_date.first c_date.second)
|
||||
table.should_equal expected
|
||||
|
||||
Test.group "Should be able to serialise a table with DateTimes to Text" <|
|
||||
Test.specify "should serialise back to input" <|
|
||||
expected_text = normalize_lines <|
|
||||
(enso_project.data / "datetime_sample.csv").read_text
|
||||
delimited = Text.from expected format=(File_Format.Delimited "," line_endings=Line_Ending_Style.Unix)
|
||||
delimited.should_equal expected_text
|
||||
|
||||
Test.specify "should serialise dates with format" <|
|
||||
test_table = Table.new [c_date]
|
||||
expected_text = 'Posting date\n05.01.2015 09-00\n05.01.2015 14-00\n06.01.2015 09-00\n07.01.2015 17-30\n05.01.2011 09-00\n09.01.2011 15-30\n'
|
||||
data_formatter = Data_Formatter . with_datetime_formats datetime_formats=["dd.MM.yyyy HH-mm"]
|
||||
delimited = Text.from test_table format=(File_Format.Delimited "," value_formatter=data_formatter line_endings=Line_Ending_Style.Unix)
|
||||
delimited.should_equal expected_text
|
||||
|
||||
main = Test.Suite.run_main spec
|
@ -148,12 +148,20 @@ js_parse text format=Nothing =
|
||||
d = Date.parse text format
|
||||
js_date d.year d.month d.day
|
||||
|
||||
## JSDate is not only a date but also time and timezone.
|
||||
## Here, we explicitly convert JS ZonedDateTime to LocalDate
|
||||
js_set_zone local_datetime =
|
||||
zone = Zone.utc
|
||||
datetime_with_tz = local_datetime.at_zone(zone)
|
||||
diff = Duration.between datetime_with_tz local_datetime (timezone_aware=False)
|
||||
(datetime_with_tz + diff).to_localdate_builtin
|
||||
|
||||
js_date year month=1 day=1 =
|
||||
Panic.catch Any (js_date_impl year month day) (err -> Error.throw (Time.Time_Error err.payload.cause))
|
||||
Panic.catch Any (js_set_zone (js_date_impl year month day)) (err -> Error.throw (Time.Time_Error err.payload.cause))
|
||||
|
||||
js_array_date year month=1 day=1 =
|
||||
arr = Panic.catch Any (js_array_dateCreate year month day) (err -> Error.throw (Time.Time_Error err.payload.cause))
|
||||
arr.at(0)
|
||||
js_set_zone arr.at(0)
|
||||
|
||||
java_parse date_text pattern=Nothing =
|
||||
if pattern == Nothing then Panic.catch Polyglot_Error (LocalDate.parse date_text) (err -> Error.throw (Time.Time_Error err.payload.cause.getMessage)) else
|
||||
|
@ -1,7 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
|
||||
import Standard.Test
|
||||
|
||||
@ -37,8 +37,8 @@ spec =
|
||||
interval.to_vector . should_equal [-11, 0, 0, 2, 0, 12, 0]
|
||||
|
||||
Test.specify "should create interval between two points in time" <|
|
||||
time1 = Time.new 2001 1 2
|
||||
time2 = Time.new 2001 2 1
|
||||
time1 = Date_Time.new 2001 1 2
|
||||
time2 = Date_Time.new 2001 2 1
|
||||
interval = Duration.between time1 time2
|
||||
interval.to_vector . should_equal [0, 0, 0, 720, 0, 0, 0]
|
||||
|
||||
@ -99,3 +99,5 @@ spec =
|
||||
duration_1!=duration_2 . should_be_true
|
||||
duration_1>duration_2 . should_be_true
|
||||
duration_1<duration_2 . should_be_false
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
@ -7,59 +7,66 @@ import Standard.Base.Data.Time.Zone
|
||||
|
||||
import Standard.Test
|
||||
|
||||
polyglot java import java.time.LocalTime
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
|
||||
spec =
|
||||
Test.group "Time_Of_Day" <|
|
||||
specWith "Time_Of_Day" enso_time Time_Of_Day.parse
|
||||
specWith "JavaLocalTime" java_time java_parse
|
||||
|
||||
specWith name create_new_time parse_time =
|
||||
Test.group name <|
|
||||
|
||||
Test.specify "should create local time" <|
|
||||
time = Time_Of_Day.new 1 0 0
|
||||
time = create_new_time 1 0 0
|
||||
time . hour . should_equal 1
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . to_seconds . should_equal 3600
|
||||
|
||||
Test.specify "should handle errors when creating a time" <|
|
||||
case Time_Of_Day.new 24 0 0 . catch of
|
||||
case create_new_time 24 0 0 . catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Invalid value for HourOfDay (valid values 0 - 23): 24"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should format local time using provided pattern" <|
|
||||
text = Time_Of_Day.new 12 20 44 . format "HHmmss"
|
||||
text = create_new_time 12 20 44 . format "HHmmss"
|
||||
text . should_equal "122044"
|
||||
|
||||
Test.specify "should format local time using default pattern" <|
|
||||
text = Time_Of_Day.new 12 20 44 . to_text
|
||||
text = create_new_time 12 20 44 . to_text
|
||||
text . should_equal "12:20:44"
|
||||
|
||||
Test.specify "should convert to Json" <|
|
||||
time = Time_Of_Day.new 1 2 3
|
||||
time = create_new_time 1 2 3
|
||||
time.to_json.should_equal <|
|
||||
time_pairs = [["hour", time.hour], ["minute", time.minute], ["second", time.second], ["nanosecond", time.nanosecond]]
|
||||
Json.from_pairs ([["type", "Time_Of_Day"]] + time_pairs)
|
||||
|
||||
Test.specify "should parse default time format" <|
|
||||
text = Time_Of_Day.new 12 20 44 . to_text
|
||||
text = create_new_time 12 20 44 . to_text
|
||||
time = Time_Of_Day.parse text
|
||||
time.to_seconds . should_equal 44444
|
||||
|
||||
Test.specify "should parse local time" <|
|
||||
time = Time_Of_Day.parse "10:00:00"
|
||||
time = parse_time "10:00:00"
|
||||
time.to_seconds . should_equal 36000
|
||||
|
||||
Test.specify "should throw error when parsing invalid time" <|
|
||||
case Time_Of_Day.parse "1200" . catch of
|
||||
case parse_time "1200" . catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '1200' could not be parsed at index 2"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should parse custom format" <|
|
||||
time = Time_Of_Day.parse "12:30AM" "hh:mma"
|
||||
time = parse_time "12:30AM" "hh:mma"
|
||||
time.to_seconds . should_equal 1800
|
||||
|
||||
Test.specify "should throw error when parsing custom format" <|
|
||||
time = Time_Of_Day.parse "12:30" "HH:mm:ss"
|
||||
time = parse_time "12:30" "HH:mm:ss"
|
||||
case time.catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '12:30' could not be parsed at index 5"
|
||||
@ -67,48 +74,60 @@ spec =
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should convert to time" <|
|
||||
time = Time_Of_Day.new 1 0 0 . to_time (Date.new 2000 12 21) Zone.utc
|
||||
time . year . should_equal 2000
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 21
|
||||
time . hour . should_equal 1
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
datetime = create_new_time 1 0 0 . to_time (Date.new 2000 12 21) Zone.utc
|
||||
datetime . year . should_equal 2000
|
||||
datetime . month . should_equal 12
|
||||
datetime . day . should_equal 21
|
||||
datetime . hour . should_equal 1
|
||||
datetime . minute . should_equal 0
|
||||
datetime . second . should_equal 0
|
||||
datetime . nanosecond . should_equal 0
|
||||
datetime . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should add time-based interval" <|
|
||||
time = Time_Of_Day.new + 1.minute
|
||||
time = create_new_time 0 + 1.minute
|
||||
time . to_seconds . should_equal 60
|
||||
|
||||
Test.specify "should subtract time-based interval" <|
|
||||
time = Time_Of_Day.new - 1.minute
|
||||
time = create_new_time 0 - 1.minute
|
||||
time . to_seconds . should_equal 86340
|
||||
|
||||
Test.specify "should support mixed interval operators" <|
|
||||
time = Time_Of_Day.new + 1.hour - 1.second
|
||||
time = create_new_time 0 + 1.hour - 1.second
|
||||
time . to_seconds . should_equal 3599
|
||||
|
||||
Test.specify "should throw error when adding date-based interval" <|
|
||||
case (Time_Of_Day.new + 1.day) . catch of
|
||||
case (create_new_time 0 + 1.day) . catch of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should throw error when subtracting date-based interval" <|
|
||||
case (Time_Of_Day.new - (1.day - 1.minute)) . catch of
|
||||
case (create_new_time 0 - (1.day - 1.minute)) . catch of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should be comparable" <|
|
||||
time_1 = Time_Of_Day.parse "12:30:12.7102"
|
||||
time_2 = Time_Of_Day.parse "04:00:10.0"
|
||||
time_1 = parse_time "12:30:12.7102"
|
||||
time_2 = parse_time "04:00:10.0"
|
||||
(time_1 == time_2) . should_be_false
|
||||
time_1==time_1 . should_be_true
|
||||
time_1!=time_2 . should_be_true
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
enso_time hour minute=0 second=0 nanoOfSecond=0 =
|
||||
Time_Of_Day.new hour minute second nanoOfSecond
|
||||
|
||||
java_time hour minute=0 second=0 nanoOfSecond=0 =
|
||||
Panic.catch Any (LocalTime.of hour minute second nanoOfSecond) (err -> Error.throw (Time.Time_Error <| err.payload.to_display_text.drop (Text_Sub_Range.First 16)))
|
||||
|
||||
java_parse time_text pattern=Nothing =
|
||||
if pattern == Nothing then Panic.catch Polyglot_Error (LocalTime.parse time_text) (err -> Error.throw (Time.Time_Error err.payload.cause.getMessage)) else
|
||||
formatter = DateTimeFormatter.ofPattern(pattern)
|
||||
Panic.catch Polyglot_Error (LocalTime.parse time_text formatter) (err -> Error.throw (Time.Time_Error err.payload.cause.getMessage))
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
@ -1,16 +1,27 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
import Standard.Test
|
||||
|
||||
polyglot java import java.time.ZonedDateTime
|
||||
polyglot java import java.time.LocalDateTime
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
|
||||
spec =
|
||||
Test.group "Time" <|
|
||||
specWith "Date_Time" Date_Time.new Date_Time.parse
|
||||
specWith "JavascriptDate" js_datetime js_parse nanoseconds_loss_in_precision=True
|
||||
specWith "JavaZonedDateTime" java_datetime java_parse
|
||||
specWith "JavascriptDataInArray" js_array_datetime js_parse nanoseconds_loss_in_precision=True
|
||||
|
||||
specWith name create_new_datetime parse_datetime nanoseconds_loss_in_precision=False =
|
||||
Test.group name <|
|
||||
|
||||
Test.specify "should create time" <|
|
||||
time = Time.new 1970 (zone = Zone.utc)
|
||||
time = create_new_datetime 1970 (zone = Zone.utc)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -21,30 +32,30 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should handle errors when creating time" <|
|
||||
case Time.new 1970 0 0 . catch of
|
||||
case create_new_datetime 1970 0 0 . catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 0"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should format using provided pattern" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . format "yyyy-MM-dd'T'HH:mm:ss"
|
||||
text = create_new_datetime 1970 (zone = Zone.utc) . format "yyyy-MM-dd'T'HH:mm:ss"
|
||||
text . should_equal "1970-01-01T00:00:00"
|
||||
|
||||
Test.specify "should format using default pattern" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . to_text
|
||||
text = create_new_datetime 1970 (zone = Zone.utc) . to_text
|
||||
text . should_equal "1970-01-01T00:00:00Z[UTC]"
|
||||
|
||||
Test.specify "should convert to Json" <|
|
||||
time = Time.new 1970 12 21 (zone = Zone.utc)
|
||||
time = create_new_datetime 1970 12 21 (zone = Zone.utc)
|
||||
time.to_json.should_equal <|
|
||||
zone_pairs = [["zone", Zone.utc]]
|
||||
time_pairs = [["year", time.year], ["month", time.month], ["day", time.day], ["hour", time.hour], ["minute", time.minute], ["second", time.second], ["nanosecond", time.nanosecond]]
|
||||
Json.from_pairs ([["type", "Time"]] + time_pairs + zone_pairs)
|
||||
|
||||
Test.specify "should parse default time format" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . to_text
|
||||
time = Time.parse text
|
||||
text = create_new_datetime 1970 (zone = Zone.utc) . to_text
|
||||
time = parse_datetime text
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -55,7 +66,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should parse local time adding system zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01"
|
||||
time = parse_datetime "1970-01-01T00:00:01"
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -63,31 +74,32 @@ spec =
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Zone.system.zone_id
|
||||
time . zone . should_equal Zone.system
|
||||
|
||||
Test.specify "should parse time Z" <|
|
||||
time = Time.parse "1970-01-01T00:00:01Z"
|
||||
time = parse_datetime "1970-01-01T00:00:01Z"
|
||||
time . to_epoch_seconds . should_equal 1
|
||||
time . zone . zone_id . should_equal "Z"
|
||||
|
||||
Test.specify "should parse time UTC" <|
|
||||
time = Time.parse "1970-01-01T00:00:01Z[UTC]"
|
||||
time = parse_datetime "1970-01-01T00:00:01Z[UTC]"
|
||||
time . to_epoch_seconds . should_equal 1
|
||||
time . zone . zone_id . should_equal "UTC"
|
||||
|
||||
Test.specify "should parse time with nanoseconds" <|
|
||||
time = Time.parse "1970-01-01T00:00:01.123456789Z"
|
||||
time = parse_datetime "1970-01-01T00:00:01.123456789Z"
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 0
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 123456789
|
||||
if nanoseconds_loss_in_precision then time . nanosecond . should_equal 123000000 else
|
||||
time . nanosecond . should_equal 123456789
|
||||
time . zone . zone_id . should_equal "Z"
|
||||
|
||||
Test.specify "should parse time with offset-based zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00"
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00"
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -98,7 +110,7 @@ spec =
|
||||
time . zone . zone_id . should_equal (Zone.new 1 . zone_id)
|
||||
|
||||
Test.specify "should parse time with id-based zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00[Europe/Paris]"
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00[Europe/Paris]"
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -109,14 +121,14 @@ spec =
|
||||
time . zone . zone_id . should_equal "Europe/Paris"
|
||||
|
||||
Test.specify "should throw error when parsing invalid time" <|
|
||||
case Time.parse "2008-1-1" . catch of
|
||||
case parse_datetime "2008-1-1" . catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '2008-1-1' could not be parsed at index 5"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should parse custom format of zoned time" <|
|
||||
time = Time.parse "2020-05-06 04:30:20 UTC" "yyyy-MM-dd HH:mm:ss z"
|
||||
time = parse_datetime "2020-05-06 04:30:20 UTC" "yyyy-MM-dd HH:mm:ss z"
|
||||
time . year . should_equal 2020
|
||||
time . month . should_equal 5
|
||||
time . day . should_equal 6
|
||||
@ -127,7 +139,7 @@ spec =
|
||||
time . zone . zone_id . should_equal "Etc/UTC"
|
||||
|
||||
Test.specify "should parse custom format of local time" <|
|
||||
time = Time.parse "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
time = parse_datetime "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
time . year . should_equal 2020
|
||||
time . month . should_equal 5
|
||||
time . day . should_equal 6
|
||||
@ -137,7 +149,7 @@ spec =
|
||||
time . nanosecond . should_equal 0
|
||||
|
||||
Test.specify "should throw error when parsing custom format" <|
|
||||
time = Time.parse "2008-01-01" "yyyy-MM-dd'T'HH:mm:ss'['z']'"
|
||||
time = parse_datetime "2008-01-01" "yyyy-MM-dd'T'HH:mm:ss'['z']'"
|
||||
case time.catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '2008-01-01' could not be parsed at index 10"
|
||||
@ -145,16 +157,16 @@ spec =
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should get epoch seconds" <|
|
||||
time = Time.new 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time = create_new_datetime 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time . to_epoch_seconds . should_equal 8
|
||||
|
||||
Test.specify "should get epoch millis" <|
|
||||
time = Time.new 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time = create_new_datetime 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time . to_epoch_milliseconds . should_equal 8000
|
||||
|
||||
Test.specify "should set offset-based timezone" <|
|
||||
tz = Zone.new 1 1 1
|
||||
time = Time.new 1970 (zone = Zone.utc) . at_zone tz
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) . at_zone tz
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -166,7 +178,7 @@ spec =
|
||||
|
||||
Test.specify "should set id-based timezone" <|
|
||||
tz = Zone.parse "Europe/Moscow"
|
||||
time = Time.new 1970 (zone = Zone.utc) . at_zone tz
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) . at_zone tz
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -177,33 +189,33 @@ spec =
|
||||
time . zone . zone_id . should_equal tz.zone_id
|
||||
|
||||
Test.specify "should get time of day from offsed-based time" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00" . time_of_day
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00" . time_of_day
|
||||
time . hour . should_equal 0
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 0
|
||||
|
||||
Test.specify "should get time of day from id-based time" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00[Europe/Paris]" . time_of_day
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00[Europe/Paris]" . time_of_day
|
||||
time . hour . should_equal 0
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 0
|
||||
|
||||
Test.specify "should get date from offsed-based time" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00" . date
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00" . date
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
|
||||
Test.specify "should get date from id-based time" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00[Europe/Paris]" . date
|
||||
time = parse_datetime "1970-01-01T00:00:01+01:00[Europe/Paris]" . date
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
|
||||
Test.specify "should add time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + 1.nanosecond
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) + 1.nanosecond
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -211,10 +223,10 @@ spec =
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . nanosecond . should_equal 1
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
time . zone . should_equal Zone.utc
|
||||
|
||||
Test.specify "should add date interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + 1.month
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) + 1.month
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
@ -225,7 +237,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should add mixed date time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + (1.month + 3.hours)
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) + (1.month + 3.hours)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
@ -236,7 +248,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.hour
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) - 1.hour
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 31
|
||||
@ -247,7 +259,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract date interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.month
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) - 1.month
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
@ -258,7 +270,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract mixed date time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - (1.month - 3.hours)
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) - (1.month - 3.hours)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
@ -269,7 +281,7 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should support mixed interval operators" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.month + 12.hours
|
||||
time = create_new_datetime 1970 (zone = Zone.utc) - 1.month + 12.hours
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
@ -280,11 +292,64 @@ spec =
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
|
||||
Test.specify "should be comparable" <|
|
||||
time_1 = Time.parse "2021-01-01T00:30:12.7102[UTC]"
|
||||
time_2 = Time.parse "2021-01-01T04:00:10.0+04:00"
|
||||
time_1 = parse_datetime "2021-01-01T00:30:12.7102[UTC]"
|
||||
time_2 = parse_datetime "2021-01-01T04:00:10.0+04:00"
|
||||
(time_1 == time_2) . should_be_false
|
||||
time_1==time_1 . should_be_true
|
||||
time_1!=time_2 . should_be_true
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Zone.system =
|
||||
Panic.catch Any (js_datetime_with_zone year month day hour minute second nanosecond zone) (err -> Error.throw (Time.Time_Error err.payload.cause))
|
||||
|
||||
# This ensures that date returned by javascript has the right timezone specified by the zone parameter.
|
||||
# Javascript's toLocaleString will accept the timezone but it will just adapt the datetime while keeping the local timezone.
|
||||
js_datetime_with_zone year month day hour minute second nanosecond zone =
|
||||
js_set_zone (js_local_datetime_impl year month day hour minute second nanosecond) zone
|
||||
|
||||
js_set_zone local_datetime zone =
|
||||
datetime_with_tz = local_datetime.at_zone(zone)
|
||||
diff = Duration.between datetime_with_tz local_datetime (timezone_aware=False)
|
||||
datetime_with_tz + diff
|
||||
|
||||
foreign js js_local_datetime_impl year month day hour minute second nanosecond = """
|
||||
if (month > 12 || month < 1) {
|
||||
throw `Invalid value for MonthOfYear (valid values 1 - 12): ${month}`;
|
||||
}
|
||||
return new Date(year, month - 1, day, hour, minute, second, nanosecond / 1000000);
|
||||
|
||||
js_parse text format=Nothing =
|
||||
d = Date_Time.parse text format
|
||||
js_datetime d.year d.month d.day d.hour d.minute d.second d.nanosecond d.zone
|
||||
|
||||
js_array_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Zone.system =
|
||||
arr = Panic.catch Any (js_array_datetimeCreate year month day hour minute second nanosecond) (err -> Error.throw (Time.Time_Error err.payload.cause))
|
||||
js_set_zone arr.at(0) zone
|
||||
|
||||
foreign js js_array_datetimeCreate year month day hour minute second nanosecond = """
|
||||
if (month > 12 || month < 1) {
|
||||
throw `Invalid value for MonthOfYear (valid values 1 - 12): ${month}`;
|
||||
}
|
||||
return [ new Date(year, month - 1, day, hour, minute, second, nanosecond / 1000000) ];
|
||||
|
||||
java_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Zone.system =
|
||||
Panic.catch Any (ZonedDateTime.of year month day hour minute second nanosecond zone) (err -> Error.throw (Time.Time_Error <| err.payload.to_display_text.drop (Text_Sub_Range.First 16)))
|
||||
|
||||
maybe_parse_java_zoned text pattern=Nothing =
|
||||
if pattern == Nothing then ZonedDateTime.parse text else
|
||||
ZonedDateTime.parse text pattern
|
||||
|
||||
parse_java_local original_error text pattern=Nothing =
|
||||
if pattern == Nothing then Panic.catch Polyglot_Error (LocalDateTime.parse text) (_ -> Error.throw (Time.Time_Error original_error.payload.cause.getMessage)) else
|
||||
formatter = DateTimeFormatter.ofPattern(pattern)
|
||||
Panic.catch Polyglot_Error (LocalDateTime.parse text formatter) (_ -> Error.throw (Time.Time_Error original_error.payload.cause.getMessage))
|
||||
|
||||
java_parse date_text_raw pattern=Nothing =
|
||||
utc_replaced = date_text_raw.replace "[UTC]" "Z"
|
||||
date_text = if utc_replaced.ends_with "ZZ" then date_text_raw else utc_replaced
|
||||
if pattern == Nothing then Panic.catch Polyglot_Error (maybe_parse_java_zoned date_text) (err -> parse_java_local err date_text pattern) else
|
||||
formatter = DateTimeFormatter.ofPattern(pattern)
|
||||
Panic.catch Polyglot_Error (maybe_parse_java_zoned date_text formatter) (err -> parse_java_local err date_text pattern)
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
@ -1,9 +1,13 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time
|
||||
import Standard.Base.Data.Time.Zone
|
||||
|
||||
import Standard.Test
|
||||
|
||||
polyglot java import java.time.ZoneId
|
||||
polyglot java import java.time.ZoneOffset
|
||||
|
||||
spec =
|
||||
Test.group "Zone" <|
|
||||
Test.specify "should get system zone id" <|
|
||||
@ -29,3 +33,36 @@ spec =
|
||||
Json.from_pairs [["type", "Zone"], ["id", "+01:02:03"]]
|
||||
Zone.utc.to_json.should_equal <|
|
||||
Json.from_pairs [["type", "Zone"], ["id", "UTC"]]
|
||||
Test.specify "should throw error when parsing invalid zone id" <|
|
||||
case Zone.parse "foo" . catch of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Unknown time-zone ID: foo"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
Test.group "JavaZoneId" <|
|
||||
Test.specify "should get system zone id" <|
|
||||
defaultZone = ZoneId.systemDefault
|
||||
Zone.system . should_equal defaultZone
|
||||
Test.specify "should parse UTC zone" <|
|
||||
zone = "UTC"
|
||||
id = ZoneId.of zone
|
||||
id . should_equal Zone.utc
|
||||
Test.specify "should parse id-based zone" <|
|
||||
zone = "Europe/Warsaw"
|
||||
id = ZoneId.of zone
|
||||
id . zone_id . should_equal zone
|
||||
Test.specify "should parse offset-based zone" <|
|
||||
zone = "+01:02:03"
|
||||
id = ZoneId.of zone
|
||||
id . zone_id . should_equal zone
|
||||
Test.specify "should get utc zone id" <|
|
||||
zone = ZoneId.of "UTC"
|
||||
zone . should_equal Zone.utc
|
||||
Test.specify "should convert to Json" <|
|
||||
zone = ZoneOffset.ofHoursMinutesSeconds 1 2 3
|
||||
zone.to_json.should_equal <|
|
||||
Json.from_pairs [["type", "Zone"], ["id", "+01:02:03"]]
|
||||
(ZoneId.of "UTC").to_json.should_equal <|
|
||||
Json.from_pairs [["type", "Zone"], ["id", "UTC"]]
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
@ -47,8 +47,8 @@ spec =
|
||||
Test.specify "send Enso date into Java" <|
|
||||
ensodate = Date.new 2022 04 01
|
||||
javatime = LocalTime.of 10 26
|
||||
javatimedate = javatime.atDate ensodate
|
||||
april1st = javatimedate . toLocalDate
|
||||
javatimedate = javatime . to_time ensodate
|
||||
april1st = javatimedate . date
|
||||
april1st.year.should_equal 2022
|
||||
april1st.month.should_equal 4
|
||||
april1st.day.should_equal 1
|
||||
|
Loading…
Reference in New Issue
Block a user