mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 18:38:11 +03:00
Implement Enso Time Library (#1171)
Add `Base.Time` module. The module wraps `java.time` data types and provides utility Enso methods to work with them.
This commit is contained in:
parent
91346a41fc
commit
72bf87c648
168
distribution/std-lib/Base/src/Time/Date.enso
Normal file
168
distribution/std-lib/Base/src/Time/Date.enso
Normal file
@ -0,0 +1,168 @@
|
||||
from Base import all
|
||||
import Base.Time.Time
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time_Of_Day
|
||||
import Base.Time.Zone
|
||||
|
||||
polyglot java import java.time.Instant
|
||||
polyglot java import java.time.LocalDate
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
type Date
|
||||
|
||||
## `Date` represents a date, often viewed as year-month-day. For example,
|
||||
the value "2nd October 2007" can be stored in a `Date`.
|
||||
|
||||
This class does not store or represent a time or timezone. Instead, it
|
||||
is a description of the date, as used for birthdays. It cannot represent
|
||||
an instant on the time-line without additional information such as an
|
||||
offset or timezone.
|
||||
type Date internal_local_date
|
||||
|
||||
## Get the year field.
|
||||
year : Integer
|
||||
year = this . internal_local_date . getYear []
|
||||
|
||||
## Get the month of year field, as a number from 1 to 12.
|
||||
month : Integer
|
||||
month = this . internal_local_date . getMonthValue []
|
||||
|
||||
## Get the day of month field.
|
||||
day : Integer
|
||||
day = this . internal_local_date . getDayOfMonth []
|
||||
|
||||
## Combine this date with time of day to create a point in time.
|
||||
|
||||
> Example
|
||||
Convert this date to midnight UTC time.
|
||||
Day.new 2020 2 3 . to_time Time_Of_Day.new Zone.utc
|
||||
to_time : Time_Of_Day -> Zone -> Time
|
||||
to_time daytime (zone = Zone.system) = Time.time (this . internal_local_date . atTime [daytime.internal_local_time] . atZone [zone.internal_zone_id])
|
||||
|
||||
## Add specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Add 6 months to a local date.
|
||||
Date.new 2020 + 6.months
|
||||
+ : Duration -> Date
|
||||
+ amount = if amount.is_time then Error.throw (Time.Time_Error "Date does not support time intervals") else Date (this . internal_local_date . plus [amount.interval_period])
|
||||
|
||||
## Subtract specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Subtract 7 days from a local date.
|
||||
Date.new 2020 - 7.days
|
||||
- : Duration -> Date
|
||||
- amount = if amount.is_time then Error.throw (Time.Time_Error "Date does not support time intervals") else Date (this . internal_local_date . minus [amount.interval_period])
|
||||
|
||||
## Format this date using the default formatter.
|
||||
to_text : Text
|
||||
to_text = Time_Utils.default_date_formatter [] . format [this.internal_local_date]
|
||||
|
||||
## Format this date using formatter text.
|
||||
|
||||
Patterns are based on a simple sequence of letters and symbols. For
|
||||
example, "d MMM yyyy" will format "2011-12-03" as "3 Dec 2011".
|
||||
|
||||
For the list of accepted symbols in pattern refer to
|
||||
`Base.Time.Time.format` doc.
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "2 June 2020"
|
||||
Date.new 2020 6 2 . format "d MMMM yyyy"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "2 June 20"
|
||||
Date.new 2020 6 2 . format "d MMMM yy"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "Tuesday, 02 June 2020"
|
||||
Date.new 2020 6 2 . format "EEEE, dd MMMM yyyy"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "Tue Jun 2"
|
||||
Date.new 2020 6 2 . format "EEE MMM d"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "2020AD"
|
||||
Date.new 2020 6 2 . format "yyyyGG"
|
||||
format : Text -> Text
|
||||
format pattern = DateTimeFormatter.ofPattern [pattern] . format [this.internal_local_date]
|
||||
|
||||
## Obtains an instance of `Date` from a text, such as "2007-12-03".
|
||||
|
||||
The text must represent a valid date and is parsed using the ISO-8601
|
||||
extended local date format. The format consists of:
|
||||
|
||||
- Four digits or more for the year. Years in the range 0000 to 9999
|
||||
will be pre-padded by zero to ensure four digits. Years outside
|
||||
that range will have a prefixed positive or negative symbol.
|
||||
- A dash
|
||||
- Two digits for the month-of-year. This is pre-padded by zero to ensure two
|
||||
digits.
|
||||
- A dash
|
||||
- Two digits for the day-of-month. This is pre-padded by zero to ensure two
|
||||
digits.
|
||||
|
||||
> Example
|
||||
Parse the date of 23rd December 2020.
|
||||
Date.parse "2020-12-23"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
Date.parse "my birthday" . catch <| case _ of
|
||||
Time.Error _ -> Date.new 2000 1 1
|
||||
parse : Text -> Date
|
||||
parse text =
|
||||
Panic.recover (Date (LocalDate.parse [text])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains an instance of `Date` from a text using custom format.
|
||||
|
||||
For the list of accepted symbols in pattern refer to
|
||||
`Base.Time.Time.format` doc.
|
||||
|
||||
> Example
|
||||
Parse "1999-1-1" as Date.
|
||||
Date.parse_format "1999-1-1" "yyyy-M-d"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
date = Date.parse "1999-1-1" "yyyy-MM-dd"
|
||||
date.catch <| case _ of
|
||||
Time.Error msg -> Date.new 2000 1 1
|
||||
parse_format : Text -> Text -> Date
|
||||
parse_format text pattern =
|
||||
format = DateTimeFormatter.ofPattern [pattern]
|
||||
Panic.recover (Date (LocalDate.parse [text, format])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains the current date from the system clock in the system timezone.
|
||||
now : Date
|
||||
now = Date (LocalDate.now [])
|
||||
|
||||
## Alias for `now`.
|
||||
today : Date
|
||||
today = here.now
|
||||
|
||||
## Obtains an instance of `Date` from a year, month and day.
|
||||
|
||||
- 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
|
||||
|
||||
> Example
|
||||
Create a new local date at Unix epoch.
|
||||
Date.new 1970
|
||||
|
||||
> Example
|
||||
Get the local date of 5th August 1986.
|
||||
Date.new 1986 8 5
|
||||
new : Integer -> Integer -> Integer -> Date
|
||||
new year (month = 1) (day = 1) =
|
||||
Panic.recover (Date (LocalDate.of [year, month, day])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
164
distribution/std-lib/Base/src/Time/Duration.enso
Normal file
164
distribution/std-lib/Base/src/Time/Duration.enso
Normal file
@ -0,0 +1,164 @@
|
||||
from Base import all
|
||||
import Base.Time.Time
|
||||
|
||||
polyglot java import java.time.Duration as Java_Duration
|
||||
polyglot java import java.time.Period as Java_Period
|
||||
|
||||
type Duration
|
||||
|
||||
## An amount of time in terms of years, months, days, hours, minutes,
|
||||
seconds and nanoseconds.
|
||||
type Duration interval_period interval_duration
|
||||
|
||||
## Add specified amount of time to this duration.
|
||||
|
||||
> Example
|
||||
Add 6 seconds to a duration of 3 minutes
|
||||
3.minutes + 6.seconds
|
||||
|
||||
> Example
|
||||
Add 12 hours to a duration of a month.
|
||||
1.month + 12.hours
|
||||
+ : Duration -> Duration
|
||||
+ other = Duration (this.interval_period . plus [other.interval_period]) (this.interval_duration . plus [other.interval_duration])
|
||||
|
||||
## Subtract specified amount of time from this duration.
|
||||
> Example
|
||||
Subtract 11 months from a duration of 3 years
|
||||
3.years - 11.months
|
||||
|
||||
> Example
|
||||
Substract 30 minutes from a duration of 7 months.
|
||||
7.months - 30.minutes
|
||||
- : Duration -> Duration
|
||||
- other = Duration (this.interval_period . minus [other.interval_period]) (this.interval_duration . minus [other.interval_duration])
|
||||
|
||||
## Get the amount of nanoseconds of this duration.
|
||||
nanoseconds : Integer
|
||||
nanoseconds = this.interval_duration . toNanosPart []
|
||||
|
||||
## Get the amount of milliseconds of this duration.
|
||||
milliseconds : Integer
|
||||
milliseconds = this.interval_duration . toMillisPart []
|
||||
|
||||
## Get the amount of minutes of this duration.
|
||||
seconds : Integer
|
||||
seconds = this.interval_duration . toSecondsPart []
|
||||
|
||||
## Get the amount of minutes of this duration.
|
||||
minutes : Integer
|
||||
minutes = this.interval_duration . toMinutesPart []
|
||||
|
||||
## Get the amount of hours of this duration.
|
||||
hours : Integer
|
||||
hours = this.interval_duration . toHours []
|
||||
|
||||
## Get the amount of days of this duration.
|
||||
days : Integer
|
||||
days = this.interval_period . getDays []
|
||||
|
||||
## Get the amount of months of this duration.
|
||||
months : Integer
|
||||
months = this.interval_period . getMonths []
|
||||
|
||||
## Get the amount of days of this duration.
|
||||
years : Integer
|
||||
years = this.interval_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]`.
|
||||
1.year . plus 1.hour . to_vector
|
||||
|
||||
> Example
|
||||
Convert duration of 800 nanoseconds to a vector returning `[0, 0, 0, 0, 0, 0, 800]`
|
||||
800.nanoseconds . to_vector
|
||||
to_vector : Vector
|
||||
to_vector = [this.years, this.months, this.days, this.hours, this.minutes, this.seconds, this.nanoseconds]
|
||||
|
||||
## Check if this duration is date-based.
|
||||
is_date : Boolean
|
||||
is_date = (not this.years==0) || (not this.months==0) || (not this.days==0)
|
||||
|
||||
## Check if this duration is time-based.
|
||||
is_time : Boolean
|
||||
is_time = (not this.hours==0) || (not this.minutes==0) || (not this.seconds==0) || (not this.nanoseconds==0)
|
||||
|
||||
## Check if this duration represents an empty time-span.
|
||||
is_empty : Boolean
|
||||
is_empty = (not this.is_date) && (not this.is_time)
|
||||
|
||||
## Duration in nanoseconds.
|
||||
Integer.nanosecond : Duration
|
||||
Integer.nanosecond = Duration (Java_Period.ofDays [0]) (Java_Duration.ofNanos [this])
|
||||
|
||||
## Duration in nanoseconds.
|
||||
Integer.nanoseconds : Duration
|
||||
Integer.nanoseconds = this.nanosecond
|
||||
|
||||
## Duration in milliseconds.
|
||||
Integer.millisecond : Duration
|
||||
Integer.millisecond = Duration (Java_Period.ofDays [0]) (Java_Duration.ofMillis [this])
|
||||
|
||||
## Duration in milliseconds.
|
||||
Integer.milliseconds : Duration
|
||||
Integer.milliseconds = this.millisecond
|
||||
|
||||
## Duration in seconds.
|
||||
Integer.second : Duration
|
||||
Integer.second = Duration (Java_Period.ofDays [0]) (Java_Duration.ofSeconds [this])
|
||||
|
||||
## Duration in seconds.
|
||||
Integer.seconds : Duration
|
||||
Integer.seconds = this.second
|
||||
|
||||
## Duration in minutes.
|
||||
Integer.minute : Duration
|
||||
Integer.minute = Duration (Java_Period.ofDays [0]) (Java_Duration.ofMinutes [this])
|
||||
|
||||
## Duration in minutes.
|
||||
Integer.minutes : Duration
|
||||
Integer.minutes = this.minute
|
||||
|
||||
## Duration in hours.
|
||||
Integer.hour : Duration
|
||||
Integer.hour = Duration (Java_Period.ofDays [0]) (Java_Duration.ofHours [this])
|
||||
|
||||
## Duration in hours.
|
||||
Integer.hours : Duration
|
||||
Integer.hours = this.hour
|
||||
|
||||
## Duration in days.
|
||||
Integer.day : Duration
|
||||
Integer.day = Duration (Java_Period.ofDays [this]) (Java_Duration.ofSeconds [0])
|
||||
|
||||
## Duration in days.
|
||||
Integer.days : Duration
|
||||
Integer.days = this.day
|
||||
|
||||
## Duration in months.
|
||||
Integer.month : Duration
|
||||
Integer.month = Duration (Java_Period.ofMonths [this]) (Java_Duration.ofSeconds [0])
|
||||
|
||||
## Duration in months.
|
||||
Integer.months : Duration
|
||||
Integer.months = this.month
|
||||
|
||||
## Duration in years.
|
||||
Integer.year : Duration
|
||||
Integer.year = Duration (Java_Period.ofYears [this]) (Java_Duration.ofSeconds [0])
|
||||
|
||||
## Duration in years.
|
||||
Integer.years : Duration
|
||||
Integer.years = this.year
|
||||
|
||||
## Create an interval representing the duration between two points in time.
|
||||
|
||||
> Example
|
||||
An hour interval between two points in time.
|
||||
Duration.between Time.now (Time.new 2010 10 20)
|
||||
between : Time -> Time -> Duration
|
||||
between start_inclusive end_exclusive =
|
||||
Duration (Java_Period.ofDays [0]) (Java_Duration.between [start_inclusive.internal_zoned_date_time, end_exclusive.internal_zoned_date_time])
|
260
distribution/std-lib/Base/src/Time/Time.enso
Normal file
260
distribution/std-lib/Base/src/Time/Time.enso
Normal file
@ -0,0 +1,260 @@
|
||||
from Base import all
|
||||
import Base.Time.Date
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time_Of_Day
|
||||
import Base.Time.Zone
|
||||
|
||||
polyglot java import java.time.ZonedDateTime
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
type Time_Error
|
||||
|
||||
## Error produced while working with time.
|
||||
type Time_Error error_message
|
||||
|
||||
type Time
|
||||
|
||||
## 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`.
|
||||
type Time internal_zoned_date_time
|
||||
|
||||
## Get the year field.
|
||||
year : Integer
|
||||
year = this . internal_zoned_date_time . getYear []
|
||||
|
||||
## Get the month of year field from 1 to 12.
|
||||
month : Integer
|
||||
month = this . internal_zoned_date_time . getMonthValue []
|
||||
|
||||
## Get the day of month field.
|
||||
day : Integer
|
||||
day = this . internal_zoned_date_time . getDayOfMonth []
|
||||
|
||||
## Get the hour of day field.
|
||||
hour : Integer
|
||||
hour = this . internal_zoned_date_time . getHour []
|
||||
|
||||
## Get the minute of hour field.
|
||||
minute : Integer
|
||||
minute = this . internal_zoned_date_time . getMinute []
|
||||
|
||||
## Get the second of minute field
|
||||
second : Integer
|
||||
second = this . internal_zoned_date_time . getSecond []
|
||||
|
||||
## Get the nano-of-second field.
|
||||
nanosecond : Integer
|
||||
nanosecond = this . internal_zoned_date_time . getNano []
|
||||
|
||||
## Get the timezone.
|
||||
zone : Zone
|
||||
zone = Zone.zone (this . internal_zoned_date_time . getZone [])
|
||||
|
||||
## Return the number of seconds from the Unix epoch.
|
||||
to_epoch_seconds : Integer
|
||||
to_epoch_seconds = this . internal_zoned_date_time . toEpochSecond []
|
||||
|
||||
## Return the number of milliseconds from the Unix epoch.
|
||||
to_epoch_milliseconds : Integer
|
||||
to_epoch_milliseconds = this . internal_zoned_date_time . toInstant [] . toEpochMilli []
|
||||
|
||||
## Convert this point in time to time of day.
|
||||
time_of_day : Time_Of_Day
|
||||
time_of_day = Time_Of_Day.time_of_day (this . internal_zoned_date_time . toLocalTime [])
|
||||
|
||||
## Convert this point in time to date.
|
||||
date : Date
|
||||
date = Date.date (this . internal_zoned_date_time . toLocalDate [])
|
||||
|
||||
## Convert the time instant to a provided timezone.
|
||||
|
||||
> Example
|
||||
Convert time instance to -04:00 timezone.
|
||||
Time.new 2020 . at_zone (Zone.new -4)
|
||||
at_zone : Zone -> Time
|
||||
at_zone zone = Time (this.internal_zoned_date_time . withZoneSameInstant [zone.internal_zone_id])
|
||||
|
||||
## Add specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Add 1 hour to a zoned date time.
|
||||
Time.new 2020 + 1.hour
|
||||
|
||||
> Example
|
||||
Add 15 years and 3 hours to a zoned date time.
|
||||
Time.new 2020 + 15.years + 3.hours
|
||||
+ : Duration -> Time
|
||||
+ amount = Time (this . internal_zoned_date_time . plus [amount.interval_period] . plus [amount.interval_duration])
|
||||
|
||||
## Subtract specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Subtract 10 days from a zoned date time.
|
||||
Time.new 2020 - 10.days
|
||||
|
||||
> Example
|
||||
Subtract 1 year and 9 months from a zoned date time.
|
||||
Time.new 2020 - 1.year - 9.months
|
||||
- : Duration -> Time
|
||||
- amount = Time (this . internal_zoned_date_time . minus [amount.interval_period] . minus [amount.interval_duration])
|
||||
|
||||
## Format this time using the default formatter.
|
||||
to_text : Text
|
||||
to_text = Time_Utils.default_time_formatter [] . format [this.internal_zoned_date_time]
|
||||
|
||||
## Format this time using formatter text.
|
||||
|
||||
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]"
|
||||
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"
|
||||
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)"
|
||||
Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEE MMM d (HH:mm)"
|
||||
format : Text -> Text
|
||||
format pattern = DateTimeFormatter.ofPattern [pattern] . format [this.internal_zoned_date_time]
|
||||
|
||||
## Obtains an instance of `Time` from a text such as
|
||||
"2007-12-03T10:15:30+01:00 Europe/Paris".
|
||||
|
||||
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 ']'.
|
||||
|
||||
> Example
|
||||
Parse UTC time.
|
||||
Time.parse "2020-10-01T04:11:12Z"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time.
|
||||
Time.parse "2020-10-01T04:11:12-04:00"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time specifying New York timezone.
|
||||
Time.parse "2020-10-01T04:11:12-04:00[America/New_York]"
|
||||
|
||||
> Example
|
||||
Parse UTC-04:00 time with nanoseconds.
|
||||
Time.parse "2020-10-01T04:11:12.177528-04:00"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
Time.parse "2020-10-01" . catch <| case _ of
|
||||
Time.Error _ -> Time.now
|
||||
parse : Text -> Time
|
||||
parse text =
|
||||
Panic.recover (Time (Time_Utils.parse_time [text])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains an instance of Time from a text using custom format.
|
||||
|
||||
For the list of accepted symbols in pattern refer to `Time.format` doc.
|
||||
|
||||
> Example
|
||||
Parse "2020-05-06 04:30:20" as Time
|
||||
Date.parse_format "2020-05-06 04:30:20" "yyyy-MM-dd HH:mm:ss"
|
||||
|
||||
> Example
|
||||
Parse "06 of May 2020 at 04:30AM" as Time
|
||||
Date.parse_format "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
|
||||
parse_format : Text -> Text -> Time_Of_Day
|
||||
parse_format text pattern =
|
||||
Panic.recover (Time (Time_Utils.parse_time_format [text, pattern])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains the current date-time from the system clock in the system timezone.
|
||||
now : Time
|
||||
now = Time (ZonedDateTime.now [])
|
||||
|
||||
## Obtains an instance of `Time` from a year, month, day, hour, minute,
|
||||
second, nanosecond and timezone.
|
||||
|
||||
- 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
|
||||
|
||||
> Example
|
||||
Create a new zoned date time at Unix epoch.
|
||||
Time.new 1970 (zone = Zone.utc)
|
||||
|
||||
> Example
|
||||
Get the 5 August 1986 at midnight.
|
||||
Time.new 1986 8 5
|
||||
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time
|
||||
new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) =
|
||||
Panic.recover (Time (ZonedDateTime.of [year, month, day, hour, minute, second, nanosecond, zone.internal_zone_id])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time_Error (err.getMessage []))
|
||||
x -> x
|
172
distribution/std-lib/Base/src/Time/Time_Of_Day.enso
Normal file
172
distribution/std-lib/Base/src/Time/Time_Of_Day.enso
Normal file
@ -0,0 +1,172 @@
|
||||
from Base import all
|
||||
import Base.Time.Date
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time
|
||||
import Base.Time.Zone
|
||||
|
||||
polyglot java import java.time.Instant
|
||||
polyglot java import java.time.LocalTime
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
type Time_Of_Day
|
||||
|
||||
## `Time_Of_Day` is a date-time object that represents a time, often viewed
|
||||
as hour-minute-second. 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
|
||||
|
||||
## Get the hour of day field.
|
||||
hour : Integer
|
||||
hour = this . internal_local_time . getHour []
|
||||
|
||||
## Get the minute of hour field.
|
||||
minute : Integer
|
||||
minute = this . internal_local_time . getMinute []
|
||||
|
||||
## Get the second of minute field.
|
||||
second : Integer
|
||||
second = this . internal_local_time . getSecond []
|
||||
|
||||
## Get the nanosecond of second field.
|
||||
nanosecond : Integer
|
||||
nanosecond = this . internal_local_time . getNano []
|
||||
|
||||
## Extracts the time as the number of seconds, from 0 to 24 * 60 * 60 - 1.
|
||||
to_seconds : Integer
|
||||
to_seconds = this . internal_local_time . toSecondOfDay []
|
||||
|
||||
## Combine this time of day with a date to create a point in time.
|
||||
|
||||
> Example
|
||||
Convert local time to 1st January 2020 12:30 at system timezone.
|
||||
Time_Of_Day.new 12 30 . to_time (Date.new 2020)
|
||||
to_time : Date -> Zone -> Time
|
||||
to_time date (zone = Zone.system) = Time.time (this . internal_local_time . atDate [date.internal_local_date] . atZone [zone.internal_zone_id])
|
||||
|
||||
## Add specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Add 3 seconds to a local time.
|
||||
Time_Of_Day.new + 3.seconds
|
||||
+ : Duration -> Time_Of_Day
|
||||
+ amount = if amount.is_date then Error.throw (Time.Time_Error "Time_Of_Day does not support date intervals") else Time_Of_Day (this . internal_local_time . plus [amount.interval_duration])
|
||||
|
||||
## Subtract specified amount of time to this instant.
|
||||
|
||||
> Example
|
||||
Subtract 12 hours from a local time.
|
||||
Time_Of_Day.new - 12.hours
|
||||
- : Duration -> Time_Of_Day
|
||||
- amount = if amount.is_date then Error.throw (Time.Time_Error "Time_Of_Day does not support date intervals") else Time_Of_Day (this . internal_local_time . minus [amount.interval_duration])
|
||||
|
||||
## Format this time of day using the default formatter.
|
||||
to_text : Text
|
||||
to_text = Time_Utils.default_time_of_day_formatter [] . format [this.internal_local_time]
|
||||
|
||||
## Format this time of day using formatter text.
|
||||
|
||||
Patterns are based on a simple sequence of letters and symbols. For
|
||||
example, "HH-mm-ss.SSS" will format "16:21:10" as "16-21-10.323".
|
||||
|
||||
For the list of accepted symbols in pattern refer to
|
||||
`Base.Time.Time.format` doc.
|
||||
|
||||
> Example
|
||||
Format "16:21:10" as "16:21:00.1234"
|
||||
Time_Of_Day.new 16 21 10 . format "HH:mm:ss.SSSS"
|
||||
|
||||
> Example
|
||||
Format "16:21:10" as "16:21:00.123456789"
|
||||
Time_Of_Day.new 16 21 10 . format "HH:mm:ss.n"
|
||||
|
||||
> Example
|
||||
Format "16:21:10" as "4:21pm"
|
||||
Time_Of_Day.new 16 21 10 . format "h:mma"
|
||||
|
||||
> Example
|
||||
Format "16:21:10" as "04:21:10pm"
|
||||
Time_Of_Day.new 16 21 10 . format "hh:mm:ssa"
|
||||
|
||||
> Example
|
||||
Format "16:21:10" as "hour:4"
|
||||
Time_Of_Day.new 16 21 10 . format "'hour:'h"
|
||||
format : Text -> Text
|
||||
format pattern = DateTimeFormatter.ofPattern [pattern] . format [this.internal_local_time]
|
||||
|
||||
## Obtains an instance of `Time_Of_Day` from a text such as "10:15".
|
||||
|
||||
The text must represent a valid time and is parsed using the ISO-8601
|
||||
extended local time format. The format consists of:
|
||||
|
||||
- Two digits for the hour-of-day. This is pre-padded by zero to ensure two
|
||||
digits.
|
||||
- A colon
|
||||
- Two digits for the minute-of-hour. This is pre-padded by zero to ensure two
|
||||
digits.
|
||||
- If the second-of-minute is not available then the format is complete.
|
||||
- A colon
|
||||
- Two digits for the second-of-minute. This is pre-padded by zero to ensure
|
||||
two digits.
|
||||
- If the nano-of-second is zero or not available then the format is complete.
|
||||
- A decimal point
|
||||
- One to nine digits for the nano-of-second. As many digits will be output as
|
||||
required.
|
||||
|
||||
> Example
|
||||
Get the time 15:05:30.
|
||||
Time_Of_Day.parse "15:05:30"
|
||||
|
||||
> Example
|
||||
Recover from the parse error.
|
||||
Time_Of_Day.parse "half past twelve" . catch <| case
|
||||
Time.Error _ -> Time_Of_Day.new
|
||||
parse : Text -> Time_Of_Day
|
||||
parse text =
|
||||
Panic.recover (Time_Of_Day (LocalTime.parse [text])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains an instance of Time_Of_Day from a text using custom format.
|
||||
|
||||
For the list of accepted symbols in pattern refer to
|
||||
`Base.Time.Time.format` doc.
|
||||
|
||||
> Example
|
||||
Parse "04:30:20" as Time_Of_Day.
|
||||
Date.parse_format "04:30:20" "HH:mm:ss"
|
||||
|
||||
> Example
|
||||
Parse "4:30AM" as Time_Of_Day
|
||||
Date.parse_format "4:30AM" "h:mma"
|
||||
parse_format : Text -> Text -> Time_Of_Day
|
||||
parse_format text pattern =
|
||||
format = DateTimeFormatter.ofPattern [pattern]
|
||||
Panic.recover (Time_Of_Day (LocalTime.parse [text, format])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
||||
|
||||
## Obtains the current time from the system clock in the default time-zone.
|
||||
now : Time_Of_Day
|
||||
now = Time_Of_Day (LocalTime.now [])
|
||||
|
||||
## Obtains an instance of `Time_Of_Day` from an hour, minute, second
|
||||
and nanosecond.
|
||||
|
||||
- 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
|
||||
|
||||
> Example
|
||||
Create a new local time at Unix epoch.
|
||||
Time_Of_Day.new
|
||||
|
||||
> Example
|
||||
Get the local time at 9:30.
|
||||
Time_Of_Day.new 9 30
|
||||
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day
|
||||
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
|
||||
Panic.recover (Time_Of_Day (LocalTime.of [hour, minute, second, nanosecond])) . catch <| case _ of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error (err.getMessage []))
|
||||
x -> x
|
63
distribution/std-lib/Base/src/Time/Zone.enso
Normal file
63
distribution/std-lib/Base/src/Time/Zone.enso
Normal file
@ -0,0 +1,63 @@
|
||||
from Base import all
|
||||
|
||||
polyglot java import java.time.ZoneId
|
||||
polyglot java import java.time.ZoneOffset
|
||||
|
||||
type Zone
|
||||
|
||||
## A type representing a time zone.
|
||||
|
||||
A time zone can be eiter offset-based like "-06:00" or id-based like
|
||||
"Europe/Paris".
|
||||
type Zone internal_zone_id
|
||||
|
||||
## Get the unique timezone ID.
|
||||
zone_id : Text
|
||||
zone_id = this.internal_zone_id . getId []
|
||||
|
||||
## This method parses the ID producing a `Zone`.
|
||||
|
||||
> Example
|
||||
Get Central European Time.
|
||||
Zone.parse "CET"
|
||||
|
||||
> Example
|
||||
Get Moscow time.
|
||||
Zone.parse "Europe/Moscow"
|
||||
|
||||
> Example
|
||||
Get time zone -06:00.
|
||||
Zone.parse "-06:00"
|
||||
|
||||
> Example
|
||||
Get custom offset +03:02:01 of 3 hours 2 minutes an 1 second.
|
||||
Zone.parse "+03:02:01"
|
||||
parse : Text -> Zone
|
||||
parse text = Zone (ZoneId.of [text])
|
||||
|
||||
## The system default timezone.
|
||||
system : Zone
|
||||
system = Zone (ZoneId.systemDefault [])
|
||||
|
||||
## The system default timezone.
|
||||
local : Zone
|
||||
local = here.system
|
||||
|
||||
## UTC time zone.
|
||||
utc : Zone
|
||||
utc = here.parse "UTC"
|
||||
|
||||
## Obtains an instance of `Zone` using an offset in hours, minutes and seconds.
|
||||
|
||||
- the timezone offset in hours, from -18 to +18
|
||||
- the timezone offset in minutes, from 0 to ±59, sign matches hours and
|
||||
seconds
|
||||
- the timezone offset in seconds, from 0 to ±59, sign matches hours and
|
||||
minutes
|
||||
|
||||
> Example
|
||||
Get time zone 1 hour 1 minute and 50 seconds of Greenwich/UTC.
|
||||
Zone.new 1 1 50
|
||||
new : Integer -> Integer -> Integer -> Zone
|
||||
new (hours = 0) (minutes = 0) (seconds = 0) =
|
||||
Zone (ZoneOffset.ofHoursMinutesSeconds [hours, minutes, seconds] . normalized [])
|
@ -59,12 +59,14 @@ case object BindingAnalysis extends IRPass {
|
||||
ref.typePointer match {
|
||||
case IR.Name.Qualified(List(), _, _, _) => Some(ref.methodName.name)
|
||||
case IR.Name.Qualified(List(n), _, _, _) =>
|
||||
if (n.name == moduleContext.module.getName.item)
|
||||
val shadowed = definedConstructors.exists(_.name == n.name)
|
||||
if (!shadowed && n.name == moduleContext.module.getName.item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
case IR.Name.Here(_, _, _) => Some(ref.methodName.name)
|
||||
case IR.Name.Literal(n, _, _, _, _) =>
|
||||
if (n == moduleContext.module.getName.item)
|
||||
val shadowed = definedConstructors.exists(_.name == n)
|
||||
if (!shadowed && n == moduleContext.module.getName.item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
case _ => None
|
||||
|
@ -46,8 +46,8 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Module binding resolution" should {
|
||||
"discover all atoms, methods, and polyglot symbols in a module" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|polyglot java import foo.bar.baz.MyClass
|
||||
@ -63,7 +63,6 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
|foo = 123
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"discover all atoms, methods, and polyglot symbols in a module" in {
|
||||
ir.getMetadata(BindingAnalysis) shouldEqual Some(
|
||||
BindingsMap(
|
||||
List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)),
|
||||
@ -73,5 +72,24 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"properly assign module-level methods when a type with the same name as module is defined" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
val moduleName = ctx.module.getName.item
|
||||
val ir =
|
||||
s"""
|
||||
|type $moduleName
|
||||
| type $moduleName
|
||||
| foo x = x + 1
|
||||
|
|
||||
|bar x = x + 1
|
||||
|
|
||||
|$moduleName.baz = 65
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
ir.getMetadata(BindingAnalysis).get.moduleMethods shouldEqual List(
|
||||
ModuleMethod("bar")
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
std-bits/src/main/java/org/enso/base/Time_Utils.java
Normal file
100
std-bits/src/main/java/org/enso/base/Time_Utils.java
Normal file
@ -0,0 +1,100 @@
|
||||
package org.enso.base;
|
||||
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
/** Utils for standard library operations on Time. */
|
||||
public class Time_Utils {
|
||||
|
||||
/**
|
||||
* The ISO-like date-time formatter that formats or parses a date-time with optional offset and
|
||||
* zone, such as '2011-12-03T10:15:30+01:00[Europe/Paris]'.
|
||||
*/
|
||||
public static final DateTimeFormatter TIME_FORMAT;
|
||||
|
||||
static {
|
||||
TIME_FORMAT =
|
||||
new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
.parseLenient()
|
||||
.optionalStart()
|
||||
.appendZoneOrOffsetId()
|
||||
.optionalEnd()
|
||||
.parseStrict()
|
||||
.optionalStart()
|
||||
.appendLiteral('[')
|
||||
.parseCaseSensitive()
|
||||
.appendZoneRegionId()
|
||||
.appendLiteral(']')
|
||||
.optionalEnd()
|
||||
.toFormatter();
|
||||
}
|
||||
|
||||
/** @return default Time formatter. */
|
||||
public static DateTimeFormatter default_time_formatter() {
|
||||
return DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
}
|
||||
|
||||
/** @return default Date formatter. */
|
||||
public static DateTimeFormatter default_date_formatter() {
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
}
|
||||
|
||||
/** @return default Time_Of_Day formatter. */
|
||||
public static DateTimeFormatter default_time_of_day_formatter() {
|
||||
return DateTimeFormatter.ISO_LOCAL_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an instance of 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.
|
||||
*/
|
||||
public static ZonedDateTime parse_time(String text) {
|
||||
TemporalAccessor time = TIME_FORMAT.parseBest(text, ZonedDateTime::from, LocalDateTime::from);
|
||||
if (time instanceof ZonedDateTime) {
|
||||
return (ZonedDateTime) time;
|
||||
} else if (time instanceof LocalDateTime) {
|
||||
return ((LocalDateTime) time).atZone(ZoneId.systemDefault());
|
||||
}
|
||||
throw new DateTimeException("Text '" + text + "' could not be parsed as Time.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an instance of ZonedDateTime from text using custom format string.
|
||||
*
|
||||
* <p>First tries to parse text as ZonedDateTime, then falls back to parsing LocalDateTime adding
|
||||
* system default timezone.
|
||||
*
|
||||
* @param text the string to parse.
|
||||
* @param pattern the format string.
|
||||
* @return parsed ZonedDateTime instance.
|
||||
*/
|
||||
public static ZonedDateTime parse_time_format(String text, String pattern) {
|
||||
TemporalAccessor time =
|
||||
DateTimeFormatter.ofPattern(pattern)
|
||||
.parseBest(text, ZonedDateTime::from, LocalDateTime::from);
|
||||
if (time instanceof ZonedDateTime) {
|
||||
return (ZonedDateTime) time;
|
||||
} else if (time instanceof LocalDateTime) {
|
||||
return ((LocalDateTime) time).atZone(ZoneId.systemDefault());
|
||||
}
|
||||
throw new DateTimeException("Text '" + text + "' could not be parsed as Time.");
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import Test.Process_Spec
|
||||
import Test.Vector.Spec as Vector_Spec
|
||||
import Test.Numbers.Spec as Numbers_Spec
|
||||
import Test.Text.Spec as Text_Spec
|
||||
import Test.Time.Spec as Time_Spec
|
||||
|
||||
main = Test.Suite.runMain <|
|
||||
List_Spec.spec
|
||||
@ -25,3 +26,4 @@ main = Test.Suite.runMain <|
|
||||
Vector_Spec.spec
|
||||
Numbers_Spec.spec
|
||||
Text_Spec.spec
|
||||
Time_Spec.spec
|
||||
|
94
test/Test/src/Time/Date_Spec.enso
Normal file
94
test/Test/src/Time/Date_Spec.enso
Normal file
@ -0,0 +1,94 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
import Base.Time.Date
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time
|
||||
import Base.Time.Time_Of_Day
|
||||
import Base.Time.Zone
|
||||
|
||||
spec =
|
||||
describe "Date" <|
|
||||
it "should create local date" <|
|
||||
date = Date.new 2020 1 1
|
||||
date . year . should_equal 2020
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 1
|
||||
it "should handle errors when creating local date" <|
|
||||
case Date.new 2020 30 30 . catch (x -> x) of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 30"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should format local date using provided pattern" <|
|
||||
text = Date.new 2020 12 21 . format "yyyyMMdd"
|
||||
text . should_equal "20201221"
|
||||
it "should format local date using default pattern" <|
|
||||
text = Date.new 2020 12 21 . to_text
|
||||
text . should_equal "2020-12-21"
|
||||
it "should parse default time format" <|
|
||||
text = Date.new 2020 12 21 . to_text
|
||||
date = Date.parse text
|
||||
date . year . should_equal 2020
|
||||
date . month . should_equal 12
|
||||
date . day . should_equal 21
|
||||
it "should throw error when parsing invalid date" <|
|
||||
case Date.parse "birthday" . catch (x -> x) of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text 'birthday' could not be parsed at index 0"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should parse local date" <|
|
||||
date = Date.parse "1999-01-01"
|
||||
date . year . should_equal 1999
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 1
|
||||
it "should parse custom format" <|
|
||||
date = Date.parse_format "1999 1 1" "yyyy M d"
|
||||
date . year . should_equal 1999
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 1
|
||||
it "should throw error when parsing custom format" <|
|
||||
date = Date.parse_format "1999-01-01" "yyyy M d"
|
||||
case date.catch (x -> x) of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '1999-01-01' could not be parsed at index 4"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should convert to time" <|
|
||||
time = Date.new 2000 12 21 . to_time (Time_Of_Day.new 12 30 45) Zone.utc
|
||||
time . year . should_equal 2000
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 21
|
||||
time . hour . should_equal 12
|
||||
time . minute . should_equal 30
|
||||
time . second . should_equal 45
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
it "should add date-based interval" <|
|
||||
date = Date.new 1970 + 1.day
|
||||
date . year . should_equal 1970
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 2
|
||||
it "should subtract date-based interval" <|
|
||||
date = Date.new 1970 - 1.year
|
||||
date . year . should_equal 1969
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 1
|
||||
it "should support mixed interval operators" <|
|
||||
date = Date.new 1970 + 1.month - 1.year
|
||||
date . year . should_equal 1969
|
||||
date . month . should_equal 2
|
||||
date . day . should_equal 1
|
||||
it "should throw error when adding time-based interval" <|
|
||||
case (Date.new 1970 + 1.hour) . catch (x -> x) of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Date does not support time intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should throw error when subtracting time-based interval" <|
|
||||
case (Date.new 1970 - (1.day - 1.minute)) . catch (x -> x) of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Date does not support time intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
49
test/Test/src/Time/Duration_Spec.enso
Normal file
49
test/Test/src/Time/Duration_Spec.enso
Normal file
@ -0,0 +1,49 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time
|
||||
|
||||
spec =
|
||||
describe "Duration" <|
|
||||
it "should create interval seconds" <|
|
||||
interval = 5.seconds
|
||||
interval.to_vector . should_equal [0, 0, 0, 0, 0, 5, 0]
|
||||
it "should create interval months" <|
|
||||
interval = 9.months
|
||||
interval.to_vector . should_equal [0, 9, 0, 0, 0, 0, 0]
|
||||
it "should add days to nanoseconds" <|
|
||||
interval = 7.nanoseconds + 3.days
|
||||
interval.to_vector . should_equal [0, 0, 3, 0, 0, 0, 7]
|
||||
it "should add milliseconds to years" <|
|
||||
interval = 4.years + 8.milliseconds
|
||||
interval.to_vector . should_equal [4, 0, 0, 0, 0, 0, 8000000]
|
||||
it "should substract seconds from months" <|
|
||||
interval = 8.months - 8.seconds
|
||||
interval.to_vector . should_equal [0, 8, 0, 0, 0, -8, 0]
|
||||
it "should subtract years from hours" <|
|
||||
interval = 2.hours - 11.years
|
||||
interval.to_vector . should_equal [-11, 0, 0, 2, 0, 0, 0]
|
||||
it "should support mixed operators" <|
|
||||
interval = 2.hours + 12.seconds - 11.years
|
||||
interval.to_vector . should_equal [-11, 0, 0, 2, 0, 12, 0]
|
||||
it "should create interval between two points in time" <|
|
||||
time1 = Time.new 2001 1 2
|
||||
time2 = Time.new 2001 2 1
|
||||
interval = Duration.between time1 time2
|
||||
interval.to_vector . should_equal [0, 0, 0, 720, 0, 0, 0]
|
||||
it "should check if time based" <|
|
||||
interval = 10.hours
|
||||
interval.is_date . should_be_false
|
||||
interval.is_time . should_be_true
|
||||
it "should check if date based" <|
|
||||
interval = 10.years
|
||||
interval.is_date . should_be_true
|
||||
interval.is_time . should_be_false
|
||||
it "should check if mixed based" <|
|
||||
interval = 10.years + 3.hours
|
||||
interval.is_date . should_be_true
|
||||
interval.is_time . should_be_true
|
||||
it "should check if empty" <|
|
||||
interval = 0.seconds
|
||||
interval.is_empty . should_be_true
|
15
test/Test/src/Time/Spec.enso
Normal file
15
test/Test/src/Time/Spec.enso
Normal file
@ -0,0 +1,15 @@
|
||||
from Base import all
|
||||
import Base.Test
|
||||
|
||||
import Test.Time.Duration_Spec
|
||||
import Test.Time.Time_Of_Day_Spec
|
||||
import Test.Time.Date_Spec
|
||||
import Test.Time.Time_Spec
|
||||
import Test.Time.Zone_Spec
|
||||
|
||||
spec =
|
||||
Zone_Spec.spec
|
||||
Time_Of_Day_Spec.spec
|
||||
Date_Spec.spec
|
||||
Time_Spec.spec
|
||||
Duration_Spec.spec
|
83
test/Test/src/Time/Time_Of_Day_Spec.enso
Normal file
83
test/Test/src/Time/Time_Of_Day_Spec.enso
Normal file
@ -0,0 +1,83 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
import Base.Time.Date
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Time
|
||||
import Base.Time.Time_Of_Day
|
||||
import Base.Time.Zone
|
||||
|
||||
spec =
|
||||
describe "Time_Of_Day" <|
|
||||
it "should create local time" <|
|
||||
time = Time_Of_Day.new 1 0 0
|
||||
time . hour . should_equal 1
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . to_seconds . should_equal 3600
|
||||
it "should handle erros when creating time" <|
|
||||
case Time_Of_Day.new 24 0 0 . catch (x -> x) 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)
|
||||
it "should format local time using provided pattern" <|
|
||||
text = Time_Of_Day.new 12 20 44 . format "HHmmss"
|
||||
text . should_equal "122044"
|
||||
it "should format local time using default pattern" <|
|
||||
text = Time_Of_Day.new 12 20 44 . to_text
|
||||
text . should_equal "12:20:44"
|
||||
it "should parse default time format" <|
|
||||
text = Time_Of_Day.new 12 20 44 . to_text
|
||||
time = Time_Of_Day.parse text
|
||||
time.to_seconds . should_equal 44444
|
||||
it "should parse local time" <|
|
||||
time = Time_Of_Day.parse "10:00:00"
|
||||
time.to_seconds . should_equal 36000
|
||||
it "should throw error when parsing invalid time" <|
|
||||
case Time_Of_Day.parse "1200" . catch (x -> x) 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)
|
||||
it "should parse custom format" <|
|
||||
time = Time_Of_Day.parse_format "12:30AM" "hh:mma"
|
||||
time.to_seconds . should_equal 1800
|
||||
it "should throw error when parsing custom format" <|
|
||||
time = Time_Of_Day.parse_format "12:30" "HH:mm:ss"
|
||||
case time.catch (x -> x) of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '12:30' could not be parsed at index 5"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "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
|
||||
it "should add time-based interval" <|
|
||||
time = Time_Of_Day.new + 1.minute
|
||||
time . to_seconds . should_equal 60
|
||||
it "should subtract time-based interval" <|
|
||||
time = Time_Of_Day.new - 1.minute
|
||||
time . to_seconds . should_equal 86340
|
||||
it "should support mixed interval operators" <|
|
||||
time = Time_Of_Day.new + 1.hour - 1.second
|
||||
time . to_seconds . should_equal 3599
|
||||
it "should throw error when adding date-based interval" <|
|
||||
case (Time_Of_Day.new + 1.day) . catch (x -> x) of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should throw error when subtracting date-based interval" <|
|
||||
case (Time_Of_Day.new - (1.day - 1.minute)) . catch (x -> x) of
|
||||
Time.Time_Error message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
242
test/Test/src/Time/Time_Spec.enso
Normal file
242
test/Test/src/Time/Time_Spec.enso
Normal file
@ -0,0 +1,242 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
import Base.Time.Time
|
||||
import Base.Time.Duration
|
||||
import Base.Time.Zone
|
||||
|
||||
spec =
|
||||
describe "Time" <|
|
||||
it "should create time" <|
|
||||
time = Time.new 1970 (zone = Zone.utc)
|
||||
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 0
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
it "should handle errors when creating time" <|
|
||||
case Time.new 1970 0 0 . catch (x -> x) 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)
|
||||
it "should format using provided pattern" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . format "yyyy-MM-dd'T'HH:mm:ss"
|
||||
text . should_equal "1970-01-01T00:00:00"
|
||||
it "should format using default pattern" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . to_text
|
||||
text . should_equal "1970-01-01T00:00:00Z[UTC]"
|
||||
it "should parse default time format" <|
|
||||
text = Time.new 1970 (zone = Zone.utc) . to_text
|
||||
time = Time.parse text
|
||||
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 0
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
it "should parse local time adding system zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01"
|
||||
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 0
|
||||
time . zone . zone_id . should_equal Zone.system.zone_id
|
||||
it "should parse time Z" <|
|
||||
time = Time.parse "1970-01-01T00:00:01Z"
|
||||
time . to_epoch_seconds . should_equal 1
|
||||
time . zone . zone_id . should_equal "Z"
|
||||
it "should parse time UTC" <|
|
||||
time = Time.parse "1970-01-01T00:00:01Z[UTC]"
|
||||
time . to_epoch_seconds . should_equal 1
|
||||
time . zone . zone_id . should_equal "UTC"
|
||||
it "should parse time with nanoseconds" <|
|
||||
time = Time.parse "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
|
||||
time . zone . zone_id . should_equal "Z"
|
||||
it "should parse time with offset-based zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00"
|
||||
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 0
|
||||
time . zone . zone_id . should_equal (Zone.new 1 . zone_id)
|
||||
it "should parse time with id-based zone" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00[Europe/Paris]"
|
||||
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 0
|
||||
time . zone . zone_id . should_equal "Europe/Paris"
|
||||
it "should throw error when parsing invalid time" <|
|
||||
case Time.parse "2008-1-1" . catch (x -> x) 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)
|
||||
it "should parse custom format of zoned time" <|
|
||||
time = Time.parse_format "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
|
||||
time . hour . should_equal 4
|
||||
time . minute . should_equal 30
|
||||
time . second . should_equal 20
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal "UTC"
|
||||
it "should parse custom format of local time" <|
|
||||
time = Time.parse_format "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
|
||||
time . hour . should_equal 4
|
||||
time . minute . should_equal 30
|
||||
time . second . should_equal 0
|
||||
time . nanosecond . should_equal 0
|
||||
it "should throw error when parsing custom format" <|
|
||||
time = Time.parse_format "2008-01-01" "yyyy-MM-dd'T'HH:mm:ss'['z']'"
|
||||
case time.catch (x -> x) of
|
||||
Time.Time_Error msg ->
|
||||
msg . should_equal "Text '2008-01-01' could not be parsed at index 10"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
it "should get epoch seconds" <|
|
||||
time = Time.new 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time . to_epoch_seconds . should_equal 8
|
||||
it "should get epoch millis" <|
|
||||
time = Time.new 1970 1 1 0 0 8 (zone = Zone.utc)
|
||||
time . to_epoch_milliseconds . should_equal 8000
|
||||
it "should set offset-based timezone" <|
|
||||
tz = Zone.new 1 1 1
|
||||
time = Time.new 1970 (zone = Zone.utc) . at_zone tz
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 1
|
||||
time . minute . should_equal 1
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal tz.zone_id
|
||||
it "should set id-based timezone" <|
|
||||
tz = Zone.parse "Europe/Moscow"
|
||||
time = Time.new 1970 (zone = Zone.utc) . at_zone tz
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 3
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal tz.zone_id
|
||||
it "should get time of day from offsed-based time" <|
|
||||
time = Time.parse "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
|
||||
it "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 . hour . should_equal 0
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 1
|
||||
time . nanosecond . should_equal 0
|
||||
it "should get date from offsed-based time" <|
|
||||
time = Time.parse "1970-01-01T00:00:01+01:00" . date
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
it "should get date from id-based time" <|
|
||||
time = Time.parse "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
|
||||
it "should add time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + 1.nanosecond
|
||||
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 0
|
||||
time . nanosecond . should_equal 1
|
||||
time . zone . zone_id . should_equal Zone.utc.zone_id
|
||||
it "should add date interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + 1.month
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 0
|
||||
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
|
||||
it "should add mixed date time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) + (1.month + 3.hours)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 3
|
||||
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
|
||||
it "should subtract time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.hour
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 31
|
||||
time . hour . should_equal 23
|
||||
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
|
||||
it "should subtract date interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.month
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 0
|
||||
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
|
||||
it "should subtract mixed date time interval" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - (1.month - 3.hours)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 3
|
||||
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
|
||||
it "should support mixed interval operators" <|
|
||||
time = Time.new 1970 (zone = Zone.utc) - 1.month + 12.hours
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 12
|
||||
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
|
24
test/Test/src/Time/Zone_Spec.enso
Normal file
24
test/Test/src/Time/Zone_Spec.enso
Normal file
@ -0,0 +1,24 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
import Base.Time.Zone
|
||||
|
||||
spec =
|
||||
describe "Zone" <|
|
||||
it "should get system zone id" <|
|
||||
Zone.system
|
||||
it "should parse UTC zone" <|
|
||||
zone = "UTC"
|
||||
id = Zone.parse zone
|
||||
id . zone_id . should_equal zone
|
||||
it "should parse id-based zone" <|
|
||||
zone = "Europe/Warsaw"
|
||||
id = Zone.parse zone
|
||||
id . zone_id . should_equal zone
|
||||
it "should parse offset-based zone" <|
|
||||
zone = "+01:02:03"
|
||||
id = Zone.parse zone
|
||||
id . zone_id . should_equal zone
|
||||
it "should get utc zone id" <|
|
||||
id = Zone.utc
|
||||
id . zone_id . should_equal "UTC"
|
Loading…
Reference in New Issue
Block a user