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:
Dmitry Bushev 2020-10-09 10:40:54 +03:00 committed by GitHub
parent 91346a41fc
commit 72bf87c648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1475 additions and 19 deletions

View 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

View 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])

View 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

View 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

View 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 [])

View File

@ -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

View File

@ -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")
)
}
}
}

View 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.");
}
}

View File

@ -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

View 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)

View 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

View 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

View 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)

View 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

View 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"