Skip to content

Commit

Permalink
Implement addition and subtraction for Date_Period and Time_Period (#…
Browse files Browse the repository at this point in the history
…6956)

Date.+ should allow Date_Period, Time_Of_Day.+ should allow Time_Period and Date_TIme.+ should allow both.
Same for subtraction.
  • Loading branch information
GregoryTravis authored Jun 7, 2023
1 parent 0a4df4f commit fcc57e4
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 63 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@
- [Split `Table.create_database_table` into `Connection.create_table` and
`Table.select_into_database_table`.][6925]
- [Speed improvements to `Column` `.truncate`, `.ceil`, and `.floor`.][6941]
- [Implemented addition and subtraction for `Date_Period` and
`Time_Period`.][6956]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -698,6 +700,7 @@
[6922]: https://github.com/enso-org/enso/pull/6922
[6925]: https://github.com/enso-org/enso/pull/6925
[6941]: https://github.com/enso-org/enso/pull/6941
[6956]: https://github.com/enso-org/enso/pull/6956

#### Enso Compiler

Expand Down
48 changes: 30 additions & 18 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso
Original file line number Diff line number Diff line change
Expand Up @@ -430,23 +430,29 @@ type Date
Add the specified amount of time to this instant to get another date.

Arguments:
- amount: The time duration to add to this instant.
- amount: The amount of time to add to this instant. It can be a
`Period` or `Date_Period`.

> Example
Add 6 months to a local date.

import Standard.Base.Data.Time.Duration

example_add = Date.new 2020 + (Period.new months=6)
+ : Period -> Date ! (Time_Error | Illegal_Argument)
+ self period =
case period of
_ : Period ->

> Example
Add a month to a local date.

import Standard.Base.Data.Time.Date_Period

example_add = Date.new 2020 + Date_Period.Month
+ : Period | Date_Period -> Date ! (Time_Error | Illegal_Argument)
+ self amount:(Period|Date_Period) =
case amount of
period : Period ->
Time_Utils.date_adjust self Time_Utils.AdjustOp.PLUS period.internal_period
_ : Duration ->
Error.throw (Time_Error.Error "Date does not support adding/subtracting Duration. Use Period instead.")
_ ->
Error.throw (Illegal_Argument.Error "Illegal period argument")
date_period : Date_Period ->
self + date_period.to_period

## ALIAS Date Range
Creates an increasing range of dates from `self` to `end`.
Expand Down Expand Up @@ -623,7 +629,8 @@ type Date
date.

Arguments:
- amount: The time duration to subtract from this date.
- amount: The amount of time to add to this instant. It can be a
`Period` or `Date_Period`.

> Example
Subtract 7 days from a local date.
Expand All @@ -632,16 +639,21 @@ type Date
import Standard.Base.Data.Time.Duration

example_subtract = Date.new 2020 - (Period.new days=7)
- : Period -> Date ! (Time_Error | Illegal_Argument)
- self period =
case period of
_ : Period ->

> Example
Subtract a month from a local date.

import Standard.Base.Data.Time.Date_Period

example_add = Date.new 2020 - Date_Period.Month
- : Period | Date_Period -> Date ! (Time_Error | Illegal_Argument)
- self amount:(Period|Date_Period) =
case amount of
period : Period ->
new_java_date = Time_Utils.date_adjust self Time_Utils.AdjustOp.MINUS period.internal_period
Date.new new_java_date.year new_java_date.month new_java_date.day
_ : Duration ->
Error.throw (Time_Error.Error "Date does not support adding/subtracting Duration. Use Period instead.")
_ ->
Error.throw (Illegal_Argument.Error "Illegal period argument")
date_period : Date_Period ->
self - date_period.to_period

## PRIVATE
Convert to a display representation of this Date.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,8 @@ 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, either `Duration` for
adding time (hours, minutes, etc.), or `Period` for adding date (days,
months, years).
- amount: The amount of time to add to this instant, It can be a
`Duration`, `Period`, `Time_Period`, or `Date_Period`.

> Example
Add 15 years and 3 hours to a zoned date time.
Expand All @@ -517,13 +516,24 @@ type Date_Time
from Standard.Base.Data.Time import Duration

example_plus = Date_Time.new 2020 + (Period.new years=15) + (Duration.new hours=3)
+ : (Duration | Period) -> Date_Time ! Time_Error
+ self amount =

> Example
Add one quarter to a zoned date time.

from Standard.Base import Date_Time, Date_Period

example_plus = Date_Time.new 2020 + Date_Period.Quarter
+ : (Duration | Period | Time_Period | Date_Period) -> Date_Time ! Time_Error
+ self amount:(Duration|Period|Time_Period|Date_Period) =
case amount of
duration : Duration ->
Panic.catch ArithmeticException (self.plus_builtin duration) (err -> Error.throw (Time_Error.Error err.getMessage))
period : Period ->
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.PLUS period.internal_period
time_period : Time_Period ->
self + time_period.to_duration
date_period : Date_Period ->
self + date_period.to_period

## Shift the date by the specified amount of business days.

Expand Down Expand Up @@ -573,9 +583,8 @@ 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. Can be either
`Duration` for subtracting time (hours, minutes, etc.), or `Period`
for subtracting days, months and years.
- amount: The amount of time to add to this instant, It can be a
`Duration`, `Period`, `Time_Period`, or `Date_Period`.

> Example
Subtract 1 year, 9 months and 12 hours from a zoned date time.
Expand All @@ -584,13 +593,24 @@ type Date_Time
import Standard.Base.Data.Time.Duration

example_minus = Date_Time.new 2020 - (Period.new years=1) - (Period.new months=9) - (Duration.new hours=5)
- : (Duration | Period) -> Date_Time ! Time_Error
- self amount =

> Example
Subtract one quarter from a zoned date time.

from Standard.Base import Date_Time, Date_Period

example_plus = Date_Time.new 2020 - Date_Period.Quarter
- : (Duration | Period | Time_Period | Date_Period) -> Date_Time ! Time_Error
- self amount:(Duration|Period|Time_Period|Date_Period) =
result = case amount of
duration : Duration ->
Panic.catch ArithmeticException (self.minus_builtin duration) (err -> Error.throw (Time_Error.Error err.getMessage))
period : Period ->
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS period.internal_period
time_period : Time_Period ->
self - time_period.to_duration
date_period : Date_Period ->
self - date_period.to_period
ensure_in_epoch result result

## PRIVATE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,38 +258,56 @@ 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. Can be only
`Duration`.
- amount: The amount of time to add to this instant. It can be a
`Duration` or `Time_Period`.

> Example
Add a `Duration` to a `Time_Of_Day`.

from Standard.Base import Time_Of_Day, Duration

example_plus = Time_Of_Day.new + (Duration.new seconds=3)
+ : Duration -> Time_Of_Day ! Time_Error
+ self amount =

> Example
Add a `Time_Period` to a `Time_Of_Day`.

from Standard.Base import Time_Of_Day, Time_Period

example_plus = Time_Of_Day.new + Time_Period.Hour
+ : Duration | Time_Period -> Time_Of_Day ! Time_Error
+ self amount:(Duration|Time_Period) =
case amount of
duration : Duration -> self.plus_builtin duration
_ : Period -> Error.throw (Time_Error.Error "Time_Of_Day does not support date intervals (periods)")
time_period : Time_Period ->
self + time_period.to_duration

## ALIAS Subtract Duration
Subtract the specified amount of time from this instant to get a new
instant.

Arguments:
- amount: The amount of time to subtract from this instant.
- amount: The amount of time to add to this instant. It can be a
`Duration` or `Time_Period`.

> Example
Subtract 12 hours from a local time.

from Standard.Base import Time_Of_Day, Duration

example_minus = Time_Of_Day.now - (Duration.new hours=12)
- : Duration -> Time_Of_Day ! Time_Error
- self amount =

> Example
Subtract a `Time_Period` from a `Time_Of_Day`.

from Standard.Base import Time_Of_Day, Time_Period

example_plus = Time_Of_Day.new - Time_Period.Hour
- : Duration | Time_Period -> Time_Of_Day ! Time_Error
- self amount:(Duration|Time_Period) =
case amount of
duration : Duration -> self.minus_builtin duration
_ : Period -> Error.throw (Time_Error.Error "Time_Of_Day does not support date intervals (periods)")
time_period : Time_Period ->
self - time_period.to_duration

## PRIVATE
Convert to a JavaScript Object representing this Time_Of_Day.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Data.Time.Date_Time.Date_Time
import project.Data.Time.Duration.Duration
from project.Data.Boolean import Boolean, True, False

polyglot java import org.enso.base.Time_Utils
Expand Down Expand Up @@ -33,3 +34,11 @@ type Time_Period
adjust_end : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time)
adjust_end self date =
(Time_Utils.utils_for date).end_of_time_period date self.to_java_unit

## PRIVATE
to_duration : Duration
to_duration self = case self of
Time_Period.Day -> Duration.new 24
Time_Period.Hour -> Duration.new 1
Time_Period.Minute -> Duration.new 0 1
Time_Period.Second -> Duration.new 0 0 1
36 changes: 26 additions & 10 deletions test/Tests/src/Data/Time/Date_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ from Standard.Base import all
import Standard.Base.Errors.Common.Incomparable_Values
import Standard.Base.Errors.Common.Type_Error
import Standard.Base.Errors.Time_Error.Time_Error
import Standard.Base.Meta

from Standard.Test import Problems, Test, Test_Suite
import Standard.Test.Extensions
Expand Down Expand Up @@ -139,18 +140,33 @@ spec_with name create_new_date parse_date =
date . day . should_equal 1

Test.specify "should throw error when adding time-based Duration" <|
case (create_new_date 1970 + (Duration.new hours=1)) . catch of
Time_Error.Error message ->
message . should_equal "Date does not support adding/subtracting Duration. Use Period instead."
result ->
Test.fail ("Unexpected result: " + result.to_text)
Test.expect_panic_with matcher=Type_Error <|
create_new_date 1970 + (Duration.new hours=1)

Test.specify "should throw error when subtracting time-based Duration" <|
case (create_new_date 1970 - (Duration.new minutes=1)) . catch of
Time_Error.Error message ->
message . should_equal "Date does not support adding/subtracting Duration. Use Period instead."
result ->
Test.fail ("Unexpected result: " + result.to_text)
Test.expect_panic_with matcher=Type_Error <|
create_new_date 1970 - (Duration.new minutes=1)

Test.specify "should support addition of Date_Period" <|
time = create_new_date 1970
time+Date_Period.Year . should_equal <| create_new_date 1971
time+Date_Period.Quarter . should_equal <| create_new_date 1970 4
time+Date_Period.Month . should_equal <| create_new_date 1970 2
time+Date_Period.Week . should_equal <| create_new_date 1970 1 8
time+Date_Period.Day . should_equal <| create_new_date 1970 1 2

Test.specify "should support subtraction of Date_Period" <|
time = create_new_date 1970
time-Date_Period.Year . should_equal <| create_new_date 1969
time-Date_Period.Quarter . should_equal <| create_new_date 1969 10
time-Date_Period.Month . should_equal <| create_new_date 1969 12
time-Date_Period.Week . should_equal <| create_new_date 1969 12 25
time-Date_Period.Day . should_equal <| create_new_date 1969 12 31

Test.specify "should support mixed addition and subtraction of Date_Period" <|
time = create_new_date 1970
time+Date_Period.Quarter-Date_Period.Month . should_equal <| create_new_date 1970 3 1
time-Date_Period.Month+Date_Period.Year . should_equal <| create_new_date 1970 12 1

Test.specify "should be comparable" <|
date_1 = parse_date "2021-01-02"
Expand Down
Loading

0 comments on commit fcc57e4

Please sign in to comment.