mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
Add Period
type (#3818)
This PR adds `Period` type, which is a date-only complement to `Duration` builtin type. # Important Notes - `Period` replaces `Date_Period`, and `Time_Period`. - Added shorthand constructors for `Duration` and `Period`. For example: `Period.days 10` instead of `Period.new days=10`. - `Period` can be compared to other `Period` in some cases, other cases throw an error.
This commit is contained in:
parent
c4a7e28fb5
commit
f8a4e2a9d2
@ -223,6 +223,7 @@
|
||||
selector variant.][3812]
|
||||
- [Implemented `Table.rows` giving access to a vector of rows.][3827]
|
||||
- [Define Enso epoch start as 15th October 1582][3804]
|
||||
- [Implemented `Period` type][3818]
|
||||
|
||||
[debug-shortcuts]:
|
||||
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
|
||||
@ -358,6 +359,7 @@
|
||||
[3827]: https://github.com/enso-org/enso/pull/3827
|
||||
[3824]: https://github.com/enso-org/enso/pull/3824
|
||||
[3804]: https://github.com/enso-org/enso/pull/3804
|
||||
[3818]: https://github.com/enso-org/enso/pull/3818
|
||||
|
||||
#### Enso Compiler
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Period
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
|
||||
import Standard.Base.Polyglot
|
||||
@ -9,6 +10,8 @@ from Standard.Base.Error.Common import Time_Error_Data, unimplemented
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
polyglot java import java.time.temporal.ChronoField
|
||||
polyglot java import java.time.temporal.IsoFields
|
||||
polyglot java import java.time.DateTimeException
|
||||
polyglot java import java.lang.ArithmeticException
|
||||
|
||||
## Obtains the current date from the system clock in the system timezone.
|
||||
|
||||
@ -139,9 +142,6 @@ parse text pattern=Nothing =
|
||||
|
||||
## This type represents a date, often viewed as year-month-day.
|
||||
|
||||
Arguments:
|
||||
- internal_local_date: The internal date representation.
|
||||
|
||||
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
|
||||
@ -272,7 +272,7 @@ type Date
|
||||
end-exclusive manner), by default the end date is not included in the
|
||||
count. This has the nice property that for example to count the work
|
||||
days within the next week you can do
|
||||
`date.work_days_until (date + 7.days)` and it will look at the 7 days
|
||||
`date.work_days_until (date + (Period.days 7)` and it will look at the 7 days
|
||||
starting from the current `date` and not 8 days. This also gives us a
|
||||
property that `date.work_days_until (date.add_work_days N) == N` for
|
||||
any non-negative N. On the other hand, sometimes we may want the end
|
||||
@ -290,7 +290,7 @@ type Date
|
||||
work_days_until : Date -> Vector Date -> Boolean -> Integer
|
||||
work_days_until self end holidays=[] include_end_date=False =
|
||||
Date_Time.ensure_in_epoch self <|
|
||||
if include_end_date then self.work_days_until (end + 1.day) holidays include_end_date=False else
|
||||
if include_end_date then self.work_days_until (end + (Period.days 1)) holidays include_end_date=False else
|
||||
weekdays = week_days_between self end
|
||||
## We count holidays that occurred within the period, but not on the
|
||||
weekends (as weekend days have already been excluded from the count).
|
||||
@ -313,7 +313,7 @@ type Date
|
||||
|
||||
from Standard.Base import Date, Time_Of_Day, Time_Zone
|
||||
|
||||
example_to_time = Date.new 2020 2 3 . to_time Time_Of_Day.new Time_Zone.utc
|
||||
example_to_time = Date.new 2020 2 3 . to_date_time Time_Of_Day.new Time_Zone.utc
|
||||
to_date_time : Time_Of_Day -> Time_Zone -> Date_Time
|
||||
to_date_time self (time_of_day=Time_Of_Day.new) (zone=Time_Zone.system) = self.to_time_builtin time_of_day zone
|
||||
|
||||
@ -327,9 +327,16 @@ type Date
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_add = Date.new 2020 + 6.months
|
||||
+ : Date_Period -> Date
|
||||
+ self _ = unimplemented "Date.+"
|
||||
example_add = Date.new 2020 + (Period.months 6)
|
||||
+ : Period -> Date ! (Time_Error | Illegal_Argument_Error)
|
||||
+ self period =
|
||||
case period of
|
||||
_ : Period.Period ->
|
||||
Time_Utils.date_adjust self Time_Utils.AdjustOp.PLUS period.internal_period
|
||||
_ : Duration.Duration ->
|
||||
Error.throw (Time_Error_Data "Date does not support adding/subtracting Duration. Use Period instead.")
|
||||
_ ->
|
||||
Error.throw (Illegal_Argument_Error_Data "Illegal period argument")
|
||||
|
||||
## Shift the date by the specified amount of business days.
|
||||
|
||||
@ -368,81 +375,90 @@ type Date
|
||||
add_work_days : Integer -> Vector Date -> Date
|
||||
add_work_days self days=1 holidays=[] =
|
||||
Date_Time.ensure_in_epoch self <|
|
||||
case days >= 0 of
|
||||
True ->
|
||||
full_weeks = days.div 5
|
||||
remaining_days = days % 5
|
||||
self.internal_add_work_days days holidays
|
||||
|
||||
# If the current day is a Saturday, the ordinal will be 6.
|
||||
ordinal = self.day_of_week.to_integer first_day=Day_Of_Week.Monday start_at_zero=False
|
||||
## PRIVATE
|
||||
|
||||
## If the current day is a Sunday, we just need to shift by one day
|
||||
to 'escape' the weekend, regardless of the overall remaining
|
||||
shift. On any other day, we check if current day plus the shift
|
||||
overlaps a weekend, we need the shift to be 2 days since we need
|
||||
to skip both Saturday and Sunday.
|
||||
additional_shift = if ordinal == 7 then 1 else
|
||||
if ordinal + remaining_days > 5 then 2 else 0
|
||||
Date_Time.ensure_in_epoch breaks tail call annotation and causes
|
||||
stack overflow. That is why `add_work_days` method is split into
|
||||
two methods.
|
||||
internal_add_work_days : Integer -> Vector Date -> Date
|
||||
internal_add_work_days self days=1 holidays=[] =
|
||||
case days >= 0 of
|
||||
True ->
|
||||
full_weeks = days.div 5
|
||||
remaining_days = days % 5
|
||||
|
||||
days_to_shift = full_weeks*7 + remaining_days + additional_shift
|
||||
end = self + days_to_shift.days
|
||||
# If the current day is a Saturday, the ordinal will be 6.
|
||||
ordinal = self.day_of_week.to_integer first_day=Day_Of_Week.Monday start_at_zero=False
|
||||
|
||||
## We have shifted the date so that weekends are taken into account,
|
||||
but other holidays may have happened during that shift period.
|
||||
Thus we may have shifted by less workdays than really desired. We
|
||||
compute the difference and if there are still remaining workdays
|
||||
to shift by, we re-run the whole shift procedure.
|
||||
workdays = self.work_days_until end holidays include_end_date=False
|
||||
diff = days - workdays
|
||||
if diff > 0 then @Tail_Call end.add_work_days diff holidays else
|
||||
## Otherwise we have accounted for all workdays we were asked
|
||||
to. But that is still not the end - we still need to ensure
|
||||
that the final day on which we have 'landed' is a workday
|
||||
too. Our procedure ensures that it is not a weekend, but it
|
||||
can still be a holiday. So we will be shifting the end date
|
||||
as long as needed to fall on a non-weekend non-holiday
|
||||
workday.
|
||||
go end_date =
|
||||
if holidays.contains end_date || is_weekend end_date then @Tail_Call go (end_date + 1.day) else end_date
|
||||
go end
|
||||
False ->
|
||||
## We shift a bit so that if shifting by N full weeks, the 'last'
|
||||
shift is done on `remaining_days` and not full weeks. That is
|
||||
because shifting a Saturday back 5 days does not want us to get
|
||||
to the earlier Saturday and fall back to the Friday before it,
|
||||
but we want to stop at the Monday just after that Saturday.
|
||||
full_weeks = (days + 1).div 5
|
||||
remaining_days = (days + 1) % 5 - 1
|
||||
## If the current day is a Sunday, we just need to shift by one day
|
||||
to 'escape' the weekend, regardless of the overall remaining
|
||||
shift. On any other day, we check if current day plus the shift
|
||||
overlaps a weekend, we need the shift to be 2 days since we need
|
||||
to skip both Saturday and Sunday.
|
||||
additional_shift = if ordinal == 7 then 1 else
|
||||
if ordinal + remaining_days > 5 then 2 else 0
|
||||
|
||||
# If the current day is a Sunday, the ordinal will be 1.
|
||||
ordinal = self.day_of_week.to_integer first_day=Day_Of_Week.Sunday start_at_zero=False
|
||||
days_to_shift = full_weeks*7 + remaining_days + additional_shift
|
||||
end = self + (Period.new days=days_to_shift)
|
||||
|
||||
## If we overlapped the weekend, we need to increase the shift by
|
||||
one day (our current shift already shifts us by one day, but we
|
||||
need one more to skip the whole two-day weekend).
|
||||
additional_shift = if ordinal == 1 then -1 else
|
||||
if ordinal + remaining_days <= 1 then -2 else 0
|
||||
## We have shifted the date so that weekends are taken into account,
|
||||
but other holidays may have happened during that shift period.
|
||||
Thus we may have shifted by less workdays than really desired. We
|
||||
compute the difference and if there are still remaining workdays
|
||||
to shift by, we re-run the whole shift procedure.
|
||||
workdays = self.work_days_until end holidays include_end_date=False
|
||||
diff = days - workdays
|
||||
if diff > 0 then @Tail_Call end.internal_add_work_days diff holidays else
|
||||
## Otherwise we have accounted for all workdays we were asked
|
||||
to. But that is still not the end - we still need to ensure
|
||||
that the final day on which we have 'landed' is a workday
|
||||
too. Our procedure ensures that it is not a weekend, but it
|
||||
can still be a holiday. So we will be shifting the end date
|
||||
as long as needed to fall on a non-weekend non-holiday
|
||||
workday.
|
||||
go end_date =
|
||||
if holidays.contains end_date || is_weekend end_date then @Tail_Call go (end_date + (Period.days 1)) else end_date
|
||||
go end
|
||||
False ->
|
||||
## We shift a bit so that if shifting by N full weeks, the 'last'
|
||||
shift is done on `remaining_days` and not full weeks. That is
|
||||
because shifting a Saturday back 5 days does not want us to get
|
||||
to the earlier Saturday and fall back to the Friday before it,
|
||||
but we want to stop at the Monday just after that Saturday.
|
||||
full_weeks = (days + 1).div 5
|
||||
remaining_days = (days + 1) % 5 - 1
|
||||
|
||||
## The rest of the logic is analogous to the positive case, we
|
||||
just need to correctly handle the reverse order of dates. The
|
||||
`days_to_shift` will be negative so `end` will come _before_
|
||||
`self`.
|
||||
days_to_shift = full_weeks*7 + remaining_days + additional_shift
|
||||
end = self + days_to_shift.days
|
||||
workdays = end.work_days_until self holidays include_end_date=False
|
||||
# If the current day is a Sunday, the ordinal will be 1.
|
||||
ordinal = self.day_of_week.to_integer first_day=Day_Of_Week.Sunday start_at_zero=False
|
||||
|
||||
## `days` is negative but `workdays` is positive, `diff` will be
|
||||
zero if we accounted for all days or negative if there are
|
||||
still workdays we need to shift by - then it will be exactly
|
||||
the remaining offset that we need to shift by.
|
||||
diff = days + workdays
|
||||
if diff < 0 then @Tail_Call end.add_work_days diff holidays else
|
||||
## As in the positive case, if the final end date falls on a
|
||||
holiday, we need to ensure that we move it - this time
|
||||
backwards - to the first workday.
|
||||
go end_date =
|
||||
if holidays.contains end_date || is_weekend end_date then @Tail_Call go (end_date - 1.day) else end_date
|
||||
go end
|
||||
## If we overlapped the weekend, we need to increase the shift by
|
||||
one day (our current shift already shifts us by one day, but we
|
||||
need one more to skip the whole two-day weekend).
|
||||
additional_shift = if ordinal == 1 then -1 else
|
||||
if ordinal + remaining_days <= 1 then -2 else 0
|
||||
|
||||
## The rest of the logic is analogous to the positive case, we
|
||||
just need to correctly handle the reverse order of dates. The
|
||||
`days_to_shift` will be negative so `end` will come _before_
|
||||
`self`.
|
||||
days_to_shift = full_weeks*7 + remaining_days + additional_shift
|
||||
end = self + (Period.new days=days_to_shift)
|
||||
workdays = end.work_days_until self holidays include_end_date=False
|
||||
|
||||
## `days` is negative but `workdays` is positive, `diff` will be
|
||||
zero if we accounted for all days or negative if there are
|
||||
still workdays we need to shift by - then it will be exactly
|
||||
the remaining offset that we need to shift by.
|
||||
diff = days + workdays
|
||||
if diff < 0 then @Tail_Call end.internal_add_work_days diff holidays else
|
||||
## As in the positive case, if the final end date falls on a
|
||||
holiday, we need to ensure that we move it - this time
|
||||
backwards - to the first workday.
|
||||
go end_date =
|
||||
if holidays.contains end_date || is_weekend end_date then @Tail_Call go (end_date - (Period.days 1)) else end_date
|
||||
go end
|
||||
|
||||
## Subtract the specified amount of time from this instant to get another
|
||||
date.
|
||||
@ -456,9 +472,17 @@ type Date
|
||||
from Standard.Base import Date
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_subtract = Date.new 2020 - 7.days
|
||||
- : Date_Period -> Date
|
||||
- self _ = unimplemented "Date.-"
|
||||
example_subtract = Date.new 2020 - (Period.days 7)
|
||||
- : Period -> Date ! (Time_Error | Illegal_Argument_Error)
|
||||
- self period =
|
||||
case period of
|
||||
_ : Period.Period ->
|
||||
new_java_date = Time_Utils.date_adjust self Time_Utils.AdjustOp.MINUS period.internal_period
|
||||
new new_java_date.year new_java_date.month new_java_date.day
|
||||
_ : Duration.Duration ->
|
||||
Error.throw (Time_Error_Data "Date does not support adding/subtracting Duration. Use Period instead.")
|
||||
_ ->
|
||||
Error.throw (Illegal_Argument_Error_Data "Illegal period argument")
|
||||
|
||||
|
||||
## A Date to Json conversion.
|
||||
@ -538,7 +562,7 @@ week_days_between start end =
|
||||
starting point), the last week (containing the end point), and the full
|
||||
weeks in between those. In some cases there may be no weeks in-between
|
||||
and the first and last week can be the same week.
|
||||
start_of_first_full_week = (start.start_of Date_Period.Week) + 7.days
|
||||
start_of_first_full_week = (start.start_of Date_Period.Week) + (Period.days 7)
|
||||
start_of_last_week = end.start_of Date_Period.Week
|
||||
full_weeks_between = (Time_Utils.days_between start_of_first_full_week start_of_last_week).div 7
|
||||
case full_weeks_between < 0 of
|
||||
@ -554,7 +578,7 @@ week_days_between start end =
|
||||
_ -> days_between
|
||||
False ->
|
||||
# We count the days in the first week up until Friday - the weekend is not counted.
|
||||
first_week_days = Math.max 0 (Time_Utils.days_between start (start_of_first_full_week - 2.days))
|
||||
first_week_days = Math.max 0 (Time_Utils.days_between start (start_of_first_full_week - (Period.days 2)))
|
||||
# We count the days in the last week, not including the weekend.
|
||||
last_week_days = Math.min (Time_Utils.days_between start_of_last_week end) 5
|
||||
full_weeks_between * 5 + first_week_days + last_week_days
|
||||
|
@ -1,13 +1,12 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
import Standard.Base.Data.Time.Time_Period
|
||||
from Standard.Base.Data.Time import Duration, Period, Date_Period, Time_Period
|
||||
from Standard.Base.Error.Common import Time_Error
|
||||
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
polyglot java import java.time.temporal.ChronoField
|
||||
polyglot java import java.time.temporal.IsoFields
|
||||
polyglot java import java.time.ZonedDateTime
|
||||
polyglot java import java.lang.ArithmeticException
|
||||
|
||||
polyglot java import org.enso.base.Time_Utils
|
||||
@ -442,18 +441,24 @@ type Date_Time
|
||||
## Add the specified amount of time to this instant to produce a new instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to add to this instant.
|
||||
- amount: The amount of time to add to this instant, either `Duration` for
|
||||
adding time (hours, minutes, etc.), or `Period` for adding date (days,
|
||||
months, years).
|
||||
|
||||
> Example
|
||||
Add 15 years and 3 hours to a zoned date time.
|
||||
|
||||
from Standard.Base import Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
from Standard.Base.Data.Time import Duration, Period
|
||||
|
||||
example_plus = Date_Time.new 2020 + 15.years + (Duration.new hours=3)
|
||||
+ : Duration -> Date_Time ! Time_Error
|
||||
example_plus = Date_Time.new 2020 + (Period.years 15) + (Duration.hours 3)
|
||||
+ : (Duration | Period) -> Date_Time ! Time_Error
|
||||
+ self amount =
|
||||
Panic.catch ArithmeticException (self.plus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage))
|
||||
case amount of
|
||||
duration : Duration.Duration ->
|
||||
Panic.catch ArithmeticException (self.plus_builtin duration) (err -> Error.throw (Time_Error_Data err.getMessage))
|
||||
period : Period.Period ->
|
||||
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.PLUS period.internal_period
|
||||
|
||||
## Shift the date by the specified amount of business days.
|
||||
|
||||
@ -502,18 +507,24 @@ type Date_Time
|
||||
Produces a warning if the resulting date time is before an Enso epoch.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to subtract from this instant.
|
||||
- amount: The amount of time to subtract from this instant. Can be either
|
||||
`Duration` for subtracting time (hours, minutes, etc.), or `Period`
|
||||
for subtracting days, months and years.
|
||||
|
||||
> Example
|
||||
Subtract 1 year and 9 months from a zoned date time.
|
||||
Subtract 1 year, 9 months and 12 hours from a zoned date time.
|
||||
|
||||
from Standard.Base import Date_Time
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_minus = Date_Time.new 2020 - 1.year - 9.months
|
||||
- : Duration -> Date_Time ! Time_Error
|
||||
example_minus = Date_Time.new 2020 - (Period.years 1) - (Period.months 9) - (Duration.hours 5)
|
||||
- : (Duration | Period) -> Date_Time ! Time_Error
|
||||
- self amount =
|
||||
result = Panic.catch ArithmeticException (self.minus_builtin amount) (err -> Error.throw (Time_Error_Data err.getMessage))
|
||||
result = case amount of
|
||||
duration : Duration.Duration ->
|
||||
Panic.catch ArithmeticException (self.minus_builtin duration) (err -> Error.throw (Time_Error_Data err.getMessage))
|
||||
period : Period.Period ->
|
||||
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS period.internal_period
|
||||
ensure_in_epoch result result
|
||||
|
||||
## Convert this time to text using the default formatter.
|
||||
|
@ -1,5 +1,6 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.System
|
||||
from Standard.Base.Data.Time import Period
|
||||
|
||||
polyglot java import java.time.Duration as Java_Duration
|
||||
polyglot java import java.time.Period as Java_Period
|
||||
@ -9,8 +10,8 @@ polyglot java import java.lang.ArithmeticException
|
||||
## Create an interval representing the duration between two points in time.
|
||||
|
||||
Arguments:
|
||||
- start_inclusive: The start datetime of the duration.
|
||||
- end_inclusive: The end datetime of the duration.
|
||||
- start_inclusive: The start datetime of the duration, included.
|
||||
- end_exclusive: The end datetime of the duration, excluded.
|
||||
- timezone_aware: Whether the duration between two given times should be
|
||||
aware of the timezone, that can be set for start or end times.
|
||||
|
||||
@ -44,6 +45,25 @@ new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Duration
|
||||
new hours=0 minutes=0 seconds=0 milliseconds=0 nanoseconds=0 =
|
||||
Duration.new_builtin hours minutes seconds milliseconds nanoseconds
|
||||
|
||||
## Create a Duration from hours.
|
||||
hours : Integer -> Duration
|
||||
hours h = new hours=h
|
||||
|
||||
## Create a Duration from minutes.
|
||||
minutes : Integer -> Duration
|
||||
minutes m = new minutes=m
|
||||
|
||||
## Create a Duration from seconds.
|
||||
seconds : Integer -> Duration
|
||||
seconds s = new seconds=s
|
||||
|
||||
## Create a Duration from milliseconds.
|
||||
milliseconds : Integer -> Duration
|
||||
milliseconds ms = new milliseconds=ms
|
||||
|
||||
## Create a Duration from nanoseconds.
|
||||
nanoseconds : Integer -> Duration
|
||||
nanoseconds ns = new nanoseconds=ns
|
||||
|
||||
## Create a zero (empty) duration.
|
||||
> Example
|
||||
@ -51,7 +71,7 @@ new hours=0 minutes=0 seconds=0 milliseconds=0 nanoseconds=0 =
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
durations = [(Duration.new seconds=1), (Duration.new seconds=2), (Duration.new seconds=3)]
|
||||
durations = [(Duration.seconds 1), (Duration.seconds 2), (Duration.seconds 3)]
|
||||
example_sum = durations.fold Duration.zero (+)
|
||||
zero : Duration
|
||||
zero = new
|
||||
@ -70,6 +90,15 @@ time_execution ~function =
|
||||
duration = new nanoseconds=(end - start)
|
||||
Pair_Data duration result
|
||||
|
||||
## PRIVATE
|
||||
ensure_duration : Any -> Suspend (Any -> Any) -> Any ! (Time_Error | Illegal_Argument_Error)
|
||||
ensure_duration object ~action =
|
||||
case object of
|
||||
_ : Duration -> action
|
||||
_ : Period.Period -> Error.throw (Time_Error_Data "Cannot use Period as a parameter")
|
||||
x ->
|
||||
Error.throw Illegal_Argument_Error_Data <|
|
||||
"Expected Duration type, got: " + (Meta.get_qualified_type_name x)
|
||||
|
||||
@Builtin_Type
|
||||
type Duration
|
||||
@ -84,39 +113,36 @@ type Duration
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_add = (Duration.new minutes=3) + (Duration.new seconds=6)
|
||||
example_add = (Duration.minutes 3) + (Duration.seconds 6)
|
||||
|
||||
> Example
|
||||
Add 12 hours to a duration of a month.
|
||||
Add 12 hours to a duration of 30 minutes.
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_add = 1.month + (Duration.new hours=12)
|
||||
example_add = (Duration.minutes 30) + (Duration.hours 12)
|
||||
+ : Duration -> Duration ! Time_Error
|
||||
+ self that =
|
||||
Panic.catch ArithmeticException (self.plus_builtin that) (err-> Error.throw (Time_Error_Data err.getMessage))
|
||||
ensure_duration that <|
|
||||
Panic.catch ArithmeticException (self.plus_builtin that) err->
|
||||
Error.throw (Time_Error_Data err.getMessage)
|
||||
|
||||
## Subtract the specified amount of time from this duration.
|
||||
|
||||
Arguments:
|
||||
- that: The duration to subtract from `self`.
|
||||
|
||||
> Example
|
||||
Subtract 11 months from a duration of 3 years
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_subtract = 3.years - 11.months
|
||||
|
||||
> Example
|
||||
Substract 30 minutes from a duration of 6 hours.
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_subtract = (Duration.new hours=6) - (Duration.new minutes=30)
|
||||
example_subtract = (Duration.hours 6) - (Duration.minutes 30)
|
||||
- : Duration -> Duration ! Time_Error
|
||||
- self that =
|
||||
Panic.catch ArithmeticException (self.minus_builtin that) (err -> Error.throw (Time_Error_Data err.getMessage))
|
||||
ensure_duration that <|
|
||||
Panic.catch ArithmeticException (self.minus_builtin that) err->
|
||||
Error.throw (Time_Error_Data err.getMessage)
|
||||
|
||||
## Check two durations for equality.
|
||||
|
||||
@ -128,9 +154,12 @@ type Duration
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_eq = (Duration.new seconds=60).total_minutes == (Duration.new minutes=1).total_minutes
|
||||
example_eq = (Duration.seconds 60).total_minutes == (Duration.minutes 1).total_minutes
|
||||
== : Duration -> Boolean
|
||||
== self that = self.equals_builtin that
|
||||
== self that =
|
||||
case that of
|
||||
_ : Duration -> self.equals_builtin that
|
||||
_ -> False
|
||||
|
||||
## Compares `self` to `that` to produce an ordering.
|
||||
|
||||
@ -144,10 +173,12 @@ type Duration
|
||||
|
||||
example_compare_to =
|
||||
duration_1 = (Duration.new hour=1)
|
||||
duration_2 = (Duration.new minutes=60) + (Duration.new minutes=5)
|
||||
duration_2 = (Duration.minutes 60) + (Duration.minutes 5)
|
||||
duration_1.compare_to duration_2
|
||||
compare_to : Duration -> Ordering
|
||||
compare_to self that = Ordering.from_sign (self.compare_to_builtin that)
|
||||
compare_to self that =
|
||||
ensure_duration that <|
|
||||
Ordering.from_sign (self.compare_to_builtin that)
|
||||
|
||||
## Get the portion of the duration expressed in nanoseconds.
|
||||
|
||||
@ -243,7 +274,7 @@ type Duration
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_to_vec = (Duration.new nanoseconds=800)).to_vector
|
||||
example_to_vec = (Duration.nanoseconds 800)).to_vector
|
||||
to_vector : Vector.Vector Integer
|
||||
to_vector self = [self.hours, self.minutes, self.seconds, self.milliseconds, self.nanoseconds]
|
||||
|
||||
@ -254,7 +285,7 @@ type Duration
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_to_json = (Duration.new seconds=10).to_json
|
||||
example_to_json = (Duration.seconds 10).to_json
|
||||
to_json : Json.Object
|
||||
to_json self =
|
||||
b = Vector.new_builder
|
||||
|
@ -0,0 +1,155 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
polyglot java import java.time.Period as Java_Period
|
||||
polyglot java import java.time.DateTimeException
|
||||
polyglot java import java.lang.ArithmeticException
|
||||
|
||||
|
||||
## Create a Period representing the time interval between two dates.
|
||||
|
||||
Arguments:
|
||||
- start_date_inclusive: The start date of the period, included.
|
||||
- end_date_exclusive: The end date of the period, excluded.
|
||||
|
||||
> Example
|
||||
Get a Period between 2022-10-21 and 2022-09-12
|
||||
|
||||
import Standard.Base.Data.Time.Period
|
||||
|
||||
example_period = Period.between (Date.new 2022 10 21) (Date.new 2022 9 12)
|
||||
between : Date -> Date -> Period
|
||||
between start_date_inclusive end_date_exclusive =
|
||||
Period.Period_Data (Java_Period.between start_date_inclusive end_date_exclusive)
|
||||
|
||||
## Create a new Period from years, months and days.
|
||||
|
||||
Arguments:
|
||||
- years: Amount of years.
|
||||
- months: Amount of months.
|
||||
- days: Amount of days.
|
||||
|
||||
> Example
|
||||
Create a Period of 2 years and 5 days
|
||||
|
||||
import Standard.Base.Data.Time.Period
|
||||
|
||||
example_period = Period.new 2 0 5
|
||||
new : Integer -> Integer -> Integer -> Period
|
||||
new years=0 months=0 days=0 =
|
||||
Period.Period_Data (Java_Period.of years months days)
|
||||
|
||||
## Create a new Period from days.
|
||||
days : Integer -> Period
|
||||
days d = new days=d
|
||||
|
||||
## Create a new Period from months.
|
||||
months : Integer -> Period
|
||||
months m = new months=m
|
||||
|
||||
## Create a new Period from years.
|
||||
years : Integer -> Period
|
||||
years y = new years=y
|
||||
|
||||
## PRIVATE
|
||||
ensure_period : Any -> Suspend (Any -> Any) -> Text -> Any ! (Time_Error | Illegal_Argument_Error)
|
||||
ensure_period object ~action error_msg="Cannot use Duration as a parameter" =
|
||||
case object of
|
||||
_ : Period -> action
|
||||
_ : Duration.Duration ->
|
||||
Error.throw (Time_Error_Data error_msg)
|
||||
x ->
|
||||
Error.throw Illegal_Argument_Error_Data <|
|
||||
"Expected Period type, got: " + (Meta.get_qualified_type_name x)
|
||||
|
||||
## A date-based amount of time in the ISO-8601 calendar system, such as
|
||||
'2 years, 3 months and 4 days'.
|
||||
|
||||
This type models an amount of time in terms of years, months and days.
|
||||
`Duration` is its time-based equivalent. Moreover, `Period` counts with
|
||||
daylight saving time. This means that a Period of 1 day does not necessarily
|
||||
have to be 24 hours of Duration.
|
||||
type Period
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Arguments:
|
||||
- internal_period: An internal representation of period of type
|
||||
java.time.Period.
|
||||
Period_Data internal_period
|
||||
|
||||
## Get the portion of the period expressed in years.
|
||||
years : Integer
|
||||
years self = self.internal_period.getYears
|
||||
|
||||
## Get the portion of the period expressed in months.
|
||||
months : Integer
|
||||
months self = self.internal_period.getMonths
|
||||
|
||||
## Get the portion of the period expressed in days.
|
||||
days : Integer
|
||||
days self = self.internal_period.getDays
|
||||
|
||||
## Add the specified amount of time to this period.
|
||||
|
||||
Arguments:
|
||||
- other_period: The period to add to `self`. Note that this cannot be a
|
||||
`Duration`, neither `Date_Time`.
|
||||
|
||||
> Example
|
||||
Add 1 day to 1 month.
|
||||
|
||||
import Standard.Base.Data.Time.Period
|
||||
|
||||
example_add = (Period.months 1) + (Period.days 1)
|
||||
+ : Period -> Period ! (Time_Error | Illegal_Argument_Error)
|
||||
+ self other_period =
|
||||
ensure_period other_period <|
|
||||
Panic.catch Any (Period.Period_Data (self.internal_period.plus other_period.internal_period)) err->
|
||||
case err of
|
||||
_ : DateTimeException -> Error.throw Time_Error_Data "Period addition failed:"+err.getMessage
|
||||
_ : ArithmeticException -> Error.throw Illegal_Argument_Error_Data "Arithmetic error:"+err.getMessage cause=err
|
||||
|
||||
## Subtract a specified amount of time from this period.
|
||||
|
||||
Arguments:
|
||||
- other_period: Other Period to add to this Period. Note that this
|
||||
cannot be a `Duration`, neither `Date_Time`.
|
||||
|
||||
> Example
|
||||
Subtract 11 months from a period of 3 years
|
||||
|
||||
import Standard.Base.Data.Time.Period
|
||||
|
||||
example_subtract = (Period.years 3) - (Period.months 11)
|
||||
|
||||
- : Period -> Period ! (Time_Error | Illegal_Argument_Error)
|
||||
- self other_period =
|
||||
ensure_period other_period <|
|
||||
Panic.catch Any (Period.Period_Data (self.internal_period.minus other_period.internal_period)) err->
|
||||
case err of
|
||||
DateTimeException -> Error.throw Time_Error_Data "Period subtraction failed"
|
||||
ArithmeticException -> Error.throw Illegal_Argument_Error_Data "Arithmetic error"
|
||||
|
||||
## Check two periods for equality.
|
||||
|
||||
Note that two periods are equal if they have the exact same amount of
|
||||
years, months, and days. So `(Period.days 30)` and
|
||||
`(Period.months 1)` are not equal. Even `(Period.years 1)` and
|
||||
`(Period.months 12)` are not equal.
|
||||
|
||||
Arguments:
|
||||
- other_period: The period to compare against `self`.
|
||||
== : Period -> Boolean
|
||||
== self that =
|
||||
ensure_period that <|
|
||||
self.internal_period.equals that.internal_period
|
||||
|
||||
## Just throws Incomparable_Values_Error, because periods cannot be
|
||||
compared without additional context.
|
||||
|
||||
To compare two Periods, use something like:
|
||||
`(start_date + period1) .compare_to (start_date + period2)`
|
||||
compare_to : Period -> Nothing ! Incomparable_Values_Error
|
||||
compare_to self _ = Error.throw Incomparable_Values_Error
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Period
|
||||
import Standard.Base.Data.Time.Time_Period
|
||||
from Standard.Base.Error.Common import Time_Error
|
||||
|
||||
@ -208,16 +209,20 @@ type Time_Of_Day
|
||||
## Add the specified amount of time to this instant to get a new instant.
|
||||
|
||||
Arguments:
|
||||
- amount: The amount of time to add to this instant.
|
||||
- amount: The amount of time to add to this instant. Can be only
|
||||
`Duration`.
|
||||
|
||||
> Example
|
||||
|
||||
from Standard.Base import Time_Of_Day
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_plus = Time_Of_Day.new + (Duration.new seconds=3)
|
||||
+ : Duration -> Time_Of_Day
|
||||
+ self amount = self.plus_builtin amount
|
||||
example_plus = Time_Of_Day.new + (Duration.seconds 3)
|
||||
+ : Duration -> Time_Of_Day ! Time_Error
|
||||
+ self amount =
|
||||
case amount of
|
||||
duration : Duration.Duration -> self.plus_builtin duration
|
||||
_ : Period.Period -> Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals (periods)")
|
||||
|
||||
## Subtract the specified amount of time from this instant to get a new
|
||||
instant.
|
||||
@ -231,9 +236,12 @@ type Time_Of_Day
|
||||
from Standard.Base import Time_Of_Day
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
example_minus = Time_Of_Day.now - (Duration.new hours=12)
|
||||
- : Duration -> Time_Of_Day
|
||||
- self amount = self.minus_builtin amount
|
||||
example_minus = Time_Of_Day.now - (Duration.hours 12)
|
||||
- : Duration -> Time_Of_Day ! Time_Error
|
||||
- self amount =
|
||||
case amount of
|
||||
duration : Duration.Duration -> self.minus_builtin duration
|
||||
_ : Period.Period -> Error.throw (Time_Error_Data "Time_Of_Day does not support date intervals (periods)")
|
||||
|
||||
## Format this time of day as text using the default formatter.
|
||||
|
||||
|
@ -31,6 +31,7 @@ import project.Data.Text.Text_Ordering
|
||||
import project.Data.Text.Span
|
||||
import project.Data.Time.Date
|
||||
import project.Data.Time.Date_Time
|
||||
import project.Data.Time.Duration
|
||||
import project.Data.Time.Time_Of_Day
|
||||
import project.Data.Time.Time_Zone
|
||||
import project.Data.Time.Day_Of_Week.Day_Of_Week
|
||||
@ -76,6 +77,7 @@ export project.Data.Text.Text_Matcher
|
||||
export project.Data.Text.Regex_Matcher
|
||||
export project.Data.Time.Date
|
||||
export project.Data.Time.Date_Time
|
||||
export project.Data.Time.Duration
|
||||
export project.Data.Time.Time_Of_Day
|
||||
export project.Data.Time.Time_Zone
|
||||
export project.Data.Time.Day_Of_Week.Day_Of_Week
|
||||
|
@ -327,6 +327,7 @@ is_a value typ = if is_same_object value typ then True else
|
||||
_ : Decimal -> typ == Decimal
|
||||
_ : Date_Time.Date_Time -> typ.is_same_object_as Date_Time.Date_Time
|
||||
_ : Date.Date -> typ.is_same_object_as Date.Date
|
||||
_ : Duration.Duration -> typ.is_same_object_as Duration.Duration
|
||||
_ : Time_Of_Day.Time_Of_Day -> typ.is_same_object_as Time_Of_Day.Time_Of_Day
|
||||
_ : Time_Zone.Time_Zone -> typ.is_same_object_as Time_Zone.Time_Zone
|
||||
Base.Polyglot ->
|
||||
|
@ -36,7 +36,7 @@ polyglot java import org.enso.base.Http_Utils
|
||||
|
||||
> Example
|
||||
Create an HTTP client with extended timeout.
|
||||
Http.new timeout=(Duration.new seconds=30)
|
||||
Http.new timeout=(Duration.seconds 30)
|
||||
|
||||
> Example
|
||||
Create an HTTP client with extended timeout and proxy settings.
|
||||
@ -46,9 +46,9 @@ polyglot java import org.enso.base.Http_Utils
|
||||
import Standard.Base.Network.Proxy
|
||||
|
||||
example_new =
|
||||
Http.new (timeout = (Duration.new seconds=30)) (proxy = Proxy.new "example.com" 8080)
|
||||
Http.new (timeout = (Duration.seconds 30)) (proxy = Proxy.new "example.com" 8080)
|
||||
new : Duration -> Boolean -> Proxy -> Http
|
||||
new (timeout = (Duration.new seconds=10)) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) =
|
||||
new (timeout = (Duration.seconds 10)) (follow_redirects = True) (proxy = Proxy.System) (version = Version.Http_1_1) =
|
||||
Http_Data timeout follow_redirects proxy version
|
||||
|
||||
## Send an Options request.
|
||||
@ -595,7 +595,7 @@ type Http
|
||||
example_request =
|
||||
form = [Form.text_field "name" "John Doe"]
|
||||
req = Request.new Method.Post "http://httpbin.org/post" . with_form form
|
||||
http = Http.new (timeout = (Duration.new seconds=30))
|
||||
http = Http.new (timeout = (Duration.seconds 30))
|
||||
http.request req
|
||||
request : Request -> Response ! Request_Error
|
||||
request self req =
|
||||
|
@ -86,19 +86,25 @@ public abstract class TypeOfNode extends Node {
|
||||
guards = {"interop.isTimeZone(value)", "!interop.isDate(value)", "!interop.isTime(value)"})
|
||||
Object doTimeZone(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
Context ctx = Context.get(this);
|
||||
return Context.get(this).getBuiltins().timeZone();
|
||||
return ctx.getBuiltins().timeZone();
|
||||
}
|
||||
|
||||
@Specialization(guards = {"interop.isDate(value)", "!interop.isTime(value)"})
|
||||
Object doDate(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
Context ctx = Context.get(this);
|
||||
return Context.get(this).getBuiltins().date();
|
||||
return ctx.getBuiltins().date();
|
||||
}
|
||||
|
||||
@Specialization(guards = {"interop.isTime(value)", "!interop.isDate(value)"})
|
||||
Object doTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
Context ctx = Context.get(this);
|
||||
return Context.get(this).getBuiltins().timeOfDay();
|
||||
return ctx.getBuiltins().timeOfDay();
|
||||
}
|
||||
|
||||
@Specialization(guards = "interop.isDuration(value)")
|
||||
Object doDuration(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
|
||||
Context ctx = Context.get(this);
|
||||
return ctx.getBuiltins().duration();
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
|
@ -215,6 +215,7 @@ public final class ParseStdLibTest extends TestCase {
|
||||
"Data/Time/Date_Period.enso",
|
||||
"Data/Time/Date_Time.enso",
|
||||
"Data/Time/Duration.enso",
|
||||
"Data/Time/Period.enso",
|
||||
"Data/Time/Time_Of_Day.enso",
|
||||
"Data/Time/Time_Zone.enso",
|
||||
"Data/Vector.enso",
|
||||
|
@ -79,15 +79,18 @@ public class Time_Utils {
|
||||
return date.atTime(time).atZone(zone);
|
||||
}
|
||||
|
||||
public static LocalDate date_adjust(LocalDate date, AdjustOp op, Period duration) {
|
||||
switch (op) {
|
||||
case PLUS:
|
||||
return date.plus(duration);
|
||||
case MINUS:
|
||||
return date.minus(duration);
|
||||
default:
|
||||
throw new DateTimeException("Unknown adjust operation");
|
||||
}
|
||||
public static LocalDate date_adjust(LocalDate date, AdjustOp op, Period period) {
|
||||
return switch (op) {
|
||||
case PLUS -> date.plus(period);
|
||||
case MINUS -> date.minus(period);
|
||||
};
|
||||
}
|
||||
|
||||
public static ZonedDateTime datetime_adjust(ZonedDateTime datetime, AdjustOp op, Period period) {
|
||||
return switch (op) {
|
||||
case PLUS -> datetime.plus(period);
|
||||
case MINUS -> datetime.minus(period);
|
||||
};
|
||||
}
|
||||
|
||||
public static int get_field_as_localdate(LocalDate date, TemporalField field) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time.Duration
|
||||
from Standard.Base.Data.Time import Duration, Period
|
||||
|
||||
from Standard.Test import Bench
|
||||
|
||||
@ -62,10 +62,10 @@ main =
|
||||
|
||||
first_day = Date.new 1971
|
||||
date_vec = integer_vec.map x->
|
||||
first_day + x.days
|
||||
first_day + (Period.new days=x)
|
||||
first_dt = Date_Time.new 1971
|
||||
datetime_vec = integer_vec.map x->
|
||||
first_dt + x.minutes
|
||||
first_dt + (Duration.new minutes=x)
|
||||
|
||||
Bench.measure (count_entries integer_vec 4567) "Integer Equality" iter_size num_iterations
|
||||
Bench.measure (count_entries boolean_vec True expected_count=(n . div 5)) "Boolean Equality" iter_size num_iterations
|
||||
|
@ -1,6 +1,6 @@
|
||||
from Standard.Base import all
|
||||
from Standard.Base.Data.Index_Sub_Range import Sample
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Period
|
||||
|
||||
from Standard.Test import Bench
|
||||
|
||||
@ -11,7 +11,7 @@ main =
|
||||
num_iterations = 10
|
||||
|
||||
first_day = Date.new 2020 1 1
|
||||
dates = Vector.new 1000 (x -> first_day + x.days)
|
||||
dates = Vector.new 1000 (x -> first_day + (Period.new days=x))
|
||||
holidays = dates.take (Sample 100 100)
|
||||
shifts = [1, 5, 20, 100]
|
||||
|
||||
@ -20,7 +20,7 @@ main =
|
||||
Bench.measure (dates.zip shifted_dates d1-> d2-> d1.work_days_until d2) "(Shift="+shift.to_text+") work_days_until" iter_size num_iterations
|
||||
Bench.measure (dates.zip shifted_dates d1-> d2-> d1.work_days_until d2 holidays=holidays) "(Shift="+shift.to_text+") work_days_until with holidays" iter_size num_iterations
|
||||
|
||||
Bench.measure (dates.map date-> date + shift.days) "(Shift="+shift.to_text+") add regular days" iter_size num_iterations
|
||||
Bench.measure (dates.map date-> date + (Period.new days=shift)) "(Shift="+shift.to_text+") add regular days" iter_size num_iterations
|
||||
Bench.measure (dates.map date-> date.add_work_days shift) "(Shift="+shift.to_text+") add work days" iter_size num_iterations
|
||||
Bench.measure (dates.map date-> date.add_work_days shift holidays=holidays) "(Shift="+shift.to_text+") add work days with holidays" iter_size num_iterations
|
||||
|
||||
|
@ -3,6 +3,7 @@ from Standard.Base import all
|
||||
import Standard.Base.Data.Text.Text_Sub_Range
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Period
|
||||
from Standard.Base.Error.Common import Time_Error
|
||||
|
||||
from Standard.Test import Test, Test_Suite
|
||||
@ -92,35 +93,35 @@ spec_with name create_new_date parse_date =
|
||||
date.to_json.should_equal <|
|
||||
Json.from_pairs [["type", "Date"], ["day", date.day], ["month", date.month], ["year", date.year]]
|
||||
|
||||
Test.specify "should add date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
date = create_new_date 1970 + 1.day
|
||||
Test.specify "should add date-based interval" <|
|
||||
date = create_new_date 1970 + (Period.days 1)
|
||||
date . year . should_equal 1970
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 2
|
||||
|
||||
Test.specify "should subtract date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
date = create_new_date 1970 - 1.year
|
||||
Test.specify "should subtract date-based interval" <|
|
||||
date = create_new_date 1970 - (Period.years 1)
|
||||
date . year . should_equal 1969
|
||||
date . month . should_equal 1
|
||||
date . day . should_equal 1
|
||||
|
||||
Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
date = create_new_date 1970 + 1.month - 1.year
|
||||
Test.specify "should support mixed interval operators" <|
|
||||
date = create_new_date 1970 + (Period.months 1) - (Period.years 1)
|
||||
date . year . should_equal 1969
|
||||
date . month . should_equal 2
|
||||
date . day . should_equal 1
|
||||
|
||||
Test.specify "should throw error when adding time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
case (create_new_date 1970 + (Duration.new hours=1)) . catch of
|
||||
Test.specify "should throw error when adding time-based Duration" <|
|
||||
case (create_new_date 1970 + (Duration.hours 1)) . catch of
|
||||
Time_Error_Data message ->
|
||||
message . should_equal "Date does not support time intervals"
|
||||
message . should_equal "Date does not support adding/subtracting Duration. Use Period instead."
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should throw error when subtracting time-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
case (create_new_date 1970 - (1.day - (Duration.new minutes=1))) . catch of
|
||||
Test.specify "should throw error when subtracting time-based Duration" <|
|
||||
case (create_new_date 1970 - (Duration.minutes 1)) . catch of
|
||||
Time_Error_Data message ->
|
||||
message . should_equal "Date does not support time intervals"
|
||||
message . should_equal "Date does not support adding/subtracting Duration. Use Period instead."
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
@ -228,7 +229,7 @@ spec_with name create_new_date parse_date =
|
||||
(create_new_date 2000 7 1).end_of Date_Period.Quarter . should_equal (Date.new 2000 9 30)
|
||||
(create_new_date 2000 6 30).end_of Date_Period.Quarter . should_equal (Date.new 2000 6 30)
|
||||
|
||||
Test.specify "should allow to compute the number of working days until a later date" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to compute the number of working days until a later date" <|
|
||||
# 2000-2-1 is a Tuesday
|
||||
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) . should_equal 0
|
||||
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) . should_equal 1
|
||||
@ -279,7 +280,7 @@ spec_with name create_new_date parse_date =
|
||||
# We duplicate the holiday entries to check that the functions are resilient to such input data.
|
||||
duplicated_holiday_november year =
|
||||
holiday_november year + holiday_november year + holiday_november year
|
||||
Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to compute the number of working days until a date, skipping custom set holidays" <|
|
||||
holiday_february = Vector.new 29 (i -> create_new_date 2000 2 i+1)
|
||||
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 3 1) holiday_february . should_equal 0
|
||||
create_new_date 2000 2 10 . work_days_until (create_new_date 2000 2 12) holiday_february . should_equal 0
|
||||
@ -294,7 +295,7 @@ spec_with name create_new_date parse_date =
|
||||
create_new_date 2000 11 1 . work_days_until (create_new_date 2000 12 1) (duplicated_holiday_november 2020) . should_equal 22
|
||||
create_new_date 1999 11 1 . work_days_until (create_new_date 1999 12 1) (duplicated_holiday_november 1999) . should_equal 19
|
||||
|
||||
Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to compute the number of working days including the end, in a manner consistent with NETWORKDAYS" <|
|
||||
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 1) include_end_date=True . should_equal 1
|
||||
create_new_date 2000 2 1 . work_days_until (create_new_date 2000 2 2) include_end_date=True . should_equal 2
|
||||
create_new_date 2000 2 3 . work_days_until (create_new_date 2000 2 16) include_end_date=True . should_equal 10
|
||||
@ -311,7 +312,7 @@ spec_with name create_new_date parse_date =
|
||||
create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 8) include_end_date=True . should_equal 2
|
||||
create_new_date 2000 2 6 . work_days_until (create_new_date 2000 2 5) include_end_date=True . should_equal 0
|
||||
|
||||
Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by N working days" <|
|
||||
# 2000-2-1 is a Tuesday
|
||||
create_new_date 2000 2 1 . add_work_days 0 . should_equal (Date.new 2000 2 1)
|
||||
create_new_date 2000 2 1 . add_work_days . should_equal (Date.new 2000 2 2)
|
||||
@ -343,7 +344,7 @@ spec_with name create_new_date parse_date =
|
||||
create_new_date 2022 3 27 . add_work_days 0 . should_equal (Date.new 2022 3 28)
|
||||
create_new_date 2022 3 27 . add_work_days 1 . should_equal (Date.new 2022 3 29)
|
||||
|
||||
Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by negative amount of working days" <|
|
||||
# 2000-2-1 is a Tuesday
|
||||
create_new_date 2000 2 1 . add_work_days -1 . should_equal (Date.new 2000 1 31)
|
||||
create_new_date 2000 2 1 . add_work_days -2 . should_equal (Date.new 2000 1 28)
|
||||
@ -372,10 +373,10 @@ spec_with name create_new_date parse_date =
|
||||
create_new_date 2000 2 6 . add_work_days -5 . should_equal (Date.new 2000 1 31)
|
||||
create_new_date 2000 2 6 . add_work_days -6 . should_equal (Date.new 2000 1 28)
|
||||
|
||||
Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by N working days, skipping custom holidays" <|
|
||||
all_year_holiday year =
|
||||
first_day = create_new_date year 1 1
|
||||
Vector.new first_day.length_of_year (n -> first_day + n.days)
|
||||
Vector.new first_day.length_of_year (n -> first_day + (Period.days n))
|
||||
|
||||
two_years_vacation = all_year_holiday 1999 + all_year_holiday 2000
|
||||
|
||||
@ -393,10 +394,10 @@ spec_with name create_new_date parse_date =
|
||||
create_new_date 1999 10 30 . add_work_days 0 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 3)
|
||||
create_new_date 1999 10 30 . add_work_days 1 (duplicated_holiday_november 1999) . should_equal (Date.new 1999 11 4)
|
||||
|
||||
Test.specify "add_work_days and work_days_until should be consistent with each other" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "add_work_days and work_days_until should be consistent with each other" <|
|
||||
first_day = create_new_date 2020 1 1
|
||||
dates = Vector.new 100 (n -> first_day + n.days)
|
||||
holidays = [1, 2, 10, 11, 12, 13, 14, 15, 30, 40, 41, 42, 50, 60].map (n -> first_day + n.days)
|
||||
dates = Vector.new 100 (n -> first_day + (Period.new days=n))
|
||||
holidays = [1, 2, 10, 11, 12, 13, 14, 15, 30, 40, 41, 42, 50, 60].map (n -> first_day + (Period.new days=n))
|
||||
shifts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 90, 100]
|
||||
dates.each date->
|
||||
date.work_days_until (date.add_work_days 0) . should_equal 0
|
||||
|
@ -1,7 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
import Standard.Base.Data.Time.Time_Period
|
||||
import Standard.Base.Data.Time.Duration
|
||||
from Standard.Base.Data.Time import Duration, Period, Date_Period, Time_Period
|
||||
|
||||
from Standard.Test import Test, Test_Suite
|
||||
|
||||
@ -231,7 +229,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . day . should_equal 1
|
||||
|
||||
Test.specify "should add time interval" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (Duration.new nanoseconds=1)
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (Duration.nanoseconds 1)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 1
|
||||
time . day . should_equal 1
|
||||
@ -241,8 +239,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . nanosecond . should_equal 1
|
||||
time . zone . should_equal Time_Zone.utc
|
||||
|
||||
Test.specify "should add date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) + 1.month
|
||||
Test.specify "should add date interval" <|
|
||||
time = (create_new_datetime 1970 (zone = Time_Zone.utc)) + (Period.months 1)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
@ -252,8 +250,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should add mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (1.month + (Duration.new hours=3))
|
||||
Test.specify "should add mixed date time interval" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) + (Period.months 1) + (Duration.hours 3)
|
||||
time . year . should_equal 1970
|
||||
time . month . should_equal 2
|
||||
time . day . should_equal 1
|
||||
@ -264,7 +262,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract time interval" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Duration.new hours=1)
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Duration.hours 1)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 31
|
||||
@ -274,8 +272,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract date interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month
|
||||
Test.specify "should subtract date interval" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Period.months 1)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
@ -285,19 +283,19 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should subtract mixed date time interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (1.month - (Duration.new hours=3))
|
||||
Test.specify "should subtract mixed date time interval" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Period.months 1) - (Duration.hours 3)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
time . hour . should_equal 3
|
||||
time . month . should_equal 11
|
||||
time . day . should_equal 30
|
||||
time . hour . should_equal 21
|
||||
time . minute . should_equal 0
|
||||
time . second . should_equal 0
|
||||
time . nanosecond . should_equal 0
|
||||
time . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should support mixed interval operators" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - 1.month + (Duration.new hours=12)
|
||||
Test.specify "should support mixed interval operators" <|
|
||||
time = create_new_datetime 1970 (zone = Time_Zone.utc) - (Period.months 1) + (Duration.hours 12)
|
||||
time . year . should_equal 1969
|
||||
time . month . should_equal 12
|
||||
time . day . should_equal 1
|
||||
@ -450,7 +448,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the spring DST switch" pending=js_dst_pending <|
|
||||
d1 = create_new_datetime 2022 3 27 1 34 15 0 tz
|
||||
d2 = create_new_datetime 2022 3 27 3 34 15 0 tz
|
||||
d1_plus = d1 + (Duration.new hours=1)
|
||||
d1_plus = d1 + (Duration.hours 1)
|
||||
d1_plus . should_equal d2
|
||||
|
||||
check_dates_spring date =
|
||||
@ -482,7 +480,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
dst_overlap_message = "We cannot correctly migrate the datetime inside of the timeline overlap through the polyglot boundar - as due to polyglot conversion limitation, always the earlier one is chosen. See the bug report: https://github.com/oracle/graal/issues/4918"
|
||||
Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the autumn DST switch" pending=dst_overlap_message <|
|
||||
d3 = create_new_datetime 2022 10 30 2 30 15 0 tz
|
||||
d4 = d3 + (Duration.new hours=1)
|
||||
d4 = d3 + (Duration.hours 1)
|
||||
|
||||
d3.hour . should_equal 2
|
||||
d4.hour . should_equal 2
|
||||
@ -524,7 +522,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
d4_end.nanosecond . should_equal max_nanos
|
||||
Time_Utils.get_datetime_offset d4_end . should_equal offset_1_h
|
||||
|
||||
Test.specify "should allow to shift the date by N working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by N working days" <|
|
||||
# 2000-2-1 is a Tuesday
|
||||
create_new_datetime 2000 2 1 12 30 . add_work_days 0 . should_equal (Date_Time.new 2000 2 1 12 30)
|
||||
create_new_datetime 2000 2 1 12 15 55 . add_work_days . should_equal (Date_Time.new 2000 2 2 12 15 55)
|
||||
@ -545,9 +543,9 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
create_new_datetime 2000 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 2000 3 2 12 10)
|
||||
create_new_datetime 1999 2 27 12 10 . add_work_days 3 . should_equal (Date_Time.new 1999 3 4 12 10)
|
||||
|
||||
Test.specify "should handle shifting dates around spring DST edge cases" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should handle shifting dates around spring DST edge cases" <|
|
||||
# 2022-10-30 and 2022-03-27 are DST switch days, Sundays.
|
||||
create_new_datetime 2022 10 30 2 30 55 1234 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 55 1234)
|
||||
create_new_datetime 2022 10 30 2 30 55 1234 . add_work_days 0 . should_equal (create_new_datetime 2022 10 31 2 30 55 1234)
|
||||
create_new_datetime 2022 10 30 1 30 . add_work_days 1 . should_equal (Date_Time.new 2022 11 1 1 30)
|
||||
create_new_datetime 2022 10 30 3 30 . add_work_days 1 . should_equal (Date_Time.new 2022 11 1 3 30)
|
||||
|
||||
@ -556,12 +554,12 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
|
||||
Test.specify "should handle shifting dates around autumn DST edge cases" pending=dst_overlap_message <|
|
||||
d3 = create_new_datetime 2022 10 30 2 30 15 0 tz
|
||||
d4 = d3 + (Duration.new hours=1)
|
||||
d4 = d3 + (Duration.hours 1)
|
||||
|
||||
# TODO we need to check and document the actual behaviour once it is expressible, it may be equally acceptable to shift to 3:30 instead of 2:30.
|
||||
d4 . add_work_days 0 . should_equal (Date_Time.new 2022 10 31 2 30 15 0 tz)
|
||||
|
||||
Test.specify "should allow to shift the date by negative amount of working days" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by negative amount of working days" <|
|
||||
# 2000-2-1 is a Tuesday
|
||||
create_new_datetime 2000 2 1 12 30 . add_work_days -1 . should_equal (Date_Time.new 2000 1 31 12 30)
|
||||
create_new_datetime 2000 2 1 13 30 . add_work_days -2 . should_equal (Date_Time.new 2000 1 28 13 30)
|
||||
@ -571,10 +569,10 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
create_new_datetime 2000 2 6 0 1 . add_work_days -2 . should_equal (Date_Time.new 2000 2 3 0 1)
|
||||
create_new_datetime 2000 2 6 23 59 . add_work_days -6 . should_equal (Date_Time.new 2000 1 28 23 59)
|
||||
|
||||
Test.specify "should allow to shift the date by N working days, skipping custom holidays" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
Test.specify "should allow to shift the date by N working days, skipping custom holidays" <|
|
||||
all_year_holiday year =
|
||||
first_day = Date.new year 1 1
|
||||
Vector.new first_day.length_of_year (n -> first_day + n.days)
|
||||
Vector.new first_day.length_of_year (n -> first_day + (Period.new days=n))
|
||||
holiday_november year =
|
||||
[Date.new year 11 1, Date.new year 11 2, Date.new year 11 11]
|
||||
# We duplicate the holiday entries to check that the functions are resilient to such input data.
|
||||
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Period
|
||||
from Standard.Test import Test, Test_Suite
|
||||
|
||||
polyglot java import java.time.Duration as Java_Duration
|
||||
@ -24,7 +25,7 @@ spec =
|
||||
Test.group "Duration" <|
|
||||
|
||||
Test.specify "should create interval seconds" <|
|
||||
duration = (Duration.new seconds=5)
|
||||
duration = (Duration.seconds 5)
|
||||
duration.seconds . should_equal 5
|
||||
duration.milliseconds . should_equal 0
|
||||
|
||||
@ -38,42 +39,54 @@ spec =
|
||||
interval.is_empty . should_be_true
|
||||
|
||||
Test.specify "should normalize periods" <|
|
||||
(Duration.new seconds=60).total_minutes . should_equal 1
|
||||
(Duration.new milliseconds=1000).total_seconds . should_equal 1
|
||||
(Duration.seconds 60).total_minutes . should_equal 1
|
||||
(Duration.milliseconds 1000).total_seconds . should_equal 1
|
||||
|
||||
Test.specify "should normalize addition" <|
|
||||
duration = (Duration.new hours=11) + (Duration.new hours=1)
|
||||
duration = (Duration.hours 11) + (Duration.hours 1)
|
||||
duration.hours . should_equal 12
|
||||
|
||||
Test.specify "should normalize subtraction" <|
|
||||
duration = (Duration.new hours=13) - (Duration.new hours=1)
|
||||
duration = (Duration.hours 13) - (Duration.hours 1)
|
||||
duration.hours . should_equal 12
|
||||
|
||||
Test.specify "should convert to Json" <|
|
||||
interval = (Duration.new nanoseconds=120) + (Duration.new seconds=30) + (Duration.new hours=14)
|
||||
interval = (Duration.nanoseconds 120) + (Duration.seconds 30) + (Duration.hours 14)
|
||||
interval.to_json.should_equal <|
|
||||
duration_pairs = [["nanoseconds", interval.nanoseconds], ["seconds", interval.seconds], ["hours", interval.hours]]
|
||||
Json.from_pairs ([["type", "Duration"]] + duration_pairs)
|
||||
|
||||
Test.specify "should be comparable" <|
|
||||
duration_1 = (Duration.new hours=5)
|
||||
duration_2 = (Duration.new minutes=1)
|
||||
duration_1 = (Duration.hours 5)
|
||||
duration_2 = (Duration.minutes 1)
|
||||
duration_1.compare_to duration_1 . should_equal Ordering.Equal
|
||||
duration_1==duration_1 . should_be_true
|
||||
duration_1!=duration_2 . should_be_true
|
||||
duration_1>duration_2 . should_be_true
|
||||
duration_1<duration_2 . should_be_false
|
||||
|
||||
Test.specify "should not mix Duration and Period" <|
|
||||
durations = [(Duration.hours 1), (Duration.zero), (Duration.new hours=1 seconds=30)]
|
||||
periods = [(Period.days 1), (Period.new 0), (Period.years 30), (Period.new years=3 months=2)]
|
||||
durations.each duration->
|
||||
periods.each period->
|
||||
(duration + period).should_fail_with Time_Error_Data
|
||||
(duration - period).should_fail_with Time_Error_Data
|
||||
(period + duration).should_fail_with Time_Error_Data
|
||||
(period - duration).should_fail_with Time_Error_Data
|
||||
(duration > period).should_fail_with Time_Error_Data
|
||||
(duration < period).should_fail_with Time_Error_Data
|
||||
|
||||
Test.specify "Date_Time supports adding and subtracting Duration" <|
|
||||
((Date_Time.new 2022 10 1 hour=10) + (Duration.new hours=2)) . should_equal (Date_Time.new 2022 10 1 hour=12)
|
||||
((Date_Time.new 2022 10 1 hour=10) - (Duration.new hours=2)) . should_equal (Date_Time.new 2022 10 1 hour=8)
|
||||
((Date_Time.new 2022 10 2) - (Duration.new hours=24)) . should_equal (Date_Time.new 2022 10 1)
|
||||
((Date_Time.new 2022 10 1 hour=2) - (Duration.new minutes=3)) . should_equal (Date_Time.new 2022 10 1 hour=1 minute=57)
|
||||
((Date_Time.new 2022 10 1 hour=10) + (Duration.hours 2)) . should_equal (Date_Time.new 2022 10 1 hour=12)
|
||||
((Date_Time.new 2022 10 1 hour=10) - (Duration.hours 2)) . should_equal (Date_Time.new 2022 10 1 hour=8)
|
||||
((Date_Time.new 2022 10 2) - (Duration.hours 24)) . should_equal (Date_Time.new 2022 10 1)
|
||||
((Date_Time.new 2022 10 1 hour=2) - (Duration.minutes 3)) . should_equal (Date_Time.new 2022 10 1 hour=1 minute=57)
|
||||
|
||||
Test.specify "Java Duration is equal to Enso Duration" <|
|
||||
(Duration.new hours=1) . should_equal (Java_Duration.ofHours 1)
|
||||
(Duration.new minutes=80) . should_equal (Java_Duration.ofMinutes 80)
|
||||
(Java_Duration.ofSeconds 30) . should_equal (Duration.new seconds=30)
|
||||
(Duration.hours 1) . should_equal (Java_Duration.ofHours 1)
|
||||
(Duration.minutes 80) . should_equal (Java_Duration.ofMinutes 80)
|
||||
(Java_Duration.ofSeconds 30) . should_equal (Duration.seconds 30)
|
||||
|
||||
Test.specify "Difference of Java Date and Enso date should be an Enso Duration" <|
|
||||
(Duration.between (java_datetime 2022 01 01) (Date_Time.new 2022 01 02) timezone_aware=False).total_hours . should_equal 24
|
||||
|
43
test/Tests/src/Data/Time/Period_Spec.enso
Normal file
43
test/Tests/src/Data/Time/Period_Spec.enso
Normal file
@ -0,0 +1,43 @@
|
||||
from Standard.Base import all
|
||||
from Standard.Test import Test, Test_Suite
|
||||
from Standard.Base.Data.Time import Period
|
||||
|
||||
spec =
|
||||
Test.group "Period" <|
|
||||
Test.specify "should create period years" <|
|
||||
period = (Period.years 5)
|
||||
period.years . should_equal 5
|
||||
period.days . should_equal 0
|
||||
|
||||
Test.specify "should add two Periods" <|
|
||||
((Period.years 1) + (Period.years 2)).years . should_equal 3
|
||||
((Period.days 1) + (Period.months 2)).days . should_equal 1
|
||||
((Period.days 1) + (Period.months 2)).months . should_equal 2
|
||||
((Period.months 2) + (Period.days 1)).days . should_equal 1
|
||||
((Period.months 2) + (Period.days 1)).months . should_equal 2
|
||||
|
||||
Test.specify "should subtract two Periods" <|
|
||||
((Period.years 2) - (Period.years 1)).years . should_equal 1
|
||||
((Period.years 1) - (Period.months 2)).months . should_equal (-2)
|
||||
((Period.years 1) - (Period.months 2)).years . should_equal 1
|
||||
|
||||
Test.specify "should get Period between two dates" <|
|
||||
(Period.between (Date.new year=100) (Date.new year=150)) . should_equal (Period.years 50)
|
||||
(Period.between (Date.new year=150) (Date.new year=100)) . should_equal (Period.new years=(-50))
|
||||
(Period.between (Date.new 2022 10 19) (Date.new 2022 11 01)) . should_equal (Period.days 13)
|
||||
|
||||
Test.specify "should not compare between two periods" <|
|
||||
((Period.days 10) > (Period.days 1)) . should_fail_with Incomparable_Values_Error
|
||||
((Period.years 10) > (Period.days 1)) . should_fail_with Incomparable_Values_Error
|
||||
((Period.new years=10 months=3) > (Period.months 5)) . should_fail_with Incomparable_Values_Error
|
||||
|
||||
Test.specify "two Periods are equal iff their fields are equal" <|
|
||||
((Period.days 1) == (Period.days 1)) . should_be_true
|
||||
((Period.months 12) == (Period.years 1)) . should_be_false
|
||||
((Period.months 3) == (Period.months 3)) . should_be_true
|
||||
((Period.days (-5)) == (Period.days (-5))) . should_be_true
|
||||
((Period.new years=1 days=10) == (Period.new years=1 days=10)) . should_be_true
|
||||
((Period.days 1) != (Period.months 1)) . should_be_true
|
||||
|
||||
|
||||
main = Test_Suite.run_main spec
|
@ -3,6 +3,7 @@ from Standard.Base import all
|
||||
from Standard.Test import Test_Suite
|
||||
|
||||
import project.Data.Time.Duration_Spec
|
||||
import project.Data.Time.Period_Spec
|
||||
import project.Data.Time.Time_Of_Day_Spec
|
||||
import project.Data.Time.Date_Spec
|
||||
import project.Data.Time.Date_Time_Spec
|
||||
@ -12,6 +13,7 @@ import project.Data.Time.Day_Of_Week_Spec
|
||||
spec =
|
||||
Date_Spec.spec
|
||||
Duration_Spec.spec
|
||||
Period_Spec.spec
|
||||
Time_Of_Day_Spec.spec
|
||||
Date_Time_Spec.spec
|
||||
Time_Zone_Spec.spec
|
||||
|
@ -1,6 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time.Duration
|
||||
import Standard.Base.Data.Time.Time_Period
|
||||
from Standard.Base.Data.Time import Duration, Period, Time_Period
|
||||
from Standard.Base.Error.Common import Time_Error_Data
|
||||
|
||||
from Standard.Test import Test, Test_Suite
|
||||
@ -83,28 +82,28 @@ specWith name create_new_time parse_time =
|
||||
datetime . zone . zone_id . should_equal Time_Zone.utc.zone_id
|
||||
|
||||
Test.specify "should add time-based interval" <|
|
||||
time = create_new_time 0 + (Duration.new minutes=1)
|
||||
time = create_new_time 0 + (Duration.minutes 1)
|
||||
time . to_seconds . should_equal 60
|
||||
|
||||
Test.specify "should subtract time-based interval" <|
|
||||
time = create_new_time 0 - (Duration.new minutes=1)
|
||||
time = create_new_time 0 - (Duration.minutes 1)
|
||||
time . to_seconds . should_equal 86340
|
||||
|
||||
Test.specify "should support mixed interval operators" <|
|
||||
time = create_new_time 0 + (Duration.new hours=1) - (Duration.new seconds=1)
|
||||
time = create_new_time 0 + (Duration.hours 1) - (Duration.seconds 1)
|
||||
time . to_seconds . should_equal 3599
|
||||
|
||||
Test.specify "should throw error when adding date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
case (create_new_time 0 + 1.day) . catch of
|
||||
Test.specify "should throw error when adding date-based interval" <|
|
||||
case (create_new_time 0 + (Period.days 1)) . catch of
|
||||
Time_Error_Data message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
message . should_equal "Time_Of_Day does not support date intervals (periods)"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
Test.specify "should throw error when subtracting date-based interval" pending="Wait until Period type is implemented (https://www.pivotaltracker.com/story/show/183336003)" <|
|
||||
case (create_new_time 0 - (1.day - (Duration.new minutes=1))) . catch of
|
||||
Test.specify "should throw error when subtracting date-based interval" <|
|
||||
case (create_new_time 0 - (Period.days 1)) . catch of
|
||||
Time_Error_Data message ->
|
||||
message . should_equal "Time_Of_Day does not support date intervals"
|
||||
message . should_equal "Time_Of_Day does not support date intervals (periods)"
|
||||
result ->
|
||||
Test.fail ("Unexpected result: " + result.to_text)
|
||||
|
||||
|
@ -30,8 +30,8 @@ spec =
|
||||
url_get = base_url_with_slash + "get"
|
||||
url_post = base_url_with_slash + "post"
|
||||
Test.specify "should create HTTP client with timeout setting" <|
|
||||
http = Http.new (timeout = (Duration.new seconds=30))
|
||||
http.timeout.should_equal (Duration.new seconds=30)
|
||||
http = Http.new (timeout = (Duration.seconds 30))
|
||||
http.timeout.should_equal (Duration.seconds 30)
|
||||
Test.specify "should create HTTP client with follow_redirects setting" <|
|
||||
http = Http.new (follow_redirects = False)
|
||||
http.follow_redirects.should_equal False
|
||||
|
Loading…
Reference in New Issue
Block a user