Add day_of_week and day_of_year to Column and DB_Column (#10081)

- Adds support for getting the weekday as an integer (1 Monday - 7 Sunday - ISO standard).
- Add support for getting the day of year.
This commit is contained in:
James Dunkerley 2024-05-27 12:29:25 +01:00 committed by GitHub
parent 200da1ad50
commit ab4b1f0f35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 92 additions and 32 deletions

View File

@ -667,6 +667,7 @@
- [Added ability to save an existing Postgres connection as a Data Link in Enso - [Added ability to save an existing Postgres connection as a Data Link in Enso
Cloud.][9957] Cloud.][9957]
- [Improved `Table.union`.][9968] - [Improved `Table.union`.][9968]
- [Add `day_of_week` and `day_of_year` to `Column`.][10081]
[debug-shortcuts]: [debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -980,6 +981,7 @@
[9879]: https://github.com/enso-org/enso/pull/9879 [9879]: https://github.com/enso-org/enso/pull/9879
[9957]: https://github.com/enso-org/enso/pull/9957 [9957]: https://github.com/enso-org/enso/pull/9957
[9968]: https://github.com/enso-org/enso/pull/9968 [9968]: https://github.com/enso-org/enso/pull/9968
[10081]: https://github.com/enso-org/enso/pull/10081
#### Enso Compiler #### Enso Compiler

View File

@ -1520,6 +1520,29 @@ type DB_Column
day self = Value_Type.expect_has_date self <| day self = Value_Type.expect_has_date self <|
self.make_unary_op "day" self.make_unary_op "day"
## GROUP Standard.Base.DateTime
ICON date_and_time
Gets the day of the year as a number (1 - 366) from the date stored in
the column.
Applies only to columns that hold the `Date` or `Date_Time` types.
Returns a column of `Integer` type.
day_of_year : DB_Column ! Invalid_Value_Type
day_of_year self = Value_Type.expect_has_date self <|
self.make_unary_op "day_of_year"
## ALIAS weekday
GROUP Standard.Base.DateTime
ICON date_and_time
Gets the weekday as a number (1 - 7) from the date stored in the column.
Monday is 1, Tuesday is 2, ..., Sunday is 7.
Applies only to columns that hold the `Date` or `Date_Time` types.
Returns a column of `Integer` type.
day_of_week : DB_Column ! Invalid_Value_Type
day_of_week self = Value_Type.expect_has_date self <|
self.make_unary_op "day_of_week"
## GROUP Standard.Base.DateTime ## GROUP Standard.Base.DateTime
ICON time ICON time
Gets the hour as a number (0-23) from the time stored in the column. Gets the hour as a number (0-23) from the time stored in the column.

View File

@ -280,7 +280,7 @@ make_internal_generator_dialect =
stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"] stddev_pop = ["STDDEV_POP", Base_Generator.make_function "stddev_pop"]
stddev_samp = ["STDDEV_SAMP", Base_Generator.make_function "stddev_samp"] stddev_samp = ["STDDEV_SAMP", Base_Generator.make_function "stddev_samp"]
stats = [agg_median, agg_mode, agg_percentile, stddev_pop, stddev_samp] stats = [agg_median, agg_mode, agg_percentile, stddev_pop, stddev_samp]
date_ops = [make_extract_as_int "year", make_extract_as_int "quarter", make_extract_as_int "month", make_extract_as_int "week", make_extract_as_int "day", make_extract_as_int "hour", make_extract_as_int "minute", make_extract_fractional_as_int "second", make_extract_fractional_as_int "millisecond" modulus=1000, make_extract_fractional_as_int "microsecond" modulus=1000, ["date_add", make_date_add], ["date_diff", make_date_diff], ["date_trunc_to_day", make_date_trunc_to_day]] date_ops = [make_extract_as_int "year", make_extract_as_int "quarter", make_extract_as_int "month", make_extract_as_int "week", make_extract_as_int "day", make_extract_as_int "day_of_week" "isodow", make_extract_as_int "day_of_year" "doy", make_extract_as_int "hour", make_extract_as_int "minute", make_extract_fractional_as_int "second", make_extract_fractional_as_int "millisecond" modulus=1000, make_extract_fractional_as_int "microsecond" modulus=1000, ["date_add", make_date_add], ["date_diff", make_date_diff], ["date_trunc_to_day", make_date_trunc_to_day]]
special_overrides = [is_null, is_empty] special_overrides = [is_null, is_empty]
other = [["RUNTIME_ERROR", make_runtime_error_op]] other = [["RUNTIME_ERROR", make_runtime_error_op]]
my_mappings = text + counts + stats + first_last_aggregators + arith_extensions + bool + date_ops + special_overrides + other my_mappings = text + counts + stats + first_last_aggregators + arith_extensions + bool + date_ops + special_overrides + other

View File

@ -1523,6 +1523,29 @@ type Column
day self = Value_Type.expect_has_date self <| day self = Value_Type.expect_has_date self <|
apply_unary_operation self DatePartOperation.DAY_INSTANCE apply_unary_operation self DatePartOperation.DAY_INSTANCE
## GROUP Standard.Base.DateTime
ICON date_and_time
Gets the day of the year as a number (1 - 366) from the date stored in
the column.
Applies only to columns that hold the `Date` or `Date_Time` types.
Returns a column of `Integer` type.
day_of_year : Column ! Invalid_Value_Type
day_of_year self = Value_Type.expect_has_date self <|
apply_unary_operation self DatePartOperation.DAY_OF_YEAR_INSTANCE
## ALIAS weekday
GROUP Standard.Base.DateTime
ICON date_and_time
Gets the weekday as a number (1 - 7) from the date stored in the column.
Monday is 1, Tuesday is 2, ..., Sunday is 7.
Applies only to columns that hold the `Date` or `Date_Time` types.
Returns a column of `Integer` type.
day_of_week : Column ! Invalid_Value_Type
day_of_week self = Value_Type.expect_has_date self <|
apply_unary_operation self DatePartOperation.DAY_OF_WEEK_INSTANCE
## GROUP Standard.Base.DateTime ## GROUP Standard.Base.DateTime
ICON time ICON time
Gets the hour as a number (0-23) from the time stored in the column. Gets the hour as a number (0-23) from the time stored in the column.

View File

@ -31,6 +31,14 @@ public class DatePartOperation extends AbstractUnaryLongOperation {
public static final UnaryOperation DAY_INSTANCE = public static final UnaryOperation DAY_INSTANCE =
new DatePartOperation(DAY, ChronoField.DAY_OF_MONTH, false); new DatePartOperation(DAY, ChronoField.DAY_OF_MONTH, false);
public static final String DAY_OF_YEAR = "day_of_year";
public static final UnaryOperation DAY_OF_YEAR_INSTANCE =
new DatePartOperation(DAY_OF_YEAR, ChronoField.DAY_OF_YEAR, false);
public static final String DAY_OF_WEEK = "day_of_week";
public static final UnaryOperation DAY_OF_WEEK_INSTANCE =
new DatePartOperation(DAY_OF_WEEK, ChronoField.DAY_OF_WEEK, false);
public static final String HOUR = "hour"; public static final String HOUR = "hour";
public static final UnaryOperation HOUR_INSTANCE = public static final UnaryOperation HOUR_INSTANCE =
new DatePartOperation(HOUR, ChronoField.HOUR_OF_DAY, true); new DatePartOperation(HOUR, ChronoField.HOUR_OF_DAY, true);

View File

@ -22,9 +22,9 @@ type Data
setup create_connection_fn table_builder = Data.Value <| setup create_connection_fn table_builder = Data.Value <|
connection = create_connection_fn Nothing connection = create_connection_fn Nothing
dates = table_builder [["A", [Date.new 2020 12 31, Date.new 2024 2 29, Date.new 1990 1 1, Nothing]], ["X", [2020, 29, 1, 100]]] connection=connection dates = table_builder [["A", [Date.new 2020 12 31, Date.new 2024 2 29, Date.new 1990 1 1, Nothing, Date.new 2024 5 26]], ["X", [2020, 29, 1, 100, 99]]] connection=connection
times = table_builder [["A", [Time_Of_Day.new 23 59 59 millisecond=567 nanosecond=123, Time_Of_Day.new 2 30 44 nanosecond=1002000, Time_Of_Day.new 0 0 0, Nothing]], ["X", [2020, 29, 1, 100]]] connection=connection times = table_builder [["A", [Time_Of_Day.new 23 59 59 millisecond=567 nanosecond=123, Time_Of_Day.new 2 30 44 nanosecond=1002000, Time_Of_Day.new 0 0 0, Nothing]], ["X", [2020, 29, 1, 100]]] connection=connection
datetimes = table_builder [["A", [Date_Time.new 2020 12 31 23 59 59 millisecond=567 nanosecond=123, Date_Time.new 2024 2 29 2 30 44 nanosecond=1002000, Date_Time.new 1990 1 1 0 0 0, Nothing]], ["X", [2020, 29, 1, 100]]] connection=connection datetimes = table_builder [["A", [Date_Time.new 2020 12 31 23 59 59 millisecond=567 nanosecond=123, Date_Time.new 2024 2 29 2 30 44 nanosecond=1002000, Date_Time.new 1990 1 1 0 0 0, Nothing, Date_Time.new 2024 5 26 11 43 22]], ["X", [2020, 29, 1, 100, 99]]] connection=connection
[connection, dates, times, datetimes] [connection, dates, times, datetimes]
teardown self = teardown self =
@ -84,34 +84,38 @@ add_specs suite_builder setup =
table_builder cols = table_builder cols =
setup.table_builder cols connection=data.connection setup.table_builder cols connection=data.connection
group_builder.specify "should allow to get the year/month/day of a Date" <| group_builder.specify "should allow to get the year/month/day/day_of_year/day_of_week of a Date" <|
t = data.dates t = data.dates
a = t.at "A" a = t.at "A"
a.year . to_vector . should_equal [2020, 2024, 1990, Nothing] a.year . to_vector . should_equal [2020, 2024, 1990, Nothing, 2024]
a.month . to_vector . should_equal [12, 2, 1, Nothing] a.month . to_vector . should_equal [12, 2, 1, Nothing, 5]
a.day . to_vector . should_equal [31, 29, 1, Nothing] a.day . to_vector . should_equal [31, 29, 1, Nothing, 26]
[a.year, a.month, a.day].each c-> a.day_of_year . to_vector . should_equal [366, 60, 1, Nothing, 147]
a.day_of_week . to_vector . should_equal [4, 4, 1, Nothing, 7]
[a.year, a.month, a.day, a.day_of_year, a.day_of_week].each c->
Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <| Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <|
c.value_type.is_integer.should_be_true c.value_type.is_integer.should_be_true
c.value_type.is_integer.should_be_true c.value_type.is_integer.should_be_true
((a.year) == (t.at "X")).to_vector . should_equal [True, False, False, Nothing] ((a.year) == (t.at "X")).to_vector . should_equal [True, False, False, Nothing, False]
((a.month) == (t.at "X")).to_vector . should_equal [False, False, True, Nothing] ((a.month) == (t.at "X")).to_vector . should_equal [False, False, True, Nothing, False]
((a.day) == (t.at "X")).to_vector . should_equal [False, True, True, Nothing] ((a.day) == (t.at "X")).to_vector . should_equal [False, True, True, Nothing, False]
group_builder.specify "should allow to get the year/month/day of a Date_Time" <| group_builder.specify "should allow to get the year/month/day/day_of_year/day_of_week of a Date_Time" <|
t = data.datetimes t = data.datetimes
a = t.at "A" a = t.at "A"
a.year . to_vector . should_equal [2020, 2024, 1990, Nothing] a.year . to_vector . should_equal [2020, 2024, 1990, Nothing, 2024]
a.month . to_vector . should_equal [12, 2, 1, Nothing] a.month . to_vector . should_equal [12, 2, 1, Nothing, 5]
a.day . to_vector . should_equal [31, 29, 1, Nothing] a.day . to_vector . should_equal [31, 29, 1, Nothing, 26]
[a.year, a.month, a.day].each c-> a.day_of_year . to_vector . should_equal [366, 60, 1, Nothing, 147]
a.day_of_week . to_vector . should_equal [4, 4, 1, Nothing, 7]
[a.year, a.month, a.day, a.day_of_year, a.day_of_week].each c->
Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <| Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <|
c.value_type.is_integer.should_be_true c.value_type.is_integer.should_be_true
((a.year) == (t.at "X")).to_vector . should_equal [True, False, False, Nothing] ((a.year) == (t.at "X")).to_vector . should_equal [True, False, False, Nothing, False]
((a.month) == (t.at "X")).to_vector . should_equal [False, False, True, Nothing] ((a.month) == (t.at "X")).to_vector . should_equal [False, False, True, Nothing, False]
((a.day) == (t.at "X")).to_vector . should_equal [False, True, True, Nothing] ((a.day) == (t.at "X")).to_vector . should_equal [False, True, True, Nothing, False]
group_builder.specify "should allow to evaluate expressions with year/month/day" <| group_builder.specify "should allow to evaluate expressions with year/month/day" <|
t = table_builder [["A", [Date.new 2020 12 31, Date.new 2024 2 29, Date.new 1990 1 1, Nothing]], ["X", [0, 2, 1, 100]], ["B", [Date_Time.new 2020 10 31 23 59 59, Date_Time.new 2024 4 29 2 30 44, Date_Time.new 1990 10 1 0 0 0, Nothing]]] t = table_builder [["A", [Date.new 2020 12 31, Date.new 2024 2 29, Date.new 1990 1 1, Nothing]], ["X", [0, 2, 1, 100]], ["B", [Date_Time.new 2020 10 31 23 59 59, Date_Time.new 2024 4 29 2 30 44, Date_Time.new 1990 10 1 0 0 0, Nothing]]]
@ -136,13 +140,13 @@ add_specs suite_builder setup =
group_builder.specify "should allow to get hour/minute/second of a Date_Time" <| group_builder.specify "should allow to get hour/minute/second of a Date_Time" <|
a = data.datetimes.at "A" a = data.datetimes.at "A"
a.hour . to_vector . should_equal [23, 2, 0, Nothing] a.hour . to_vector . should_equal [23, 2, 0, Nothing, 11]
a.minute . to_vector . should_equal [59, 30, 0, Nothing] a.minute . to_vector . should_equal [59, 30, 0, Nothing, 43]
a.second . to_vector . should_equal [59, 44, 0, Nothing] a.second . to_vector . should_equal [59, 44, 0, Nothing, 22]
a.date_part Time_Period.Hour . to_vector . should_equal [23, 2, 0, Nothing] a.date_part Time_Period.Hour . to_vector . should_equal [23, 2, 0, Nothing, 11]
a.date_part Time_Period.Minute . to_vector . should_equal [59, 30, 0, Nothing] a.date_part Time_Period.Minute . to_vector . should_equal [59, 30, 0, Nothing, 43]
a.date_part Time_Period.Second . to_vector . should_equal [59, 44, 0, Nothing] a.date_part Time_Period.Second . to_vector . should_equal [59, 44, 0, Nothing, 22]
[a.hour, a.minute, a.second, a.date_part Time_Period.Hour, a.date_part Time_Period.Minute, a.date_part Time_Period.Second].each c-> [a.hour, a.minute, a.second, a.date_part Time_Period.Hour, a.date_part Time_Period.Minute, a.date_part Time_Period.Second].each c->
Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <| Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <|
@ -168,8 +172,8 @@ add_specs suite_builder setup =
group_builder.specify "should allow to get week/quarter of Date through date_part" <| group_builder.specify "should allow to get week/quarter of Date through date_part" <|
a = data.dates.at "A" a = data.dates.at "A"
a.date_part Date_Period.Quarter . to_vector . should_equal [4, 1, 1, Nothing] a.date_part Date_Period.Quarter . to_vector . should_equal [4, 1, 1, Nothing, 2]
a.date_part Date_Period.Week . to_vector . should_equal [53, 9, 1, Nothing] a.date_part Date_Period.Week . to_vector . should_equal [53, 9, 1, Nothing, 21]
[a.date_part Date_Period.Quarter, a.date_part Date_Period.Week].each c-> [a.date_part Date_Period.Quarter, a.date_part Date_Period.Week].each c->
Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <| Test.with_clue "The column "+c.name+" value type ("+c.value_type.to_display_text+") should be an integer: " <|
@ -177,13 +181,13 @@ add_specs suite_builder setup =
group_builder.specify "should allow to get various date_part of Date_Time" <| group_builder.specify "should allow to get various date_part of Date_Time" <|
a = data.datetimes.at "A" a = data.datetimes.at "A"
a.date_part Date_Period.Quarter . to_vector . should_equal [4, 1, 1, Nothing] a.date_part Date_Period.Quarter . to_vector . should_equal [4, 1, 1, Nothing, 2]
a.date_part Date_Period.Week . to_vector . should_equal [53, 9, 1, Nothing] a.date_part Date_Period.Week . to_vector . should_equal [53, 9, 1, Nothing, 21]
a.date_part Time_Period.Millisecond . to_vector . should_equal [567, 1, 0, Nothing] a.date_part Time_Period.Millisecond . to_vector . should_equal [567, 1, 0, Nothing, 0]
a.date_part Time_Period.Microsecond . to_vector . should_equal [0, 2, 0, Nothing] a.date_part Time_Period.Microsecond . to_vector . should_equal [0, 2, 0, Nothing, 0]
case setup.test_selection.supports_nanoseconds_in_time of case setup.test_selection.supports_nanoseconds_in_time of
True -> True ->
a.date_part Time_Period.Nanosecond . to_vector . should_equal [123, 0, 0, Nothing] a.date_part Time_Period.Nanosecond . to_vector . should_equal [123, 0, 0, Nothing, 0]
False -> False ->
a.date_part Time_Period.Nanosecond . should_fail_with Unsupported_Database_Operation a.date_part Time_Period.Nanosecond . should_fail_with Unsupported_Database_Operation