Skip to content

Commit

Permalink
Add date_diff, date_add and date_part to scalar Enso date-time …
Browse files Browse the repository at this point in the history
…values. (enso-org#7273)

Followup of enso-org#7221, adding `date_diff`, `date_add` and `date_part` to scalar Enso date-time values.
  • Loading branch information
radeusgd authored Jul 13, 2023
1 parent b042807 commit 620cc36
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 29 deletions.
42 changes: 42 additions & 0 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 @@ -325,6 +325,48 @@ type Date
ensure_in_epoch self <| ensure_in_epoch end <|
(Time_Utils.days_between self end) + if include_end_date then 1 else 0

## Returns a requested date part as integer.

Produces a warning for a Date that is before epoch start.
See `Date_Time.enso_epoch_start`.
date_part : Date_Period -> Integer
date_part self (period : Date_Period) =
case period of
Date_Period.Year -> self.year
Date_Period.Quarter -> self.quarter
Date_Period.Month -> self.month
Date_Period.Week _ -> self.week_of_year locale=Nothing
Date_Period.Day -> self.day

## Computes a time difference between the two dates.

It returns an integer expressing how many periods fit between the two
dates.

The difference will be positive if `end` is greater than `self`.

Produces a warning for a Date that is before epoch start.
See `Date_Time.enso_epoch_start`.

Arguments:
- end: A date to compute the difference from.
- period: The period to compute the difference in.
date_diff : Date -> Date_Period -> Integer
date_diff self (end : Date) (period : Date_Period) = ensure_in_epoch self <|
Time_Utils.unit_date_difference period.to_java_unit self end

## Shifts the date by a specified period.

Produces a warning for a Date that is before epoch start.
See `Date_Time.enso_epoch_start`.

Arguments:
- amount: An integer specifying by how many periods to shift the date.
- period: The period by which to shift.
date_add : Integer -> Date_Period -> Date
date_add self (amount : Integer) (period : Date_Period) = ensure_in_epoch self <|
Time_Utils.unit_date_add period.to_java_unit self amount

## Counts workdays between self (inclusive) and the provided end date
(exclusive).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,16 +312,44 @@ type Date_Time
second : Integer
second self = @Builtin_Method "Date_Time.second"

## Get the millisecond portion of the time.

> Example
Get the current millisecond.

from Standard.Base import Date_Time

example_millisecond = Date_Time.now.millisecond
millisecond : Integer
millisecond self = @Builtin_Method "Date_Time.millisecond"

## Get the microsecond portion of the time.

> Example
Get the current microsecond.

from Standard.Base import Date_Time

example_microsecond = Date_Time.now.microsecond
microsecond : Integer
microsecond self = @Builtin_Method "Date_Time.microsecond"

## Get the nanosecond portion of the time.

Arguments:
- include_milliseconds: Specifies if the whole fractional part of the
second should be returned as nanoseconds. Defaults to `False`, meaning
it will only return the nanosecond part in the range 0-999.

> Example
Get the current nanosecond.

from Standard.Base import Date_Time

example_nanosecond = Date_Time.now.nanosecond
nanosecond : Integer
nanosecond self = @Builtin_Method "Date_Time.nanosecond"
nanosecond : Boolean -> Integer
nanosecond self include_milliseconds=False =
self.nanosecond_builtin include_milliseconds

## Get the timezone for the time.

Expand Down Expand Up @@ -495,6 +523,56 @@ type Date_Time
at_zone self zone =
Time_Utils.with_zone_same_instant self zone

## Returns a requested date-time part as integer.

Produces a warning for a Date_Time that is before epoch start.
See `Date_Time.enso_epoch_start`.
date_part : Date_Period | Time_Period -> Integer
date_part self (period : Date_Period | Time_Period) =
case period of
Date_Period.Year -> self.year
Date_Period.Quarter -> self.quarter
Date_Period.Month -> self.month
Date_Period.Week _ -> self.week_of_year locale=Nothing
Date_Period.Day -> self.day
Time_Period.Day -> self.day
Time_Period.Hour -> self.hour
Time_Period.Minute -> self.minute
Time_Period.Second -> self.second
Time_Period.Millisecond -> self.millisecond
Time_Period.Microsecond -> self.microsecond
Time_Period.Nanosecond -> self.nanosecond

## Computes a time difference between the two date-times.

It returns an integer expressing how many periods fit between the two
date-times.

The difference will be positive if `end` is greater than `self`.

Produces a warning for a Date_Time that is before epoch start.
See `Date_Time.enso_epoch_start`.

Arguments:
- end: A date-time to compute the difference from.
- period: The period to compute the difference in.
date_diff : Date_Time -> Date_Period | Time_Period -> Integer
date_diff self (end : Date_Time) (period : Date_Period | Time_Period) = ensure_in_epoch self <|
Time_Utils.unit_datetime_difference period.to_java_unit self end

## Shifts the date-time by a specified period.

Produces a warning for a Date_Time that is before epoch start.
See `Date_Time.enso_epoch_start`.

Arguments:
- amount: An integer specifying by how many periods to shift the
date-time.
- period: The period by which to shift.
date_add : Integer -> Date_Period | Time_Period -> Date_Time
date_add self (amount : Integer) (period : Date_Period | Time_Period) = ensure_in_epoch self <|
Time_Utils.unit_datetime_add period.to_java_unit self amount

## ALIAS Add Period, Add Duration
Add the specified amount of time to this instant to produce a new instant.

Expand Down Expand Up @@ -610,7 +688,7 @@ type Date_Time
Convert to a display representation of this Date_Time.
to_display_text : Text
to_display_text self =
time_format = if self.nanosecond == 0 then "HH:mm:ss" else "HH:mm:ss.n"
time_format = if self.nanosecond include_milliseconds=True == 0 then "HH:mm:ss" else "HH:mm:ss.n"
self.format "yyyy-MM-dd "+time_format+" VV"

## PRIVATE
Expand All @@ -624,7 +702,7 @@ type Date_Time
to_js_object self =
type_pair = ["type", "Date_Time"]
cons_pair = ["constructor", "new"]
JS_Object.from_pairs [type_pair, cons_pair, ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond], ["zone", self.zone]]
JS_Object.from_pairs [type_pair, cons_pair, ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond include_milliseconds=True], ["zone", self.zone]]

## Format this time as text using the specified format specifier.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import project.Data.Time.Time_Period.Time_Period
import project.Data.Time.Time_Zone.Time_Zone
import project.Error.Error
import project.Errors.Common.Type_Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.Time_Error.Time_Error
import project.Meta
import project.Nothing.Nothing
Expand Down Expand Up @@ -201,16 +202,44 @@ type Time_Of_Day
second : Integer
second self = @Builtin_Method "Time_Of_Day.second"

## Get the millisecond portion of the time of day.

> Example
Get the current millisecond.

from Standard.Base import Time_Of_Day

example_millisecond = Time_Of_Day.now.millisecond
millisecond : Integer
millisecond self = @Builtin_Method "Time_Of_Day.millisecond"

## Get the microsecond portion of the time of day.

> Example
Get the current microsecond.

from Standard.Base import Time_Of_Day

example_microsecond = Time_Of_Day.now.microsecond
microsecond : Integer
microsecond self = @Builtin_Method "Time_Of_Day.microsecond"

## Get the nanosecond portion of the time of day.

Arguments:
- include_milliseconds: Specifies if the whole fractional part of the
second should be returned as nanoseconds. Defaults to `False`, meaning
it will only return the nanosecond part in the range 0-999.

> Example
Get the current nanosecond.

from Standard.Base import Time_Of_Day

example_nanosecond = Time_Of_Day.now.nanosecond
nanosecond : Integer
nanosecond self = @Builtin_Method "Time_Of_Day.nanosecond"
nanosecond : Boolean -> Integer
nanosecond self include_milliseconds=False =
self.nanosecond_builtin include_milliseconds

## Returns the first time within the `Time_Period` containing self.
start_of : Time_Period -> Time_Of_Day
Expand Down Expand Up @@ -247,6 +276,48 @@ type Time_Of_Day
to_date_time self date (zone=Time_Zone.system) =
Time_Utils.make_zoned_date_time date self zone


## Returns a requested time part as integer.
date_part : Time_Period -> Integer
date_part self (period : Time_Period) =
case period of
Time_Period.Day -> Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part.")
Time_Period.Hour -> self.hour
Time_Period.Minute -> self.minute
Time_Period.Second -> self.second
Time_Period.Millisecond -> self.millisecond
Time_Period.Microsecond -> self.microsecond
Time_Period.Nanosecond -> self.nanosecond

## Computes a time difference between the two times of day.

It returns an integer expressing how many periods fit between the two
times of day.

The difference will be positive if `end` is greater than `self`.

Arguments:
- end: A time of day to compute the difference from.
- period: The period to compute the difference in.
date_diff : Time_Of_Day -> Time_Period -> Integer
date_diff self (end : Time_Of_Day) (period : Time_Period) = case period of
Time_Period.Day ->
Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part to compute a difference in days.")
_ ->
Time_Utils.unit_time_difference period.to_java_unit self end

## Shifts the time of day by a specified period.

Arguments:
- amount: An integer specifying by how many periods to shift the time.
- period: The period by which to shift.
date_add : Integer -> Time_Period -> Time_Of_Day
date_add self (amount : Integer) (period : Time_Period) = case period of
Time_Period.Day ->
Error.throw (Illegal_Argument.Error "The Time_Of_Day does not have a day part to add days to.")
_ ->
Time_Utils.unit_time_add period.to_java_unit self amount

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

Expand Down Expand Up @@ -313,13 +384,13 @@ type Time_Of_Day
to_js_object self =
type_pair = ["type", "Time_Of_Day"]
cons_pair = ["constructor", "new"]
JS_Object.from_pairs [type_pair, cons_pair, ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond]]
JS_Object.from_pairs [type_pair, cons_pair, ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond include_milliseconds=True]]

## PRIVATE
Convert to a display representation of this Time_Of_Day.
to_display_text : Text
to_display_text self =
if self.nanosecond == 0 then self.format "HH:mm:ss" else
if self.nanosecond include_milliseconds=True == 0 then self.format "HH:mm:ss" else
self.format "HH:mm:ss.n"

## Format this time of day using the provided formatter pattern.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ type Column

Returns a column of `Integer` type.
@period Date_Time_Helpers.make_period_selector_for_column
date_part : Date_Period | Time_Period -> Column ! Invalid_Value_Type
date_part : Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_part self period =
Date_Time_Helpers.make_date_part_function self period simple_unary_op self.naming_helpers

Expand All @@ -1226,7 +1226,7 @@ type Column
differences in time calculations between backends, especially around
unusual events like DST.
@period Date_Time_Helpers.make_period_selector_for_column
date_diff : (Column | Date | Date_Time | Time_Of_Day) -> Date_Period | Time_Period -> Column
date_diff : (Column | Date | Date_Time | Time_Of_Day) -> Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_diff self end (period : Date_Period | Time_Period) =
Value_Type.expect_type self .is_date_or_time "date/time" <|
my_type = self.inferred_precise_value_type
Expand All @@ -1253,7 +1253,7 @@ type Column
differences in time calculations between backends, especially around
unusual events like DST.
@period Date_Time_Helpers.make_period_selector_for_column
date_add : (Column | Integer) -> Date_Period | Time_Period -> Column
date_add : (Column | Integer) -> Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_add self amount (period : Date_Period | Time_Period) =
Value_Type.expect_type self .is_date_or_time "date/time" <|
my_type = self.inferred_precise_value_type
Expand Down
10 changes: 7 additions & 3 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ type Column

Returns a column of `Integer` type.
@period Date_Time_Helpers.make_period_selector_for_column
date_part : Date_Period | Time_Period -> Column ! Invalid_Value_Type
date_part : Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_part self period =
Date_Time_Helpers.make_date_part_function self period simple_unary_op Naming_Helpers

Expand All @@ -1311,7 +1311,7 @@ type Column
differences in time calculations between backends, especially around
unusual events like DST.
@period Date_Time_Helpers.make_period_selector_for_column
date_diff : (Column | Date | Date_Time | Time_Of_Day) -> Date_Period | Time_Period -> Column
date_diff : (Column | Date | Date_Time | Time_Of_Day) -> Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_diff self end (period : Date_Period | Time_Period) =
Value_Type.expect_type self .is_date_or_time "date/time" <|
my_type = self.inferred_precise_value_type
Expand Down Expand Up @@ -1346,14 +1346,18 @@ type Column
differences in time calculations between backends, especially around
unusual events like DST.
@period Date_Time_Helpers.make_period_selector_for_column
date_add : (Column | Integer) -> Date_Period | Time_Period -> Column
date_add : (Column | Integer) -> Date_Period | Time_Period -> Column ! Invalid_Value_Type | Illegal_Argument
date_add self amount (period : Date_Period | Time_Period) =
Value_Type.expect_type self .is_date_or_time "date/time" <|
my_type = self.inferred_precise_value_type
Value_Type.expect_integer amount <|
Date_Time_Helpers.check_period_aligned_with_value_type my_type period <|
new_name = Naming_Helpers.function_name "date_add" [self, amount, period.to_display_text]
java_unit = period.to_java_unit
## Here we do not need a Time_Utils helper like in scalar
implementations of `date_add`, because the date coming
from the column will always be already converted into a
Java instance.
fn date amount =
java_unit.addTo date amount
run_binary_op self amount fn new_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,27 @@ public long second() {
return dateTime.getSecond();
}

@Builtin.Method(description = "Gets the nanosecond")
@Builtin.Method(description = "Gets the millisecond")
@CompilerDirectives.TruffleBoundary
public long nanosecond() {
return dateTime.getNano();
public long millisecond() {
return dateTime.getNano() / 1000_000;
}

@Builtin.Method(description = "Gets the microsecond")
@CompilerDirectives.TruffleBoundary
public long microsecond() {
return (dateTime.getNano() / 1000) % 1000;
}

@Builtin.Method(name = "nanosecond_builtin", description = "Gets the nanosecond")
@CompilerDirectives.TruffleBoundary
public long nanosecond(boolean includeMilliseconds) {
long nanos = dateTime.getNano();
if (includeMilliseconds) {
return nanos;
} else {
return nanos % 1000;
}
}

@Builtin.Method(name = "zone", description = "Gets the zone")
Expand Down
Loading

0 comments on commit 620cc36

Please sign in to comment.