Duration type is a builtin type (#3759)

- Reimplement the `Duration` type to a built-in type.
- `Duration` is an interop type.
- Allow Enso method dispatch on `Duration` interop coming from different languages.

# Important Notes
- The older `Duration` type should now be split into new `Duration` builtin type and a `Period` type.
- This PR does not implement `Period` type, so all the `Period`-related functionality is currently not working, e.g., `Date - Period`.
- This PR removes `Integer.milliseconds`, `Integer.seconds`, ..., `Integer.years` extension methods.
This commit is contained in:
Pavel Marek 2022-10-14 20:08:08 +02:00 committed by GitHub
parent ce6267f098
commit e9260227c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 730 additions and 596 deletions

View File

@ -209,6 +209,7 @@
- [Added `Filter_Condition` to `Vector`, `Range` and `List`.][3770]
- [Extended `Filter_Condition` with `Is_Empty`, `Not_Empty`, `Like` and
`Not_Like`.][3775]
- [Reimplemented `Duration` as a built-in type.][3759]
- [Implemented `Table.replace_text` for in-memory table.][3793]
[debug-shortcuts]:
@ -336,6 +337,7 @@
[3750]: https://github.com/enso-org/enso/pull/3750
[3770]: https://github.com/enso-org/enso/pull/3770
[3775]: https://github.com/enso-org/enso/pull/3775
[3759]: https://github.com/enso-org/enso/pull/3759
[3793]: https://github.com/enso-org/enso/pull/3793
#### Enso Compiler

View File

@ -1891,8 +1891,7 @@ lazy val `std-table` = project
.copyDependencies(
`table-polyglot-root`,
Some("std-table.jar"),
ignoreScalaLibrary = true,
unpackedDeps = Set("xmlbeans")
ignoreScalaLibrary = true
)
.value
result

View File

@ -4,7 +4,7 @@ import Standard.Base.Data.Time.Duration
import Standard.Base.Data.Time.Date_Period
import Standard.Base.Polyglot
from Standard.Base.Error.Common import Time_Error_Data
from Standard.Base.Error.Common import Time_Error_Data, unimplemented
polyglot java import org.enso.base.Time_Utils
polyglot java import java.time.temporal.ChronoField
@ -308,9 +308,8 @@ type Date
import Standard.Base.Data.Time.Duration
example_add = Date.new 2020 + 6.months
+ : Duration -> Date
+ self amount = if amount.is_time then Error.throw (Time_Error_Data "Date does not support time intervals") else
(Time_Utils.date_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period) . internal_local_date
+ : Date_Period -> Date
+ self _ = unimplemented "Date.+"
## Shift the date by the specified amount of business days.
@ -433,9 +432,8 @@ type Date
import Standard.Base.Data.Time.Duration
example_subtract = Date.new 2020 - 7.days
- : Duration -> Date
- self amount = if amount.is_time then Error.throw (Time_Error_Data "Date does not support time intervals") else
(Time_Utils.date_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period) . internal_local_date
- : Date_Period -> Date
- self _ = unimplemented "Date.-"
## A Date to Json conversion.

View File

@ -8,6 +8,7 @@ from Standard.Base.Error.Common import Time_Error
polyglot java import java.time.format.DateTimeFormatter
polyglot java import java.time.temporal.ChronoField
polyglot java import java.time.temporal.IsoFields
polyglot java import java.lang.ArithmeticException
polyglot java import org.enso.base.Time_Utils
@ -387,10 +388,10 @@ type Date_Time
from Standard.Base import Date_Time
import Standard.Base.Data.Time.Duration
example_plus = Date_Time.new 2020 + 15.years + 3.hours
+ : Duration -> Date_Time
example_plus = Date_Time.new 2020 + 15.years + (Duration.new hours=3)
+ : Duration -> Date_Time ! Time_Error
+ self amount =
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.PLUS amount.internal_period amount.internal_duration
Panic.catch ArithmeticException (self.plus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage))
## Shift the date by the specified amount of business days.
@ -442,9 +443,9 @@ type Date_Time
import Standard.Base.Data.Time.Duration
example_minus = Date_Time.new 2020 - 1.year - 9.months
- : Duration -> Date_Time
- : Duration -> Date_Time ! Time_Error
- self amount =
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS amount.internal_period amount.internal_duration
Panic.catch ArithmeticException (self.minus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage))
## Convert this time to text using the default formatter.

View File

@ -1,17 +1,18 @@
from Standard.Base import all
import Standard.Base.System
polyglot java import java.lang.ArithmeticException
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
polyglot java import java.lang.ArithmeticException
## 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.
- start_inclusive: The start datetime of the duration.
- end_inclusive: The end datetime of the duration.
- timezone_aware: Whether the duration between two given times should be
aware of the timezone, that can be set for start or end times.
> Example
An hour interval between two points in time.
@ -20,12 +21,40 @@ polyglot java import org.enso.base.Time_Utils
import Standard.Base.Data.Time.Duration
example_between = Duration.between Date_Time.now (Date_Time.new 2010 10 20)
between : Date_Time -> Date_Time -> Duration
between : Date_Time -> Date_Time -> Boolean -> Duration
between start_inclusive end_exclusive timezone_aware=True =
period = Java_Period.ofDays 0 . normalized
duration = Time_Utils.duration_between start_inclusive end_exclusive timezone_aware
Duration_Data period duration
Duration.between_builtin start_inclusive end_exclusive timezone_aware
## Create a duration from time units.
Arguments:
- hours: hours
- minutes: minutes
- seconds: seconds
- milliseconds: milliseconds
- nanoseconds: nanoseconds
> Example
Duration of 2 hours.
import Standard.Base.Data.Time.Duration
example_duration = Duration.new hours=2
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Duration
new hours=0 minutes=0 seconds=0 milliseconds=0 nanoseconds=0 =
Duration.new_builtin hours minutes seconds milliseconds nanoseconds
## Create a zero (empty) duration.
> Example
Folding a vector of durations.
import Standard.Base.Data.Time.Duration
durations = [(Duration.new seconds=1), (Duration.new seconds=2), (Duration.new seconds=3)]
example_sum = durations.fold Duration.zero (+)
zero : Duration
zero = new
## ADVANCED
@ -38,25 +67,13 @@ time_execution ~function =
start = System.nano_time
result = Runtime.no_inline function
end = System.nano_time
duration = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofNanos (end - start))
duration = new nanoseconds=(end - start)
Pair_Data duration result
# TODO Dubious constructor export
from project.Data.Time.Duration.Duration import all
from project.Data.Time.Duration.Duration export all
@Builtin_Type
type Duration
## An amount of time in terms of years, months, days, hours, minutes,
seconds and nanoseconds.
Arguments:
- internal_period: The internal representation of the time as a period.
- internal_duration: The internal representation of the time as a
duration.
Duration_Data internal_period internal_duration
## Add the specified amount of time to this duration.
Arguments:
@ -67,19 +84,17 @@ type Duration
import Standard.Base.Data.Time.Duration
example_add = 3.minutes + 6.seconds
example_add = (Duration.new minutes=3) + (Duration.new seconds=6)
> Example
Add 12 hours to a duration of a month.
import Standard.Base.Data.Time.Duration
example_add = 1.month + 12.hours
+ : Duration -> Duration
example_add = 1.month + (Duration.new hours=12)
+ : Duration -> Duration ! Time_Error
+ self that =
period = self.internal_period . plus that.internal_period . normalized
duration = self.internal_duration . plus that.internal_duration
Duration_Data period duration
Panic.catch ArithmeticException (self.plus_builtin that) (err-> Error.throw (Time_Error_Data err.getMessage))
## Subtract the specified amount of time from this duration.
@ -94,186 +109,14 @@ type Duration
example_subtract = 3.years - 11.months
> Example
Substract 30 minutes from a duration of 7 months.
Substract 30 minutes from a duration of 6 hours.
import Standard.Base.Data.Time.Duration
example_subtract = 7.months - 30.minutes
- : Duration -> Duration
example_subtract = (Duration.new hours=6) - (Duration.new minutes=30)
- : Duration -> Duration ! Time_Error
- self that =
period = self.internal_period . minus that.internal_period . normalized
duration = self.internal_duration . minus that.internal_duration
Duration_Data period duration
## Get the portion of the duration expressed in nanoseconds.
> Example
Get the portion of the duration expressed in nanoseconds.
import Standard.Examples
example_nanos = Examples.duration.nanoseconds
nanoseconds : Integer
nanoseconds self = self.internal_duration . toNanosPart
## Get the portion of the duration expressed in milliseconds.
> Example
Get the portion of the duration expressed in milliseconds.
import Standard.Examples
example_millis = Examples.duration.milliseconds
milliseconds : Integer
milliseconds self = self.internal_duration . toMillisPart
## UNSTABLE
Convert the duration to total milliseconds.
total_milliseconds : Integer ! Illegal_Argument_Error
total_milliseconds self =
Panic.catch ArithmeticException (self.internal_duration . toMillis) _->
Error.throw (Illegal_Argument_Error_Data "The duration is too large to convert it to milliseconds.")
## Get the portion of the duration expressed in seconds.
> Example
Get the portion of the duration expressed in seconds.
import Standard.Examples
example_seconds = Examples.duration.milliseconds
seconds : Integer
seconds self = self.internal_duration . toSecondsPart
## Get the portion of the duration expressed in minutes.
> Example
Get the portion of the duration expressed in minutes.
import Standard.Examples
example_minutes = Examples.duration.milliseconds
minutes : Integer
minutes self = self.internal_duration . toMinutesPart
## Get the portion of the duration expressed in hours.
> Example
Get the portion of the duration expressed in hours.
import Standard.Examples
example_hours = Examples.duration.milliseconds
hours : Integer
hours self = self.internal_duration . toHours
## Get the portion of the duration expressed in days.
> Example
Get the portion of the duration expressed in days.
import Standard.Examples
example_days = Examples.duration.milliseconds
days : Integer
days self = self.internal_period . getDays
## Get the portion of the duration expressed in months.
> Example
Get the portion of the duration expressed in months.
import Standard.Examples
example_months = Examples.duration.months
months : Integer
months self = self.internal_period . getMonths
## Get the portion of the duration expressed in years.
> Example
Get the portion of the duration expressed in years.
import Standard.Examples
example_years = Examples.duration.years
years : Integer
years self = self.internal_period . getYears
## Convert this duration to a Vector of years, months, days, hours, minutes,
seconds and nanosecnods.
> Example
Convert duration of a year and a hour to a vector returning
`[1, 0, 0, 1, 0, 0, 0]`.
import Standard.Base.Data.Time.Duration
example_to_vec = (1.year + 1.hour).to_vector
> Example
Convert duration of 800 nanoseconds to a vector returning
`[0, 0, 0, 0, 0, 0, 800]`
import Standard.Base.Data.Time.Duration
example_to_vec = 800.nanoseconds . to_vector
to_vector : Vector.Vector Integer
to_vector self = [self.years, self.months, self.days, self.hours, self.minutes, self.seconds, self.nanoseconds]
## A Duration to Json conversion.
> Example
Convert a duration of 10 seconds to Json.
import Standard.Base.Data.Time.Duration
example_to_json = 10.seconds.to_json
to_json : Json.Object
to_json self =
b = Vector.new_builder
b.append ["type", "Duration"]
if self.years==0 . not then b.append ["years", self.years]
if self.months==0 . not then b.append ["months", self.months]
if self.days==0 . not then b.append ["days", self.days]
if self.hours==0 . not then b.append ["hours", self.hours]
if self.minutes==0 . not then b.append ["minutes", self.minutes]
if self.seconds==0 . not then b.append ["seconds", self.seconds]
if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds]
Json.from_pairs b.to_vector
## Check if this duration is date-based.
> Example
Check if the duration of 10 seconds is date-based.
import Standard.Base.Data.Time.Duration
example_is_date = 10.seconds.is_date
is_date : Boolean
is_date self = (self.years==0 . not) || (self.months==0 . not) || (self.days==0 . not)
## Check if this duration is time-based.
> Example
Check if the duration of 10 seconds is time-based.
import Standard.Base.Data.Time.Duration
example_is_time = 10.seconds.is_time
is_time : Boolean
is_time self = (self.hours==0 . not) || (self.minutes==0 . not) || (self.seconds==0 . not) || (self.nanoseconds==0 . not)
## Check if this duration represents an empty time-span.
> Example
Check if the duration of 10 seconds is empty.
import Standard.Base.Data.Time.Duration
example_is_empty = 10.seconds.is_empty
is_empty : Boolean
is_empty self = self.is_date.not && self.is_time.not
Panic.catch ArithmeticException (self.minus_builtin that) (err -> Error.throw (Time_Error_Data err.getMessage))
## Check two durations for equality.
@ -285,9 +128,9 @@ type Duration
import Standard.Base.Data.Time.Duration
example_eq = 60.seconds == 1.minute
example_eq = (Duration.new seconds=60).total_minutes == (Duration.new minutes=1).total_minutes
== : Duration -> Boolean
== self that = self.to_vector == that.to_vector
== self that = self.equals_builtin that
## Compares `self` to `that` to produce an ordering.
@ -300,193 +143,136 @@ type Duration
import Standard.Base.Data.Time.Duration
example_compare_to =
duration_1 = 1.year
duration_2 = 12.months + 1.day
duration_1 = (Duration.new hour=1)
duration_2 = (Duration.new minutes=60) + (Duration.new minutes=5)
duration_1.compare_to duration_2
compare_to : Duration -> Ordering
compare_to self that =
if self.years > that.years then Ordering.Greater else
if self.years < that.years then Ordering.Less else
if self.months > that.months then Ordering.Greater else
if self.months < that.months then Ordering.Less else
if self.days > that.days then Ordering.Greater else
if self.days < that.days then Ordering.Less else
duration_sign = self.internal_duration.compareTo that.internal_duration
Ordering.from_sign duration_sign
compare_to self that = Ordering.from_sign (self.compare_to_builtin that)
## Create a duration of `self` nanoseconds.
## Get the portion of the duration expressed in nanoseconds.
> Examples
Create a duration of 1 nanosecond.
> Example
Get the portion of the duration expressed in nanoseconds.
import Standard.Base.Data.Time.Duration
import Standard.Examples
example_nano = 1.nanosecond
Integer.nanosecond : Duration
Integer.nanosecond self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofNanos self)
example_nanos = Examples.duration.nanoseconds
nanoseconds : Integer
nanoseconds self = @Builtin_Method "Duration.nanoseconds"
## Create a duration of `self` nanoseconds.
## Get the portion of the duration expressed in milliseconds.
> Examples
Create a duration of 20 nanoseconds.
> Example
Get the portion of the duration expressed in milliseconds.
import Standard.Base.Data.Time.Duration
import Standard.Examples
example_nano = 20.nanoseconds
Integer.nanoseconds : Duration
Integer.nanoseconds self = self.nanosecond
example_millis = Examples.duration.milliseconds
milliseconds : Integer
milliseconds self = @Builtin_Method "Duration.milliseconds"
## Create a duration of `self` milliseconds.
## Get the portion of the duration expressed in seconds.
> Example
Create a duration of 1 millisecond.
> Example
Get the portion of the duration expressed in seconds.
import Standard.Base.Data.Time.Duration
import Standard.Examples
example_milli = 1.millisecond
Integer.millisecond : Duration
Integer.millisecond self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofMillis self)
example_seconds = Examples.duration.seconds
seconds : Integer
seconds self = @Builtin_Method "Duration.seconds"
## Create a duration of `self` milliseconds.
## Get the portion of the duration expressed in minutes.
> Example
Create a duration of 20 milliseconds.
> Example
Get the portion of the duration expressed in minutes.
import Standard.Base.Data.Time.Duration
import Standard.Examples
example_milli = 20.milliseconds
Integer.milliseconds : Duration
Integer.milliseconds self = self.millisecond
example_minutes = Examples.duration.minutes
minutes : Integer
minutes self = @Builtin_Method "Duration.minutes"
## Create a duration of `self` seconds.
## Get the portion of the duration expressed in hours.
> Example
Create a duration of 1 second.
> Example
Get the portion of the duration expressed in hours.
import Standard.Base.Data.Time.Duration
import Standard.Examples
example_second = 1.second
Integer.second : Duration
Integer.second self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofSeconds self)
example_hours = Examples.duration.milliseconds
hours : Integer
hours self = @Builtin_Method "Duration.hours"
## Create a duration of `self` seconds.
## UNSTABLE
Convert the duration to total milliseconds.
total_milliseconds : Integer ! Illegal_State_Error
total_milliseconds self =
Panic.catch ArithmeticException (self.total_milliseconds_builtin) _->
Error.throw (Illegal_State_Error_Data "The duration is too large to convert it to milliseconds")
> Example
Create a duration of 20 seconds.
## UNSTABLE
Convert the duration to total seconds.
total_seconds : Decimal ! Illegal_State_Error
total_seconds self = self.total_milliseconds / 1000.0
import Standard.Base.Data.Time.Duration
## UNSTABLE
Convert the duration to total minutes.
total_minutes : Decimal ! Illegal_State_Error
total_minutes self = self.total_seconds / 60.0
example_second = 20.seconds
Integer.seconds : Duration
Integer.seconds self = self.second
## UNSTABLE
Convert the duration to total minutes.
total_hours : Decimal ! Illegal_State_Error
total_hours self = self.total_minutes / 60.0
## Create a duration of `self` minutes.
## Convert this duration to a Vector of hours, minutes, seconds, milliseconds
and nanoseconds.
> Example
Create a duration of 1 minute.
> Example
Convert a duration of one hour to a vector resulting in
`[1, 0, 30, 0, 0]`.
import Standard.Base.Data.Time.Duration
import Standard.Base.Data.Time.Duration
example_min = 1.minute
Integer.minute : Duration
Integer.minute self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofMinutes self)
example_to_vec = (Duration.new hours=1 seconds=30).to_vector
## Create a duration of `self` minutes.
> Example
Convert duration of 800 nanoseconds to a vector returning
`[0, 0, 0, 0, 0, 800]`
> Example
Create a duration of 20 minutes.
import Standard.Base.Data.Time.Duration
import Standard.Base.Data.Time.Duration
example_to_vec = (Duration.new nanoseconds=800)).to_vector
to_vector : Vector.Vector Integer
to_vector self = [self.hours, self.minutes, self.seconds, self.milliseconds, self.nanoseconds]
example_min = 20.minutes
Integer.minutes : Duration
Integer.minutes self = self.minute
## A Duration to Json conversion.
## Create a duration of `self` hours.
> Example
Convert a duration of 10 seconds to Json.
> Example
Create a duration of 1 hour.
import Standard.Base.Data.Time.Duration
import Standard.Base.Data.Time.Duration
example_to_json = (Duration.new seconds=10).to_json
to_json : Json.Object
to_json self =
b = Vector.new_builder
b.append ["type", "Duration"]
if self.hours==0 . not then b.append ["hours", self.hours]
if self.minutes==0 . not then b.append ["minutes", self.minutes]
if self.seconds==0 . not then b.append ["seconds", self.seconds]
if self.milliseconds==0 . not then b.append ["milliseconds", self.milliseconds]
if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds]
Json.from_pairs b.to_vector
example_hour = 1.hour
Integer.hour : Duration
Integer.hour self = Duration_Data (Java_Period.ofDays 0) (Java_Duration.ofHours self)
## Check if this duration represents an empty time-span.
## Create a duration of `self` hours.
> Example
Check if the duration of 10 seconds is empty.
> Example
Create a duration of 20 hours.
import Standard.Base.Data.Time.Duration
example_hour = 20.hours
Integer.hours : Duration
Integer.hours self = self.hour
## Create a duration of `self` days.
> Example
Create a duration of 1 day.
import Standard.Base.Data.Time.Duration
example_day = 1.day
Integer.day : Duration
Integer.day self = Duration_Data (Java_Period.ofDays self . normalized) (Java_Duration.ofSeconds 0)
## Create a duration of `self` days.
> Example
Create a duration of 20 days.
import Standard.Base.Data.Time.Duration
example_day = 20.days
Integer.days : Duration
Integer.days self = self.day
## Create a duration of `self` months.
> Example
Create a duration of 1 month.
import Standard.Base.Data.Time.Duration
example_month = 1.month
Integer.month : Duration
Integer.month self = Duration_Data (Java_Period.ofMonths self . normalized) (Java_Duration.ofSeconds 0)
## Create a duration of `self` months.
> Example
Create a duration of 6 months.
import Standard.Base.Data.Time.Duration
example_month = 6.months
Integer.months : Duration
Integer.months self = self.month
## Create a duration of `self` years.
> Example
Create a duration of 1 year.
import Standard.Base.Data.Time.Duration
example_year = 1.year
Integer.year : Duration
Integer.year self = Duration_Data (Java_Period.ofYears self . normalized) (Java_Duration.ofSeconds 0)
## Create a duration of `self` years.
> Example
Create a duration of 20 years.
import Standard.Base.Data.Time.Duration
example_year = 20.years
Integer.years : Duration
Integer.years self = self.year
import Standard.Base.Data.Time.Duration
example_is_empty = 10.seconds.is_empty
is_empty : Boolean
is_empty self = self.to_vector . all (==0)

View File

@ -215,10 +215,9 @@ type Time_Of_Day
from Standard.Base import Time_Of_Day
import Standard.Base.Data.Time.Duration
example_plus = Time_Of_Day.new + 3.seconds
example_plus = Time_Of_Day.new + (Duration.new seconds=3)
+ : Duration -> Time_Of_Day
+ self amount = if amount.is_date then Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals") else
Time_Utils.time_adjust self Time_Utils.AdjustOp.PLUS amount.internal_duration
+ self amount = self.plus_builtin amount
## Subtract the specified amount of time from this instant to get a new
instant.
@ -232,10 +231,9 @@ type Time_Of_Day
from Standard.Base import Time_Of_Day
import Standard.Base.Data.Time.Duration
example_minus = Time_Of_Day.now - 12.hours
example_minus = Time_Of_Day.now - (Duration.new hours=12)
- : Duration -> Time_Of_Day
- self amount = if amount.is_date then Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals") else
Time_Utils.time_adjust self Time_Utils.AdjustOp.MINUS amount.internal_duration
- self amount = self.minus_builtin amount
## Format this time of day as text using the default formatter.

View File

@ -36,7 +36,7 @@ polyglot java import org.enso.base.Http_Utils
> Example
Create an HTTP client with extended timeout.
Http.new timeout=30.seconds
Http.new timeout=(Duration.new seconds=30)
> Example
Create an HTTP client with extended timeout and proxy settings.
@ -48,7 +48,7 @@ polyglot java import org.enso.base.Http_Utils
example_new =
Http.new (timeout = 30.seconds) (proxy = Proxy.new "example.com" 8080)
new : Duration -> Boolean -> Proxy -> Http
new (timeout = 10.seconds) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) =
new (timeout = (Duration.new seconds=10)) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) =
Http_Data timeout follow_redirects proxy version
## Send an Options request.
@ -662,9 +662,7 @@ type Http
internal_http_client : HttpClient
internal_http_client self =
builder = HttpClient.newBuilder
# timeout
if self.timeout.is_date then Panic.throw (Time_Error_Data "Connection timeout does not support date intervals") else
builder.connectTimeout self.timeout.internal_duration
builder.connectTimeout self.timeout
# redirect
redirect = HttpClient.Redirect
redirect_policy = case self.follow_redirects of

View File

@ -165,7 +165,7 @@ get_geo_data = Http.fetch geo_data_url
## A simple HTTP client for examples.
http_client : Http
http_client = Http.new (timeout = 30.seconds)
http_client = Http.new (timeout = Duration.new seconds=30)
## A basic URI for examples.
uri : URI

View File

@ -166,7 +166,7 @@ specify : Text -> Any -> (Text | Nothing) -> Nothing
specify label ~behavior pending=Nothing =
pair = case pending of
Nothing -> Duration.time_execution (run_spec behavior)
reason -> Pair_Data 0.milliseconds (Pending reason)
reason -> Pair_Data Duration.zero (Pending reason)
result = pair.second
time_taken = pair.first
spec = State.get Spec
@ -838,14 +838,14 @@ report_pending_group name reason config builder =
Prints a report on the tests to standard output.
Spec.print_report : Suite_Config -> (StringBuilder|Nothing) -> Nothing
Spec.print_report self config builder =
total_time = self.behaviors.fold 0.milliseconds acc-> behavior->
total_time = self.behaviors.fold Duration.zero acc-> behavior->
acc + behavior.time_taken
if config.should_output_junit then
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 + '"')
builder.append (' time="' + (total_time.total_milliseconds / 1000.0).to_text + '"')
builder.append (' time="' + (total_time.total_seconds).to_text + '"')
builder.append ('>\n')
self.behaviors.reverse.each behavior->

View File

@ -111,6 +111,13 @@ For example, to update settings for the Launcher:
java -agentlib:native-image-agent=config-merge-dir=engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher -jar launcher.jar <arguments>
```
Note that for convenience, you can run the launcher/engine runner via
`bin/enso`, e.g.
```bash
env JAVA_OPTS="-agentlib:native-image-agent=config-merge-dir=./engine/runner-native/src/main/resources/META-INF/native-image/org/enso/runner" ./built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso --run tmp.enso
```
The command may need to be re-run with different arguments to ensure that all
execution paths that use reflection are covered. The configuration files between
consecutive runs will be merged (a warning may be issued for the first run if

View File

@ -261,6 +261,90 @@
{
"name":"org.enso.interpreter.node.expression.builtin.date.ToTextTimeOfDayMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinTimeOfDayMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MillisecondsDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDateTimeMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.BetweenBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.CompareToBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.TotalMillisecondsBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.Duration",
"methods":[{"name":"<init>","parameterTypes":[] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.EqualsBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.HoursDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MillisecondsDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinutesDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDateTimeMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.MinusBuiltinTimeOfDayMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.NanosecondsDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.NewBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinDateTimeMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.PlusBuiltinTimeOfDayMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.SecondsDurationMethodGen",
"methods":[{"name":"makeFunction","parameterTypes":["org.enso.interpreter.Language"] }]}
,
{
"name":"org.enso.interpreter.node.expression.builtin.date.ToTextTimeZoneMethodGen",

View File

@ -18,7 +18,7 @@ import java.util.Arrays;
* in EPB because EPB is a dependency of runtime.
*/
@ExportLibrary(InteropLibrary.class)
public class ReadOnlyArray implements TruffleObject {
public final class ReadOnlyArray implements TruffleObject {
private final Object[] items;

View File

@ -27,7 +27,7 @@ import org.enso.interpreter.epb.node.ContextRewrapNode;
* org.enso.interpreter.epb.EpbLanguage} for the explanation of this system.
*/
@ExportLibrary(InteropLibrary.class)
public class PolyglotProxy implements TruffleObject {
public final class PolyglotProxy implements TruffleObject {
final Object delegate;
private final GuardedTruffleContext origin;
private final GuardedTruffleContext target;

View File

@ -54,7 +54,7 @@ public class FunctionCallInstrumentationNode extends Node implements Instrumenta
/** A simple value class for function call information. */
@ExportLibrary(InteropLibrary.class)
public static class FunctionCall implements TruffleObject {
public static final class FunctionCall implements TruffleObject {
private final Function function;
private final Object state;
private final @CompilerDirectives.CompilationFinal(dimensions = 1) Object[] arguments;

View File

@ -14,11 +14,6 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.resolver.HostMethodCallNode;
@ -28,12 +23,24 @@ 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.*;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoDate;
import org.enso.interpreter.runtime.data.EnsoDateTime;
import org.enso.interpreter.runtime.data.EnsoDuration;
import org.enso.interpreter.runtime.data.EnsoTimeOfDay;
import org.enso.interpreter.runtime.data.EnsoTimeZone;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.state.Stateful;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
@ -318,6 +325,35 @@ public abstract class InvokeMethodNode extends BaseNode {
}
}
@Specialization(
guards = {
"!types.hasType(self)",
"!types.hasSpecialDispatch(self)",
"getPolyglotCallType(self, symbol, interop) == CONVERT_TO_DURATION"
})
Stateful doConvertDuration(
VirtualFrame frame,
Object state,
UnresolvedSymbol symbol,
Object self,
Object[] arguments,
@CachedLibrary(limit = "10") TypesLibrary types,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Cached MethodResolverNode methodResolverNode) {
var ctx = Context.get(this);
try {
var duration = interop.asDuration(self);
var ensoDuration = new EnsoDuration(duration);
Function function =
methodResolverNode.expectNonNull(ensoDuration, ctx.getBuiltins().duration(), symbol);
arguments[0] = ensoDuration;
return invokeFunctionNode.execute(function, frame, state, arguments);
} catch (UnsupportedMessageException e) {
throw new PanicException(ctx.getBuiltins().error().makeNoSuchMethodError(self, symbol), this);
}
}
@CompilerDirectives.TruffleBoundary
private ZonedDateTime dateTime(LocalDate date, LocalTime time) {
return date.atTime(time).atZone(ZoneId.systemDefault());

View File

@ -1,7 +1,15 @@
package org.enso.interpreter.node.callable.resolver;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.interop.*;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
@ -57,6 +65,11 @@ public abstract class HostMethodCallNode extends Node {
* Standard.Base.Data.Time.Date_Time} with a system Time_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.Duration} and dispatching natively.
*/
CONVERT_TO_DURATION,
/**
* The method call should be handled by converting {@code self} to a {@code
* Standard.Base.Data.Time.Time_Of_Day} and dispatching natively.
@ -83,6 +96,7 @@ public abstract class HostMethodCallNode extends Node {
&& this != CONVERT_TO_TEXT
&& this != CONVERT_TO_DATE
&& this != CONVERT_TO_DATE_TIME
&& this != CONVERT_TO_DURATION
&& this != CONVERT_TO_ZONED_DATE_TIME
&& this != CONVERT_TO_TIME_OF_DAY
&& this != CONVERT_TO_TIME_ZONE;
@ -134,6 +148,8 @@ public abstract class HostMethodCallNode extends Node {
}
} else if (library.isTime(self)) {
return PolyglotCallType.CONVERT_TO_TIME_OF_DAY;
} else if (library.isDuration(self)) {
return PolyglotCallType.CONVERT_TO_DURATION;
} else if (library.isTimeZone(self)) {
return PolyglotCallType.CONVERT_TO_TIME_ZONE;
} else if (library.isString(self)) {

View File

@ -46,7 +46,7 @@ import scala.Function1;
/** Represents a source module with a known location. */
@ExportLibrary(InteropLibrary.class)
public class Module implements TruffleObject {
public final class Module implements TruffleObject {
/** Defines a stage of compilation of the module. */
public enum CompilationStage {

View File

@ -88,6 +88,7 @@ public class Builtins {
private final Builtin file;
private final Builtin date;
private final Builtin dateTime;
private final Builtin duration;
private final Builtin timeOfDay;
private final Builtin timeZone;
private final Builtin warning;
@ -129,6 +130,7 @@ public class Builtins {
file = builtins.get(File.class);
date = builtins.get(org.enso.interpreter.node.expression.builtin.date.Date.class);
dateTime = builtins.get(org.enso.interpreter.node.expression.builtin.date.DateTime.class);
duration = builtins.get(org.enso.interpreter.node.expression.builtin.date.Duration.class);
timeOfDay = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeOfDay.class);
timeZone = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeZone.class);
special = new Special(language);
@ -457,6 +459,15 @@ public class Builtins {
return timeOfDay.getType();
}
/**
* Returns the {@code Duration} atom constructor.
*
* @return the {@code Duration} atom constructor.
*/
public Type duration() {
return duration.getType();
}
/**
* Returns the {@code TimeZone} atom constructor.
*

View File

@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.state.data.EmptyMap;
/** Simple runtime value representing a yet-unresolved by-name symbol. */
@ExportLibrary(InteropLibrary.class)
public class UnresolvedConversion implements TruffleObject {
public final class UnresolvedConversion implements TruffleObject {
private final ModuleScope scope;
/**

View File

@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.state.data.EmptyMap;
/** Simple runtime value representing a yet-unresolved by-name symbol. */
@ExportLibrary(InteropLibrary.class)
public class UnresolvedSymbol implements TruffleObject {
public final class UnresolvedSymbol implements TruffleObject {
private final String name;
private final ModuleScope scope;

View File

@ -12,7 +12,7 @@ import com.oracle.truffle.api.library.ExportMessage;
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
@ExportLibrary(InteropLibrary.class)
class ArraySlice implements TruffleObject {
public final class ArraySlice implements TruffleObject {
private final Object storage;
private final long start;
private final long end;

View File

@ -3,20 +3,26 @@ package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
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.data.text.Text;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import java.time.*;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
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.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
@ -138,6 +144,24 @@ public final class EnsoDateTime implements TruffleObject {
return new EnsoTimeZone(dateTime.getZone());
}
@Builtin.Method(name = "plus_builtin", description = "Adds a duration to this date time")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@CompilerDirectives.TruffleBoundary
public EnsoDateTime plus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return new EnsoDateTime(dateTime.plus(interop.asDuration(durationObject)));
}
@Builtin.Method(name = "minus_builtin", description = "Subtracts a duration from this date time")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@CompilerDirectives.TruffleBoundary
public EnsoDateTime minus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return new EnsoDateTime(dateTime.minus(interop.asDuration(durationObject)));
}
@Builtin.Method(description = "Return the number of seconds from the Unix epoch.")
@CompilerDirectives.TruffleBoundary
public long toEpochSeconds() {

View File

@ -0,0 +1,195 @@
package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
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.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
@Builtin(pkg = "date", name = "Duration", stdlibName = "Standard.Base.Data.Time.Duration")
public final class EnsoDuration implements TruffleObject {
private final Duration duration;
public EnsoDuration(Duration duration) {
this.duration = duration;
}
@ExportMessage
boolean hasType() {
return true;
}
@ExportMessage
Type getType(@CachedLibrary("this") TypesLibrary thisLib) {
return Context.get(thisLib).getBuiltins().duration();
}
@Builtin.Method(
name = "new_builtin",
description =
"Constructs a new Duration from hours, minutes, seconds, milliseconds and nanoseconds")
@TruffleBoundary
public static EnsoDuration create(
long hours, long minutes, long seconds, long milliseconds, long nanoseconds) {
var duration =
Duration.ofHours(hours)
.plusMinutes(minutes)
.plusSeconds(seconds)
.plusMillis(milliseconds)
.plusNanos(nanoseconds);
return new EnsoDuration(duration);
}
@Builtin.Method(
name = "between_builtin",
description =
"Construct a new Duration that is between the given start date inclusive, and end date exclusive")
@Builtin.Specialize
@TruffleBoundary
public static EnsoDuration between(
Object startInclusive, Object endExclusive, boolean timeZoneAware, InteropLibrary interop) {
if (!isDateTime(startInclusive, interop)
|| (timeZoneAware && !hasTimeZone(startInclusive, interop))) {
throw createNotDateTimePanic("start_inclusive", startInclusive, interop);
}
if (!isDateTime(endExclusive, interop)
|| (timeZoneAware && !hasTimeZone(endExclusive, interop))) {
throw createNotDateTimePanic("end_exclusive", endExclusive, interop);
}
Temporal startTime = convertToDateTime(startInclusive, timeZoneAware, interop);
Temporal endTime = convertToDateTime(endExclusive, timeZoneAware, interop);
return new EnsoDuration(Duration.between(startTime, endTime));
}
private static boolean isDateTime(Object dateTime, InteropLibrary interop) {
return interop.isDate(dateTime) && interop.isTime(dateTime);
}
private static boolean hasTimeZone(Object dateTime, InteropLibrary interop) {
return interop.isTimeZone(dateTime);
}
private static PanicException createNotDateTimePanic(
String varName, Object object, InteropLibrary interop) {
return new PanicException(
Context.get(interop).getBuiltins().error().makeTypeError("Date_Time", object, varName),
interop);
}
private static Temporal convertToDateTime(
Object dateTimeObject, boolean timeZoneAware, InteropLibrary interop) {
assert interop.isDate(dateTimeObject);
assert interop.isTime(dateTimeObject);
try {
LocalDate date = interop.asDate(dateTimeObject);
LocalTime time = interop.asTime(dateTimeObject);
if (timeZoneAware) {
assert interop.isTime(dateTimeObject);
ZoneId zone = interop.asTimeZone(dateTimeObject);
return ZonedDateTime.of(date, time, zone);
} else {
return LocalDateTime.of(date, time);
}
} catch (UnsupportedMessageException e) {
throw new IllegalStateException(e);
}
}
@Builtin.Method(description = "Gets the hours part")
public long hours() {
return duration.toHoursPart();
}
@Builtin.Method(description = "Gets the minutes part")
public long minutes() {
return duration.toMinutesPart();
}
@Builtin.Method(description = "Gets the seconds part")
public long seconds() {
return duration.toSecondsPart();
}
@Builtin.Method(description = "Gets the milliseconds part")
public long milliseconds() {
return duration.toMillisPart();
}
@Builtin.Method(description = "Gets the nanoseconds part")
public long nanoseconds() {
return duration.toNanosPart();
}
@Builtin.Method(
name = "total_milliseconds_builtin",
description = "Gets the total amount of milliseconds")
public long totalMilliseconds() {
return duration.toMillis();
}
@Builtin.Method(name = "plus_builtin", description = "Adds another Duration")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@TruffleBoundary
public EnsoDuration plus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return new EnsoDuration(duration.plus(interop.asDuration(durationObject)));
}
@Builtin.Method(name = "minus_builtin", description = "Subtracts another Duration")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@TruffleBoundary
public EnsoDuration minus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return new EnsoDuration(duration.minus(interop.asDuration(durationObject)));
}
@Builtin.Method(name = "compare_to_builtin", description = "Compares to other duration")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
public long compareTo(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return duration.compareTo(interop.asDuration(durationObject));
}
@Builtin.Method(name = "equals_builtin")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
public boolean equalsDuration(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
return duration.equals(interop.asDuration(durationObject));
}
@ExportMessage
public boolean isDuration() {
return true;
}
@ExportMessage
public Duration asDuration() {
return duration;
}
@ExportMessage
@TruffleBoundary
public String toDisplayString(boolean allowSideEffects) {
return duration.toString();
}
}

View File

@ -30,7 +30,7 @@ import java.util.Set;
*/
@ExportLibrary(TypesLibrary.class)
@Builtin(pkg = "io", name = "File", stdlibName = "Standard.Base.System.File.File")
public class EnsoFile implements TruffleObject {
public final class EnsoFile implements TruffleObject {
private final TruffleFile truffleFile;
public EnsoFile(TruffleFile truffleFile) {

View File

@ -1,28 +1,29 @@
package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
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.data.text.Text;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
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.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
@Builtin(pkg = "date", name = "TimeOfDay", stdlibName = "Standard.Base.Data.Time.Time_Of_Day")
public class EnsoTimeOfDay implements TruffleObject {
public final class EnsoTimeOfDay implements TruffleObject {
private LocalTime localTime;
public EnsoTimeOfDay(LocalTime localTime) {
@ -77,6 +78,28 @@ public class EnsoTimeOfDay implements TruffleObject {
return localTime.getNano();
}
@Builtin.Method(name = "plus_builtin", description = "Adds a duration to this Time_Of_Day")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@TruffleBoundary
public EnsoTimeOfDay plus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
assert interop.isDuration(durationObject);
return new EnsoTimeOfDay(localTime.plus(interop.asDuration(durationObject)));
}
@Builtin.Method(
name = "minus_builtin",
description = "Subtracts a duration from this Time_Of_Day")
@Builtin.Specialize
@Builtin.WrapException(from = UnsupportedMessageException.class, to = PanicException.class)
@TruffleBoundary
public EnsoTimeOfDay minus(Object durationObject, InteropLibrary interop)
throws UnsupportedMessageException {
assert interop.isDuration(durationObject);
return new EnsoTimeOfDay(localTime.minus(interop.asDuration(durationObject)));
}
@Builtin.Method(description = "Gets a value second")
@CompilerDirectives.TruffleBoundary
public long toSeconds() {

View File

@ -14,7 +14,7 @@ import java.lang.ref.PhantomReference;
/** A runtime representation of a managed resource. */
@ExportLibrary(TypesLibrary.class)
@Builtin(pkg = "resource", stdlibName = "Standard.Base.Runtime.Resource.Managed_Resource")
public class ManagedResource implements TruffleObject {
public final class ManagedResource implements TruffleObject {
private final Object resource;
private PhantomReference<ManagedResource> phantomReference;

View File

@ -11,7 +11,7 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
/** A mutable reference type. */
@ExportLibrary(TypesLibrary.class)
@Builtin(pkg = "mutable", stdlibName = "Standard.Base.Runtime.Ref.Ref")
public class Ref implements TruffleObject {
public final class Ref implements TruffleObject {
private volatile Object value;
/**

View File

@ -29,7 +29,7 @@ import java.util.*;
@ExportLibrary(TypesLibrary.class)
@ExportLibrary(InteropLibrary.class)
public class Type implements TruffleObject {
public final class Type implements TruffleObject {
private final String name;
private @CompilerDirectives.CompilationFinal ModuleScope definitionScope;
private final boolean builtin;

View File

@ -18,7 +18,7 @@ import java.util.concurrent.locks.ReentrantLock;
/** The main runtime type for Enso's Text. */
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
public class Text implements TruffleObject {
public final class Text implements TruffleObject {
private volatile Object contents;
private volatile boolean isFlat;
private static final Lock lock = new ReentrantLock();

View File

@ -23,7 +23,7 @@ import java.util.Comparator;
@Builtin(pkg = "error", stdlibName = "Standard.Base.Warning.Warning")
@ExportLibrary(TypesLibrary.class)
public class Warning implements TruffleObject {
public final class Warning implements TruffleObject {
private final Object value;
private final Object origin;
private final ArrayRope<Reassignment> reassignments;
@ -137,7 +137,7 @@ public class Warning implements TruffleObject {
}
@ExportLibrary(InteropLibrary.class)
public static class Reassignment implements TruffleObject {
public static final class Reassignment implements TruffleObject {
private final String methodName;
private final SourceSection location;

View File

@ -8,7 +8,7 @@ import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ExportLibrary(TypesLibrary.class)
public class WithWarnings implements TruffleObject {
public final class WithWarnings implements TruffleObject {
private final ArrayRope<Warning> warnings;
private final Object value;

View File

@ -17,7 +17,7 @@ import java.math.BigInteger;
/** Internal wrapper for a {@link BigInteger}. */
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
public class EnsoBigInteger implements TruffleObject {
public final class EnsoBigInteger implements TruffleObject {
private final BigInteger value;
/**

View File

@ -14,7 +14,7 @@ import org.enso.interpreter.runtime.error.RedefinedMethodException;
import org.enso.interpreter.runtime.error.RedefinedConversionException;
/** A representation of Enso's per-file top-level scope. */
public class ModuleScope implements TruffleObject {
public final class ModuleScope implements TruffleObject {
private final Type associatedType;
private final Module module;
private Map<String, Object> polyglotSymbols = new HashMap<>();

View File

@ -28,7 +28,7 @@ import org.enso.polyglot.MethodNames;
/** Represents the top scope of Enso execution, containing all the importable modules. */
@ExportLibrary(InteropLibrary.class)
public class TopLevelScope implements TruffleObject {
public final class TopLevelScope implements TruffleObject {
private final Builtins builtins;
private final PackageRepository packageRepository;

View File

@ -52,6 +52,7 @@ import org.enso.polyglot.data.TypeGraph;
EnsoDateTime.class,
EnsoTimeOfDay.class,
EnsoTimeZone.class,
EnsoDuration.class
})
public class Types {
@ -142,6 +143,8 @@ public class Types {
return ConstantsGen.DATE_TIME;
} else if (TypesGen.isEnsoTimeOfDay(value)) {
return ConstantsGen.TIME_OF_DAY;
} else if (TypesGen.isEnsoDuration(value)) {
return ConstantsGen.DURATION;
} else if (TypesGen.isEnsoTimeZone(value)) {
return ConstantsGen.TIME_ZONE;
} else if (TypesGen.isEnsoFile(value)) {
@ -245,6 +248,7 @@ public class Types {
graph.insert(ConstantsGen.DATE, ConstantsGen.ANY);
graph.insert(ConstantsGen.DATE_TIME, ConstantsGen.ANY);
graph.insert(ConstantsGen.TIME_OF_DAY, ConstantsGen.ANY);
graph.insert(ConstantsGen.DURATION, ConstantsGen.ANY);
graph.insert(ConstantsGen.TIME_ZONE, ConstantsGen.ANY);
graph.insertWithoutParent(ConstantsGen.PANIC);
graph.insertWithoutParent(Constants.THUNK);

View File

@ -49,6 +49,7 @@ public record TypeWithKind(String baseType, TypeKind kind) {
"org.enso.interpreter.runtime.data.EnsoDateTime",
"org.enso.interpreter.runtime.data.EnsoTimeOfDay",
"org.enso.interpreter.runtime.data.EnsoTimeZone",
"org.enso.interpreter.runtime.data.EnsoDuration",
"org.enso.interpreter.runtime.data.ManagedResource",
"org.enso.interpreter.runtime.data.Ref",
"org.enso.interpreter.runtime.data.text.Text",

View File

@ -1,4 +1,3 @@
import LibraryManifestGenerator.BundledLibrary
import sbt.Keys._
import sbt._
import sbt.internal.util.ManagedLogger
@ -6,7 +5,6 @@ import sbt.io.IO
import sbt.librarymanagement.{ConfigurationFilter, DependencyFilter}
import java.io.File
import scala.sys.process.Process
object StdBits {
@ -24,8 +22,7 @@ object StdBits {
def copyDependencies(
destination: File,
baseJarName: Option[String],
ignoreScalaLibrary: Boolean,
unpackedDeps: Set[String] = Set()
ignoreScalaLibrary: Boolean
): Def.Initialize[Task[Unit]] =
Def.task {
val libraryUpdates = (Compile / update).value
@ -53,7 +50,7 @@ object StdBits {
report =>
val expectedFileNames =
report.checked.map(
getDestinationFileName(_, unpackedDeps)
file => file.getName
) ++ baseJarName.toSeq
for (existing <- IO.listFiles(destination)) {
if (!expectedFileNames.contains(existing.getName)) {
@ -67,53 +64,25 @@ object StdBits {
log.info(
s"Updating changed std-bits dependency ${changed.getName}."
)
updateDependency(changed, unpackedDeps, destination, log)
updateDependency(changed, destination, log)
}
for (file <- report.unmodified) {
val dest = destination / getDestinationFileName(file, unpackedDeps)
val dest = destination / file.getName
if (!dest.exists()) {
log.info(s"Adding missing std-bits dependency ${file.getName}.")
updateDependency(file, unpackedDeps, destination, log)
updateDependency(file, destination, log)
}
}
}
}
private def shouldUnpack(jar: File, unpacked: Set[String]): Boolean = {
// Maven stores dependencies like this:
// .../repo1.maven.org/maven2/org/apache/xmlbeans/xmlbeans/5.0.1/xmlbeans-5.0.1.jar
// therefore, the parent of the parent of the jar file is the directory
// named with the un-versioned library name.
unpacked.contains(jar.getParentFile.getParentFile.getName)
}
private def updateDependency(
jar: File,
unpacked: Set[String],
destinationDir: File,
logger: ManagedLogger
): Unit = {
val destination = destinationDir / getDestinationFileName(jar, unpacked)
if (shouldUnpack(jar, unpacked)) {
destination.mkdirs()
val exitCode = Process(s"jar xf ${jar.getAbsolutePath}", destination).!
if (exitCode != 0) {
logger.err(s"Could not unpack a dependency jar: $jar.")
throw new RuntimeException(s"Could not unpack a dependency jar: $jar.")
}
} else {
IO.copyFile(jar, destination)
}
}
private def getDestinationFileName(
file: File,
unpacked: Set[String]
): String = {
if (shouldUnpack(file, unpacked)) {
val name = file.getName
name.stripSuffix(".jar")
} else file.getName
val destination = destinationDir / jar.getName
IO.copyFile(jar, destination)
}
/**

View File

@ -6,10 +6,21 @@ import org.enso.base.time.TimeUtilsBase;
import org.enso.base.time.Time_Of_Day_Utils;
import org.graalvm.polyglot.Value;
import java.time.*;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.*;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
import java.util.Locale;
/** Utils for standard library operations on Time. */
@ -79,29 +90,6 @@ public class Time_Utils {
}
}
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 int get_field_as_localdate(LocalDate date, TemporalField field) {
return date.get(field);
}

View File

@ -92,33 +92,33 @@ spec_with name create_new_date parse_date =
date.to_json.should_equal <|
Json.from_pairs [["type", "Date"], ["day", date.day], ["month", date.month], ["year", date.year]]
Test.specify "should add date-based interval" <|
Test.specify "should add date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
date = create_new_date 1970 + 1.day
date . year . should_equal 1970
date . month . should_equal 1
date . day . should_equal 2
Test.specify "should subtract date-based interval" <|
Test.specify "should subtract date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
date = create_new_date 1970 - 1.year
date . year . should_equal 1969
date . month . should_equal 1
date . day . should_equal 1
Test.specify "should support mixed interval operators" <|
Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
date = create_new_date 1970 + 1.month - 1.year
date . year . should_equal 1969
date . month . should_equal 2
date . day . should_equal 1
Test.specify "should throw error when adding time-based interval" <|
case (create_new_date 1970 + 1.hour) . catch of
Test.specify "should throw error when adding time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
case (create_new_date 1970 + (Duration.new hours=1)) . catch of
Time_Error_Data message ->
message . should_equal "Date does not support time intervals"
result ->
Test.fail ("Unexpected result: " + result.to_text)
Test.specify "should throw error when subtracting time-based interval" <|
case (create_new_date 1970 - (1.day - 1.minute)) . catch of
Test.specify "should throw error when subtracting time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
case (create_new_date 1970 - (1.day - (Duration.new minutes=1))) . catch of
Time_Error_Data message ->
message . should_equal "Date does not support time intervals"
result ->
@ -199,7 +199,7 @@ spec_with name create_new_date parse_date =
(create_new_date 2000 7 1).end_of Date_Period.Quarter . should_equal (Date.new 2000 9 30)
(create_new_date 2000 6 30).end_of Date_Period.Quarter . should_equal (Date.new 2000 6 30)
Test.specify "should allow to compute the number of working days until a later date" <|
Test.specify "should allow to compute the number of working days until a later date" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2000-2-1 is a Tuesday
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) . should_equal 0
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) . should_equal 1
@ -250,7 +250,7 @@ spec_with name create_new_date parse_date =
# We duplicate the holiday entries to check that the functions are resilient to such input data.
duplicated_holiday_november year =
holiday_november year + holiday_november year + holiday_november year
Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" <|
Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
holiday_february = Vector.new 29 (i -> create_new_date 2000 2 i+1)
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 3 1) holiday_february . should_equal 0
create_new_date 2000 2 10 . work_days_until (create_new_date 2000 2 12) holiday_february . should_equal 0
@ -265,7 +265,7 @@ spec_with name create_new_date parse_date =
create_new_date 2000 11 1 . work_days_until (create_new_date 2000 12 1) (duplicated_holiday_november 2020) . should_equal 22
create_new_date 1999 11 1 . work_days_until (create_new_date 1999 12 1) (duplicated_holiday_november 1999) . should_equal 19
Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" <|
Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) include_end_date=True . should_equal 1
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) include_end_date=True . should_equal 2
create_new_date 2000 2 3 . work_days_until (create_new_date 2000 2 16) include_end_date=True . should_equal 10
@ -282,7 +282,7 @@ spec_with name create_new_date parse_date =
create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 8) include_end_date=True . should_equal 2
create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 5) include_end_date=True . should_equal 0
Test.specify "should allow to shift the date by N working days" <|
Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2000-2-1 is a Tuesday
create_new_date 2000 2 1 . add_work_days 0 . should_equal (Date.new 2000 2 1)
create_new_date 2000 2 1 . add_work_days . should_equal (Date.new 2000 2 2)
@ -314,7 +314,7 @@ spec_with name create_new_date parse_date =
create_new_date 2022 3 27 . add_work_days 0 . should_equal (Date.new 2022 3 28)
create_new_date 2022 3 27 . add_work_days 1 . should_equal (Date.new 2022 3 29)
Test.specify "should allow to shift the date by negative amount of working days" <|
Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2000-2-1 is a Tuesday
create_new_date 2000 2 1 . add_work_days -1 . should_equal (Date.new 2000 1 31)
create_new_date 2000 2 1 . add_work_days -2 . should_equal (Date.new 2000 1 28)
@ -343,7 +343,7 @@ spec_with name create_new_date parse_date =
create_new_date 2000 2 6 . add_work_days -5 . should_equal (Date.new 2000 1 31)
create_new_date 2000 2 6 . add_work_days -6 . should_equal (Date.new 2000 1 28)
Test.specify "should allow to shift the date by N working days, skipping custom holidays" <|
Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
all_year_holiday year =
first_day = create_new_date year 1 1
Vector.new first_day.length_of_year (n -> first_day + n.days)
@ -364,7 +364,7 @@ spec_with name create_new_date parse_date =
create_new_date 1999 10 30 . add_work_days 0 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 3)
create_new_date 1999 10 30 . add_work_days 1 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 4)
Test.specify "add_work_days and work_days_until should be consistent with each other" <|
Test.specify "add_work_days and work_days_until should be consistent with each other" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
first_day = create_new_date 2020 1 1
dates = Vector.new 100 (n -> first_day + n.days)
holidays = [1, 2, 10, 11, 12, 13, 14, 15, 30, 40, 41, 42, 50, 60].map (n -> first_day + n.days)

View File

@ -230,7 +230,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . day . should_equal 1
Test.specify "should add time interval" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) + 1.nanosecond
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (Duration.new nanoseconds=1)
time . year . should_equal 1970
time . month . should_equal 1
time . day . should_equal 1
@ -240,7 +240,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . nanosecond . should_equal 1
time . zone . should_equal Time_Zone.utc
Test.specify "should add date interval" <|
Test.specify "should add date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) + 1.month
time . year . should_equal 1970
time . month . should_equal 2
@ -251,8 +251,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . nanosecond . should_equal 0
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should add mixed date time interval" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (1.month + 3.hours)
Test.specify "should add mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (1.month + (Duration.new hours=3))
time . year . should_equal 1970
time . month . should_equal 2
time . day . should_equal 1
@ -263,7 +263,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should subtract time interval" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.hour
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Duration.new hours=1)
time . year . should_equal 1969
time . month . should_equal 12
time . day . should_equal 31
@ -273,7 +273,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . nanosecond . should_equal 0
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should subtract date interval" <|
Test.specify "should subtract date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month
time . year . should_equal 1969
time . month . should_equal 12
@ -284,8 +284,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . nanosecond . should_equal 0
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should subtract mixed date time interval" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (1.month - 3.hours)
Test.specify "should subtract mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (1.month - (Duration.new hours=3))
time . year . should_equal 1969
time . month . should_equal 12
time . day . should_equal 1
@ -295,8 +295,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . nanosecond . should_equal 0
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should support mixed interval operators" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month + 12.hours
Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month + (Duration.new hours=12)
time . year . should_equal 1969
time . month . should_equal 12
time . day . should_equal 1
@ -428,7 +428,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the spring DST switch" pending=js_dst_pending <|
d1 = create_new_datetime 2022 3 27 1 34 15 0 tz
d2 = create_new_datetime 2022 3 27 3 34 15 0 tz
d1_plus = d1 + 1.hour
d1_plus = d1 + (Duration.new hours=1)
d1_plus . should_equal d2
check_dates_spring date =
@ -460,7 +460,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
dst_overlap_message = "We cannot correctly migrate the datetime inside of the timeline overlap through the polyglot boundar - as due to polyglot conversion limitation, always the earlier one is chosen. See the bug report: https://github.com/oracle/graal/issues/4918"
Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the autumn DST switch" pending=dst_overlap_message <|
d3 = create_new_datetime 2022 10 30 2 30 15 0 tz
d4 = d3 + (1.hour)
d4 = d3 + (Duration.new hours=1)
d3.hour . should_equal 2
d4.hour . should_equal 2
@ -502,7 +502,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
d4_end.nanosecond . should_equal max_nanos
Time_Utils.get_datetime_offset d4_end . should_equal offset_1_h
Test.specify "should allow to shift the date by N working days" <|
Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2000-2-1 is a Tuesday
create_new_datetime 2000 2 1 12 30 . add_work_days 0 . should_equal (Date_Time.new 2000 2 1 12 30)
create_new_datetime 2000 2 1 12 15 55 . add_work_days . should_equal (Date_Time.new 2000 2 2 12 15 55)
@ -523,7 +523,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
create_new_datetime 2000 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 2000 3 2 12 10)
create_new_datetime 1999 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 1999 3 4 12 10)
Test.specify "should handle shifting dates around spring DST edge cases" pending=js_dst_pending <|
Test.specify "should handle shifting dates around spring DST edge cases" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2022-10-30 and 2022-03-27 are DST switch days, Sundays.
create_new_datetime 2022 10 30 2 30 55 1234 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 55 1234)
create_new_datetime 2022 10 30 1 30 . add_work_days 1 . should_equal (Date_Time.new 2022 11 1 1 30)
@ -534,12 +534,12 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
Test.specify "should handle shifting dates around autumn DST edge cases" pending=dst_overlap_message <|
d3 = create_new_datetime 2022 10 30 2 30 15 0 tz
d4 = d3 + (1.hour)
d4 = d3 + (Duration.new hours=1)
# TODO we need to check and document the actual behaviour once it is expressible, it may be equally acceptable to shift to 3:30 instead of 2:30.
d4 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 15 0 tz)
Test.specify "should allow to shift the date by negative amount of working days" <|
Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
# 2000-2-1 is a Tuesday
create_new_datetime 2000 2 1 12 30 . add_work_days -1 . should_equal (Date_Time.new 2000 1 31 12 30)
create_new_datetime 2000 2 1 13 30 . add_work_days -2 . should_equal (Date_Time.new 2000 1 28 13 30)
@ -549,7 +549,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
create_new_datetime 2000 2 6 0 1 . add_work_days -2 . should_equal (Date_Time.new 2000 2 3 0 1)
create_new_datetime 2000 2 6 23 59 . add_work_days -6 . should_equal (Date_Time.new 2000 1 28 23 59)
Test.specify "should allow to shift the date by N working days, skipping custom holidays" <|
Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
all_year_holiday year =
first_day = Date.new year 1 1
Vector.new first_day.length_of_year (n -> first_day + n.days)

View File

@ -3,99 +3,93 @@ from Standard.Base import all
import Standard.Base.Data.Time.Duration
import Standard.Test
polyglot java import java.time.Duration as Java_Duration
polyglot java import java.time.LocalDate
polyglot java import java.time.LocalDateTime as Java_DateTime
java_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 =
Panic.catch Any (Java_DateTime.of year month day hour minute second nanosecond) (err -> Error.throw (Time_Error_Data <| err.payload.to_display_text.drop (Text_Sub_Range.First 16)))
js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 =
Panic.catch Any (js_datetime_impl year month day hour minute second nanosecond) (err -> Error.throw (Time_Error_Data err.payload.cause))
foreign js js_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);
spec =
Test.group "Duration" <|
Test.specify "should create interval seconds" <|
interval = 5.seconds
interval.to_vector . should_equal [0, 0, 0, 0, 0, 5, 0]
Test.specify "should create interval months" <|
interval = 9.months
interval.to_vector . should_equal [0, 9, 0, 0, 0, 0, 0]
Test.specify "should add days to nanoseconds" <|
interval = 7.nanoseconds + 3.days
interval.to_vector . should_equal [0, 0, 3, 0, 0, 0, 7]
Test.specify "should add milliseconds to years" <|
interval = 4.years + 8.milliseconds
interval.to_vector . should_equal [4, 0, 0, 0, 0, 0, 8000000]
Test.specify "should substract seconds from months" <|
interval = 8.months - 8.seconds
interval.to_vector . should_equal [0, 8, 0, 0, 0, -8, 0]
Test.specify "should subtract years from hours" <|
interval = 2.hours - 11.years
interval.to_vector . should_equal [-11, 0, 0, 2, 0, 0, 0]
Test.specify "should support mixed operators" <|
interval = 2.hours + 12.seconds - 11.years
interval.to_vector . should_equal [-11, 0, 0, 2, 0, 12, 0]
duration = (Duration.new seconds=5)
duration.seconds . should_equal 5
duration.milliseconds . should_equal 0
Test.specify "should create interval between two points in time" <|
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]
Test.specify "should check if time based" <|
interval = 10.hours
interval.is_date . should_be_false
interval.is_time . should_be_true
Test.specify "should check if date based" <|
interval = 10.years
interval.is_date . should_be_true
interval.is_time . should_be_false
Test.specify "should check if mixed based" <|
interval = 10.years + 3.hours
interval.is_date . should_be_true
interval.is_time . should_be_true
(Duration.between (Date_Time.new 2001 1 1 3) (Date_Time.new 2001 1 1 10)).total_hours . should_equal 7
(Duration.between (Date_Time.new 2001 1 1) (Date_Time.new 2001 1 7)).total_hours . should_equal (6 * 24)
(Duration.between (Date_Time.new 2001 1 1 13) (Date_Time.new 2001 1 7 16)).total_hours . should_equal (3 + 6 * 24)
Test.specify "should check if empty" <|
interval = 0.seconds
interval = Duration.zero
interval.is_empty . should_be_true
Test.specify "should normalize periods" <|
interval = 12.months
interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0]
(Duration.new seconds=60).total_minutes . should_equal 1
(Duration.new milliseconds=1000).total_seconds . should_equal 1
Test.specify "should normalize addition" <|
interval = 11.months + 1.month
interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0]
duration = (Duration.new hours=11) + (Duration.new hours=1)
duration.hours . should_equal 12
Test.specify "should normalize subtraction" <|
interval = 13.months - 1.month
interval.to_vector . should_equal [1, 0, 0, 0, 0, 0, 0]
Test.specify "should check equality" <|
3.seconds.should_equal 3.seconds
60.seconds.should_equal 1.minute
61.seconds.should_equal (1.minute + 1.second)
60.minutes.should_equal 1.hour
(24.hours == 1.day) . should_be_false
(30.days == 1.month) . should_be_false
12.months.should_equal 1.year
18.months.should_equal (1.year + 6.months)
1.year.should_equal (11.months + 1.month)
10.years.should_equal 10.years
duration = (Duration.new hours=13) - (Duration.new hours=1)
duration.hours . should_equal 12
Test.specify "should convert to Json" <|
interval = 120.nanoseconds + 30.seconds + 14.hours + 12.days + 1.month + 9.years
interval = (Duration.new nanoseconds=120) + (Duration.new seconds=30) + (Duration.new hours=14)
interval.to_json.should_equal <|
duration_pairs = [["nanoseconds", interval.nanoseconds], ["seconds", interval.seconds], ["hours", interval.hours], ["days", interval.days], ["months", interval.months], ["years", interval.years]]
duration_pairs = [["nanoseconds", interval.nanoseconds], ["seconds", interval.seconds], ["hours", interval.hours]]
Json.from_pairs ([["type", "Duration"]] + duration_pairs)
Test.specify "should be comparable" <|
duration_1 = 1.year
duration_2 = 24.days
duration_1 = (Duration.new hours=5)
duration_2 = (Duration.new minutes=1)
duration_1.compare_to duration_1 . should_equal Ordering.Equal
duration_1==duration_1 . should_be_true
duration_1!=duration_2 . should_be_true
duration_1>duration_2 . should_be_true
duration_1<duration_2 . should_be_false
Test.specify "Date_Time supports adding and subtracting Duration" <|
((Date_Time.new 2022 10 1 hour=10) + (Duration.new hours=2)) . should_equal (Date_Time.new 2022 10 1 hour=12)
((Date_Time.new 2022 10 1 hour=10) - (Duration.new hours=2)) . should_equal (Date_Time.new 2022 10 1 hour=8)
((Date_Time.new 2022 10 2) - (Duration.new hours=24)) . should_equal (Date_Time.new 2022 10 1)
((Date_Time.new 2022 10 1 hour=2) - (Duration.new minutes=3)) . should_equal (Date_Time.new 2022 10 1 hour=1 minute=57)
Test.specify "Java Duration is equal to Enso Duration" <|
(Duration.new hours=1) . should_equal (Java_Duration.ofHours 1)
(Duration.new minutes=80) . should_equal (Java_Duration.ofMinutes 80)
(Java_Duration.ofSeconds 30) . should_equal (Duration.new seconds=30)
Test.specify "Difference of Java Date and Enso date should be an Enso Duration" <|
(Duration.between (java_datetime 2022 01 01) (Date_Time.new 2022 01 02) timezone_aware=False).total_hours . should_equal 24
(Duration.between (Date_Time.new 2022 01 01) (java_datetime 2022 01 02) timezone_aware=False).total_hours . should_equal 24
(Duration.between (Date_Time.new 2022 01 01 13) (java_datetime 2022 01 01 14) timezone_aware=False).total_hours . should_equal 1
(Duration.between (java_datetime 2022 01 01 13 13) (Date_Time.new 2022 01 01 13 15) timezone_aware=False).total_minutes . should_equal 2
Test.specify "Difference of two Java Dates should be an Enso Duration" <|
(Duration.between (java_datetime 2022 01 01) (java_datetime 2022 01 02) timezone_aware=False).total_hours . should_equal 24
Test.specify "Difference of two JS Dates is an Enso Duration" <|
(Duration.between (js_datetime 2022 09 01) (js_datetime 2022 09 02) timezone_aware=False).total_hours . should_equal 24
Test.specify "Difference of JS Dates and Java Dates should be Enso Duration" <|
(Duration.between (js_datetime 2022 09 08) (java_datetime 2022 09 09) timezone_aware=False).total_hours . should_equal 24
(Duration.between (java_datetime 2022 09 09) (js_datetime 2022 09 08) timezone_aware=False).total_hours . should_equal (-24)
main = Test.Suite.run_main spec

View File

@ -83,26 +83,26 @@ specWith name create_new_time parse_time =
datetime . zone . zone_id . should_equal Time_Zone.utc.zone_id
Test.specify "should add time-based interval" <|
time = create_new_time 0 + 1.minute
time = create_new_time 0 + (Duration.new minutes=1)
time . to_seconds . should_equal 60
Test.specify "should subtract time-based interval" <|
time = create_new_time 0 - 1.minute
time = create_new_time 0 - (Duration.new minutes=1)
time . to_seconds . should_equal 86340
Test.specify "should support mixed interval operators" <|
time = create_new_time 0 + 1.hour - 1.second
time = create_new_time 0 + (Duration.new hours=1) - (Duration.new seconds=1)
time . to_seconds . should_equal 3599
Test.specify "should throw error when adding date-based interval" <|
Test.specify "should throw error when adding date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
case (create_new_time 0 + 1.day) . catch of
Time_Error_Data 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 (create_new_time 0 - (1.day - 1.minute)) . catch of
Test.specify "should throw error when subtracting date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
case (create_new_time 0 - (1.day - (Duration.new minutes=1))) . catch of
Time_Error_Data message ->
message . should_equal "Time_Of_Day does not support date intervals"
result ->

View File

@ -30,8 +30,8 @@ spec =
url_get = base_url_with_slash + "get"
url_post = base_url_with_slash + "post"
Test.specify "should create HTTP client with timeout setting" <|
http = Http.new (timeout = 30.seconds)
http.timeout.should_equal 30.seconds
http = Http.new (timeout = (Duration.new seconds=30))
http.timeout.should_equal (Duration.new seconds=30)
Test.specify "should create HTTP client with follow_redirects setting" <|
http = Http.new (follow_redirects = False)
http.follow_redirects.should_equal False

View File

@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Test
polyglot java import java.lang.Float
polyglot java import java.lang.Integer
@ -9,7 +10,6 @@ polyglot java import java.util.ArrayList
polyglot java import java.time.LocalDate
polyglot java import java.time.LocalTime
import Standard.Test
Any.test_me x = x.is_nothing