From d758a2350d58f94bbf02d1357eeaf3b013bce18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 6 Sep 2022 13:20:41 +0200 Subject: [PATCH 01/18] Adding Date periods, WIP for Date_Time! --- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 24 ++++++++++++ .../0.0.0-dev/src/Data/Time/Date_Time.enso | 26 +++++++++++++ .../0.0.0-dev/src/Data/Time/Time_Period.enso | 5 +++ .../main/java/org/enso/base/Time_Utils.java | 38 +++++++++++++++++-- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 2168ede5b37b..11c7ba1d14da 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -1,6 +1,7 @@ from Standard.Base import all import Standard.Base.Data.Time.Duration +import Standard.Base.Data.Time.Date_Period import Standard.Base.Polyglot from Standard.Base.Error.Common import Time_Error_Data @@ -8,6 +9,7 @@ from Standard.Base.Error.Common import Time_Error_Data 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.temporal.TemporalAdjusters ## Obtains the current date from the system clock in the system timezone. @@ -230,6 +232,28 @@ type Date day_of_week self = Day_Of_Week.from (Time_Utils.get_field_as_localdate self ChronoField.DAY_OF_WEEK) Day_Of_Week.Monday + ## Returns the first date within the `Date_Period` containing self. + start_of : Date_Period -> Date + start_of self period=Date_Period.Month = case period of + Date_Period.Year -> + Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfYear + Date_Period.Quarter -> + Time_Utils.quarter_start self + Date_Period.Month -> + Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfMonth + Date_Period.Day -> self + + ## Returns the last date within the `Date_Period` containing self. + end_of: Date_Period -> Date + end_of self period=Date_Period.Month = case self of + Date_Period.Year -> + Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear + Date_Period.Quarter -> + Time_Utils.quarter_end self + Date_Period.Month -> + Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth + Date_Period.Day -> self + ## ALIAS Date to Time Combine this date with time of day to create a point in time. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index 6cc94119f20b..18de68319751 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -1,6 +1,8 @@ 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.Error.Common import Time_Error polyglot java import java.time.format.DateTimeFormatter @@ -326,6 +328,30 @@ type Date_Time day_of_week self = Day_Of_Week.from (Time_Utils.get_field_as_zoneddatetime self ChronoField.DAY_OF_WEEK) Day_Of_Week.Monday + ## Returns the first date within the `Date_Period` containing self. + start_of : (Date_Period|Time_Period) -> Date_Time + start_of self period=Date_Period.Month = + case period of + Date_Period.Year -> + Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfYear + Date_Period.Quarter -> + Time_Utils.quarter_start self + Date_Period.Month -> + Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfMonth + Date_Period.Day -> self + + ## Returns the last date within the `Date_Period` containing self. + end_of: (Date_Period|Time_Period) -> Date_Time + end_of self period=Date_Period.Month = + case self of + Date_Period.Year -> + Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear + Date_Period.Quarter -> + Time_Utils.quarter_end self + Date_Period.Month -> + Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth + Date_Period.Day -> self + ## ALIAS Time to Date Convert this point in time to date, discarding the time of day diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso new file mode 100644 index 000000000000..78380680e5a3 --- /dev/null +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso @@ -0,0 +1,5 @@ +## Represents a period of time shorter than a day. +type Time_Period + Hour + Minute + Second diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index c7f914a4ac4a..042626c57a76 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -3,9 +3,7 @@ import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.TemporalAccessor; -import java.time.temporal.TemporalField; -import java.time.temporal.WeekFields; +import java.time.temporal.*; import java.util.Locale; /** Utils for standard library operations on Time. */ @@ -206,4 +204,38 @@ public static LocalTime parse_time(String text, String pattern, Locale locale) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); return (LocalTime.parse(text, formatter.withLocale(locale))); } + + public static void playground() { + TemporalAdjusters.firstDayOfYear(); + TemporalAdjusters.lastDayOfYear(); + + TemporalAdjusters.firstDayOfMonth(); + TemporalAdjusters.lastDayOfMonth(); + } + + public static Temporal apply_adjuster(Temporal date, TemporalAdjuster adjuster) { + return date.with(adjuster); + } + + public static Temporal quarter_start(Temporal temporal) { + int month = temporal.get(ChronoField.MONTH_OF_YEAR); + int quarter = (month - 1) / 3; + int firstMonth = quarter * 3 + 1; + return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth); + } + + public static Temporal quarter_end(Temporal temporal) { + int month = temporal.get(ChronoField.MONTH_OF_YEAR); + int quarter = (month - 1) / 3; + int lastMonth = quarter * 3 + 3; + return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); + } + + public static ZonedDateTime start_of_day(ZonedDateTime date) { + return date.truncatedTo(ChronoUnit.DAYS); + } + + public static ZonedDateTime end_of_day(ZonedDateTime date) { + return date.truncatedTo(ChronoUnit.DAYS).plusDays(1).minusNanos(1); + } } From 312c59e2f1efc0461206736f427b5ff47590e429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 6 Sep 2022 17:59:00 +0200 Subject: [PATCH 02/18] Initial implementation of start_of and end_of --- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 19 ++--------- .../0.0.0-dev/src/Data/Time/Date_Period.enso | 31 ++++++++++++++++++ .../0.0.0-dev/src/Data/Time/Date_Time.enso | 32 ++++++++----------- .../0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 8 +++++ .../0.0.0-dev/src/Data/Time/Time_Period.enso | 32 ++++++++++++++++++- .../main/java/org/enso/base/Time_Utils.java | 24 +++++++------- 6 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 11c7ba1d14da..35e0919916c3 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -9,7 +9,6 @@ from Standard.Base.Error.Common import Time_Error_Data 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.temporal.TemporalAdjusters ## Obtains the current date from the system clock in the system timezone. @@ -234,25 +233,11 @@ type Date ## Returns the first date within the `Date_Period` containing self. start_of : Date_Period -> Date - start_of self period=Date_Period.Month = case period of - Date_Period.Year -> - Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfYear - Date_Period.Quarter -> - Time_Utils.quarter_start self - Date_Period.Month -> - Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfMonth - Date_Period.Day -> self + start_of self period=Date_Period.Month = period.adjust_start self ## Returns the last date within the `Date_Period` containing self. end_of: Date_Period -> Date - end_of self period=Date_Period.Month = case self of - Date_Period.Year -> - Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear - Date_Period.Quarter -> - Time_Utils.quarter_end self - Date_Period.Month -> - Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth - Date_Period.Day -> self + end_of self period=Date_Period.Month = period.adjust_end self ## ALIAS Date to Time diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso new file mode 100644 index 000000000000..0421151c7f22 --- /dev/null +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso @@ -0,0 +1,31 @@ +from Standard.Base import all + +polyglot java import org.enso.base.Time_Utils +polyglot java import java.time.temporal.TemporalAdjusters + +## Represents a period of time longer on the scale of days (longer than a day). +type Date_Period + Year + Quarter + Month + + ## PRIVATE + This method could be replaced with matching on `Date_Period` supertype + if/when that is supported. + is_date_period : Boolean + is_date_period self = True + + ## PRIVATE + adjust_start : (Date | Date_Time) -> (Date | Date_Time) + adjust_start self date = case self of + Year -> Time_Utils.apply_adjuster date TemporalAdjusters.firstDayOfYear + Quarter -> Time_Utils.quarter_start date + Month -> + Time_Utils.apply_adjuster date TemporalAdjusters.firstDayOfMonth + + ## PRIVATE + adjust_end : (Date | Date_Time) -> (Date | Date_Time) + adjust_end self date = case self of + Year -> Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear + Quarter -> Time_Utils.quarter_end self + Month -> Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index 18de68319751..f39e5a733591 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -328,29 +328,23 @@ type Date_Time day_of_week self = Day_Of_Week.from (Time_Utils.get_field_as_zoneddatetime self ChronoField.DAY_OF_WEEK) Day_Of_Week.Monday - ## Returns the first date within the `Date_Period` containing self. + ## Returns the first date within the `Time_Period` or `Date_Period` + containing self. start_of : (Date_Period|Time_Period) -> Date_Time start_of self period=Date_Period.Month = - case period of - Date_Period.Year -> - Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfYear - Date_Period.Quarter -> - Time_Utils.quarter_start self - Date_Period.Month -> - Time_Utils.apply_adjuster self TemporalAdjusters.firstDayOfMonth - Date_Period.Day -> self - - ## Returns the last date within the `Date_Period` containing self. + adjusted = period.adjust_start self + case period.is_date_period of + True -> Time_Period.Day.adjust_start adjusted + False -> adjusted + + ## Returns the last date within the `Time_Period` or `Date_Period` + containing self. end_of: (Date_Period|Time_Period) -> Date_Time end_of self period=Date_Period.Month = - case self of - Date_Period.Year -> - Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear - Date_Period.Quarter -> - Time_Utils.quarter_end self - Date_Period.Month -> - Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth - Date_Period.Day -> self + adjusted = period.adjust_end self + case period.is_date_period of + True -> Time_Period.Day.adjust_end adjusted + False -> adjusted ## ALIAS Time to Date diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index bb5f56006249..e4f5c822a9d0 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -170,6 +170,14 @@ type Time_Of_Day nanosecond : Integer nanosecond self = @Builtin_Method "Time_Of_Day.nanosecond" + ## Returns the first time within the `Time_Period` containing self. + start_of : Time_Period -> Time_Of_Day + start_of self period=Time_Period.Day = period.adjust_start self + + ## Returns the last time within the `Time_Period` containing self. + end_of: Time_Period -> Time_Of_Day + end_of self period=Date_Period.Month = period.adjust_end self + ## Extracts the time as the number of seconds, from 0 to 24 * 60 * 60 - 1. > Example diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso index 78380680e5a3..5f63d369e564 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso @@ -1,5 +1,35 @@ -## Represents a period of time shorter than a day. +from Standard.Base import all + +polyglot java import org.enso.base.Time_Utils +polyglot java import java.time.temporal.ChronoUnit + +## Represents a period of time of a day or shorter. type Time_Period + Day Hour Minute Second + + ## PRIVATE + This method could be replaced with matching on `Date_Period` supertype + if/when that is supported. + is_date_period : Boolean + is_date_period self = False + + ## PRIVATE + to_java_unit : TemporalUnit + to_java_unit self = case self of + Day -> ChronoUnit.DAYS + Hour -> ChronoUnit.HOURS + Minute -> ChronoUnit.MINUTES + Second -> ChronoUnit.SECONDS + + ## PRIVATE + adjust_start : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time) + adjust_start self date = + Time_Utils.start_of_time_period date self.to_java_unit + + ## PRIVATE + adjust_end : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time) + adjust_end self date = + Time_Utils.end_of_time_period date self.to_java_unit diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index 042626c57a76..7e89f58edcf6 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -205,14 +205,6 @@ public static LocalTime parse_time(String text, String pattern, Locale locale) { return (LocalTime.parse(text, formatter.withLocale(locale))); } - public static void playground() { - TemporalAdjusters.firstDayOfYear(); - TemporalAdjusters.lastDayOfYear(); - - TemporalAdjusters.firstDayOfMonth(); - TemporalAdjusters.lastDayOfMonth(); - } - public static Temporal apply_adjuster(Temporal date, TemporalAdjuster adjuster) { return date.with(adjuster); } @@ -231,11 +223,19 @@ public static Temporal quarter_end(Temporal temporal) { return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); } - public static ZonedDateTime start_of_day(ZonedDateTime date) { - return date.truncatedTo(ChronoUnit.DAYS); + public static ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { + return date.truncatedTo(unit); + } + + public static LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { + return date.truncatedTo(unit); + } + + public static ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) { + return date.truncatedTo(unit).plus(1, unit).minusNanos(1); } - public static ZonedDateTime end_of_day(ZonedDateTime date) { - return date.truncatedTo(ChronoUnit.DAYS).plusDays(1).minusNanos(1); + public static LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { + return date.truncatedTo(unit).plus(1, unit).minusNanos(1); } } From d322d1af72c9823ed6adad2e34f3f9c2d963cb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 6 Sep 2022 22:25:55 +0200 Subject: [PATCH 03/18] added tests for Date+Month, some fixes --- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 4 +-- .../0.0.0-dev/src/Data/Time/Date_Period.enso | 12 ++++---- .../0.0.0-dev/src/Data/Time/Date_Time.enso | 2 +- .../0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 7 +++-- .../0.0.0-dev/src/Data/Time/Time_Period.enso | 8 +++--- .../main/java/org/enso/base/Time_Utils.java | 6 +++- test/Tests/src/Data/Time/Date_Spec.enso | 28 +++++++++++++++++++ 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 35e0919916c3..450ada503805 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -236,7 +236,7 @@ type Date start_of self period=Date_Period.Month = period.adjust_start self ## Returns the last date within the `Date_Period` containing self. - end_of: Date_Period -> Date + end_of : Date_Period -> Date end_of self period=Date_Period.Month = period.adjust_end self ## ALIAS Date to Time @@ -254,7 +254,7 @@ type Date example_to_time = Date.new 2020 2 3 . to_time Time_Of_Day.new Time_Zone.utc to_time : Time_Of_Day -> Time_Zone -> Date_Time - to_time self time_of_day (zone=Time_Zone.system) = self.to_time_builtin time_of_day zone + to_time self (time_of_day=Time_Of_Day.new) (zone=Time_Zone.system) = self.to_time_builtin time_of_day zone ## Add the specified amount of time to this instant to get another date. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso index 0421151c7f22..3741b7fdd45a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso @@ -5,9 +5,9 @@ polyglot java import java.time.temporal.TemporalAdjusters ## Represents a period of time longer on the scale of days (longer than a day). type Date_Period - Year - Quarter - Month + Year + Quarter + Month ## PRIVATE This method could be replaced with matching on `Date_Period` supertype @@ -26,6 +26,6 @@ type Date_Period ## PRIVATE adjust_end : (Date | Date_Time) -> (Date | Date_Time) adjust_end self date = case self of - Year -> Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfYear - Quarter -> Time_Utils.quarter_end self - Month -> Time_Utils.apply_adjuster self TemporalAdjusters.lastDayOfMonth + Year -> Time_Utils.apply_adjuster date TemporalAdjusters.lastDayOfYear + Quarter -> Time_Utils.quarter_end date + Month -> Time_Utils.apply_adjuster date TemporalAdjusters.lastDayOfMonth diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index f39e5a733591..c0fbdafe66ea 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -339,7 +339,7 @@ type Date_Time ## Returns the last date within the `Time_Period` or `Date_Period` containing self. - end_of: (Date_Period|Time_Period) -> Date_Time + end_of : (Date_Period|Time_Period) -> Date_Time end_of self period=Date_Period.Month = adjusted = period.adjust_end self case period.is_date_period of diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index e4f5c822a9d0..755ca2ec42c9 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -1,6 +1,7 @@ from Standard.Base import all import Standard.Base.Data.Time.Duration +import Standard.Base.Data.Time.Time_Period from Standard.Base.Error.Common import Time_Error polyglot java import java.time.format.DateTimeFormatter @@ -175,8 +176,8 @@ type Time_Of_Day start_of self period=Time_Period.Day = period.adjust_start self ## Returns the last time within the `Time_Period` containing self. - end_of: Time_Period -> Time_Of_Day - end_of self period=Date_Period.Month = period.adjust_end self + end_of : Time_Period -> Time_Of_Day + end_of self period=Time_Period.Day = period.adjust_end self ## Extracts the time as the number of seconds, from 0 to 24 * 60 * 60 - 1. @@ -201,7 +202,7 @@ type Time_Of_Day from Standard.Base import Time_Of_Day example_to_time = Time_Of_Day.new 12 30 . to_time (Date.new 2020) - to_time : Date -> Time_Zone -> Time + to_time : Date -> Time_Zone -> Date_Time to_time self date (zone=Time_Zone.system) = self.to_time_builtin date zone ## Add the specified amount of time to this instant to get a new instant. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso index 5f63d369e564..52ff65b86a3d 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso @@ -5,10 +5,10 @@ polyglot java import java.time.temporal.ChronoUnit ## Represents a period of time of a day or shorter. type Time_Period - Day - Hour - Minute - Second + Day + Hour + Minute + Second ## PRIVATE This method could be replaced with matching on `Date_Period` supertype diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index 7e89f58edcf6..a63f7f7bf43f 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -205,7 +205,11 @@ public static LocalTime parse_time(String text, String pattern, Locale locale) { return (LocalTime.parse(text, formatter.withLocale(locale))); } - public static Temporal apply_adjuster(Temporal date, TemporalAdjuster adjuster) { + public static LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { + return date.with(adjuster); + } + + public static ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) { return date.with(adjuster); } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 446879a74c45..3708ed9e848e 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -2,6 +2,8 @@ from Standard.Base import all import Standard.Base.Data.Text.Text_Sub_Range import Standard.Base.Data.Time.Duration +import Standard.Base.Data.Time.Date_Period +import Standard.Base.Data.Time.Time_Period from Standard.Base.Error.Common import Time_Error import Standard.Test @@ -134,6 +136,32 @@ spec_with name create_new_date parse_date = Date_Part_Spec.spec name create_new_date + Test.group name+" start_of/end_of period" <| + Test.specify "should allow to find start of a Date_Period containing the current date" <| + d1 = create_new_date 2022 9 12 + d1.start_of Date_Period.Year . should_equal (Date.new 2022 1 1) + d1.end_of Date_Period.Year . should_equal (Date.new 2022 12 31) + d1.start_of Date_Period.Month . should_equal (Date.new 2022 9 1) + d1.end_of Date_Period.Month . should_equal (Date.new 2022 9 30) + + d2 = create_new_date 2022 2 7 + d2.start_of Date_Period.Month . should_equal (Date.new 2022 2 1) + d2.end_of Date_Period.Month . should_equal (Date.new 2022 2 28) + + d3 = create_new_date 2020 2 17 + d3.start_of Date_Period.Year . should_equal (Date.new 2022 1 1) + d3.end_of Date_Period.Year . should_equal (Date.new 2022 12 31) + d3.start_of Date_Period.Month . should_equal (Date.new 2020 2 1) + d3.end_of Date_Period.Month . should_equal (Date.new 2020 2 29) + + d4 = create_new_date 1970 12 31 + d4.start_of Date_Period.Month . should_equal (Date.new 1970 12 1) + d4.end_of Date_Period.Month . should_equal (Date.new 1970 12 31) + + d5 = create_new_date 2040 1 1 + d5.start_of Date_Period.Month . should_equal (Date.new 2040 1 1) + d5.end_of Date_Period.Month . should_equal (Date.new 2040 1 31) + main = Test.Suite.run_main spec parseNormally x y = (Date.parse x y) . to_text From 45c4ff02e01e0d2314edf0c0bc278b24f61192dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Wed, 7 Sep 2022 17:59:29 +0200 Subject: [PATCH 04/18] Add more tests, add a fix for method dispatch, fix quarter computation --- .../main/java/org/enso/base/Time_Utils.java | 20 ++++++++++++++++- test/Tests/src/Data/Time/Date_Spec.enso | 22 +++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index a63f7f7bf43f..8c5fd491cbd9 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -217,7 +217,7 @@ public static Temporal quarter_start(Temporal temporal) { int month = temporal.get(ChronoField.MONTH_OF_YEAR); int quarter = (month - 1) / 3; int firstMonth = quarter * 3 + 1; - return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth); + return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth).with(TemporalAdjusters.firstDayOfMonth()); } public static Temporal quarter_end(Temporal temporal) { @@ -227,6 +227,24 @@ public static Temporal quarter_end(Temporal temporal) { return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); } + // These methods are added because the polyglot conversion only happens for exact types and not supertypes. + // TODO: check if that is the desired design, as it seems weird to have to add these methods. + public static LocalDate quarter_start(LocalDate date) { + return (LocalDate) quarter_start((Temporal) date); + } + + public static LocalDate quarter_end(LocalDate date) { + return (LocalDate) quarter_end((Temporal) date); + } + + public static ZonedDateTime quarter_start(ZonedDateTime date) { + return (ZonedDateTime) quarter_start((Temporal) date); + } + + public static ZonedDateTime quarter_end(ZonedDateTime date) { + return (ZonedDateTime) quarter_end((Temporal) date); + } + public static ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { return date.truncatedTo(unit); } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 3708ed9e848e..367e4ad2839a 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -141,27 +141,45 @@ spec_with name create_new_date parse_date = d1 = create_new_date 2022 9 12 d1.start_of Date_Period.Year . should_equal (Date.new 2022 1 1) d1.end_of Date_Period.Year . should_equal (Date.new 2022 12 31) + d1.start_of Date_Period.Quarter . should_equal (Date.new 2022 7 1) + d1.end_of Date_Period.Quarter . should_equal (Date.new 2022 9 30) d1.start_of Date_Period.Month . should_equal (Date.new 2022 9 1) d1.end_of Date_Period.Month . should_equal (Date.new 2022 9 30) d2 = create_new_date 2022 2 7 + d2.start_of Date_Period.Quarter . should_equal (Date.new 2022 1 1) + d2.end_of Date_Period.Quarter . should_equal (Date.new 2022 3 31) d2.start_of Date_Period.Month . should_equal (Date.new 2022 2 1) d2.end_of Date_Period.Month . should_equal (Date.new 2022 2 28) d3 = create_new_date 2020 2 17 - d3.start_of Date_Period.Year . should_equal (Date.new 2022 1 1) - d3.end_of Date_Period.Year . should_equal (Date.new 2022 12 31) + d3.start_of Date_Period.Year . should_equal (Date.new 2020 1 1) + d3.end_of Date_Period.Year . should_equal (Date.new 2020 12 31) d3.start_of Date_Period.Month . should_equal (Date.new 2020 2 1) d3.end_of Date_Period.Month . should_equal (Date.new 2020 2 29) d4 = create_new_date 1970 12 31 + d4.start_of Date_Period.Year . should_equal (Date.new 1970 1 1) + d4.end_of Date_Period.Year . should_equal (Date.new 1970 12 31) + d4.start_of Date_Period.Quarter . should_equal (Date.new 1970 10 1) + d4.end_of Date_Period.Quarter . should_equal (Date.new 1970 12 31) d4.start_of Date_Period.Month . should_equal (Date.new 1970 12 1) d4.end_of Date_Period.Month . should_equal (Date.new 1970 12 31) d5 = create_new_date 2040 1 1 + d5.start_of Date_Period.Year . should_equal (Date.new 2040 1 1) + d5.end_of Date_Period.Year . should_equal (Date.new 2040 12 31) + d5.start_of Date_Period.Quarter . should_equal (Date.new 2040 1 1) + d5.end_of Date_Period.Quarter . should_equal (Date.new 2040 3 31) d5.start_of Date_Period.Month . should_equal (Date.new 2040 1 1) d5.end_of Date_Period.Month . should_equal (Date.new 2040 1 31) + (create_new_date 2000 7 1).start_of Date_Period.Quarter . should_equal (Date.new 2000 7 1) + (create_new_date 2000 6 30).start_of Date_Period.Quarter . should_equal (Date.new 2000 4 1) + + (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) + main = Test.Suite.run_main spec parseNormally x y = (Date.parse x y) . to_text From 32ce02184735c68b8f01e26af243f1382dfe15af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Thu, 8 Sep 2022 18:05:17 +0200 Subject: [PATCH 05/18] Rework how utils are organized due to Polyglot overloading limitations --- build.sbt | 29 ++++---- .../0.0.0-dev/src/Data/Time/Date_Period.enso | 20 +++-- .../0.0.0-dev/src/Data/Time/Time_Period.enso | 7 +- .../main/java/org/enso/base/Time_Utils.java | 73 +++++-------------- .../org/enso/base/time/Date_Period_Utils.java | 21 ++++++ .../org/enso/base/time/Date_Time_Utils.java | 29 ++++++++ .../java/org/enso/base/time/Date_Utils.java | 20 +++++ .../org/enso/base/time/TimeUtilsBase.java | 3 + .../org/enso/base/time/Time_Of_Day_Utils.java | 16 ++++ test/Tests/src/Data/Time/Date_Spec.enso | 11 ++- test/Tests/src/Data/Time/Date_Time_Spec.enso | 46 ++++++++++++ 11 files changed, 192 insertions(+), 83 deletions(-) create mode 100644 std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java create mode 100644 std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java create mode 100644 std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java create mode 100644 std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java create mode 100644 std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java diff --git a/build.sbt b/build.sbt index f5a89af8e4d4..50023c41be27 100644 --- a/build.sbt +++ b/build.sbt @@ -5,8 +5,11 @@ import sbt.Keys.{libraryDependencies, scalacOptions} import sbt.addCompilerPlugin import sbt.complete.DefaultParsers._ import sbt.complete.Parser -import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject} -import src.main.scala.licenses.{DistributionDescription, SBTDistributionComponent} +import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} +import src.main.scala.licenses.{ + DistributionDescription, + SBTDistributionComponent +} import java.io.File @@ -14,9 +17,9 @@ import java.io.File // === Global Configuration =================================================== // ============================================================================ -val scalacVersion = "2.13.8" -val graalVersion = "21.3.0" -val javaVersion = "11" +val scalacVersion = "2.13.8" +val graalVersion = "21.3.0" +val javaVersion = "11" val defaultDevEnsoVersion = "0.0.0-dev" val ensoVersion = sys.env.getOrElse( "ENSO_VERSION", @@ -713,11 +716,11 @@ lazy val `profiling-utils` = project "org.netbeans.api" % "org-netbeans-modules-sampler" % netbeansApiVersion exclude ("org.netbeans.api", "org-openide-loaders") exclude ("org.netbeans.api", "org-openide-nodes") - exclude("org.netbeans.api", "org-netbeans-api-progress-nb") - exclude("org.netbeans.api", "org-netbeans-api-progress") - exclude("org.netbeans.api", "org-openide-util-lookup") - exclude("org.netbeans.api", "org-openide-util") - exclude("org.netbeans.api", "org-openide-dialogs") + exclude ("org.netbeans.api", "org-netbeans-api-progress-nb") + exclude ("org.netbeans.api", "org-netbeans-api-progress") + exclude ("org.netbeans.api", "org-openide-util-lookup") + exclude ("org.netbeans.api", "org-openide-util") + exclude ("org.netbeans.api", "org-openide-dialogs") exclude ("org.netbeans.api", "org-openide-filesystems") exclude ("org.netbeans.api", "org-openide-util-ui") exclude ("org.netbeans.api", "org-openide-awt") @@ -1007,7 +1010,6 @@ val truffleRunOptions = if (java.lang.Boolean.getBoolean("bench.compileOnly")) { ) } - val truffleRunOptionsSettings = Seq( fork := true, javaOptions ++= truffleRunOptions @@ -1744,7 +1746,8 @@ lazy val `std-base` = project Compile / packageBin / artifactPath := `base-polyglot-root` / "std-base.jar", libraryDependencies ++= Seq( - "com.ibm.icu" % "icu4j" % icuVersion + "com.ibm.icu" % "icu4j" % icuVersion, + "org.graalvm.truffle" % "truffle-api" % graalVersion % "provided" ), Compile / packageBin := Def.task { val result = (Compile / packageBin).value @@ -1767,7 +1770,7 @@ lazy val `std-table` = project Compile / packageBin / artifactPath := `table-polyglot-root` / "std-table.jar", libraryDependencies ++= Seq( - "com.ibm.icu" % "icu4j" % icuVersion % "provided", + "com.ibm.icu" % "icu4j" % icuVersion % "provided", "com.univocity" % "univocity-parsers" % "2.9.1", "org.apache.poi" % "poi-ooxml" % "5.2.2", "org.apache.xmlbeans" % "xmlbeans" % "5.1.0", diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso index 3741b7fdd45a..eda83399cb82 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso @@ -1,6 +1,7 @@ from Standard.Base import all -polyglot java import org.enso.base.Time_Utils +polyglot java import org.enso.base.time.Date_Utils +polyglot java import org.enso.base.time.Date_Time_Utils polyglot java import java.time.temporal.TemporalAdjusters ## Represents a period of time longer on the scale of days (longer than a day). @@ -18,14 +19,19 @@ type Date_Period ## PRIVATE adjust_start : (Date | Date_Time) -> (Date | Date_Time) adjust_start self date = case self of - Year -> Time_Utils.apply_adjuster date TemporalAdjusters.firstDayOfYear - Quarter -> Time_Utils.quarter_start date + Year -> + (Time_Utils.utils_for date).apply_adjuster_datetime date TemporalAdjusters.firstDayOfYear + Quarter -> + (Time_Utils.utils_for date).quarter_start_datetime date Month -> - Time_Utils.apply_adjuster date TemporalAdjusters.firstDayOfMonth + (Time_Utils.utils_for date).apply_adjuster_datetime date TemporalAdjusters.firstDayOfMonth ## PRIVATE adjust_end : (Date | Date_Time) -> (Date | Date_Time) adjust_end self date = case self of - Year -> Time_Utils.apply_adjuster date TemporalAdjusters.lastDayOfYear - Quarter -> Time_Utils.quarter_end date - Month -> Time_Utils.apply_adjuster date TemporalAdjusters.lastDayOfMonth + Year -> + (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.lastDayOfYear + Quarter -> + (Time_Utils.utils_for date).quarter_end date + Month -> + (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.lastDayOfMonth diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso index 52ff65b86a3d..78ff7a785c2b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso @@ -1,6 +1,7 @@ from Standard.Base import all -polyglot java import org.enso.base.Time_Utils +polyglot java import org.enso.base.time.Time_Of_Day_Utils +polyglot java import org.enso.base.time.Date_Time_Utils polyglot java import java.time.temporal.ChronoUnit ## Represents a period of time of a day or shorter. @@ -27,9 +28,9 @@ type Time_Period ## PRIVATE adjust_start : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time) adjust_start self date = - Time_Utils.start_of_time_period date self.to_java_unit + (Time_Utils.utils_for date).start_of_time_period date self.to_java_unit ## PRIVATE adjust_end : (Time_Of_Day | Date_Time) -> (Time_Of_Day | Date_Time) adjust_end self date = - Time_Utils.end_of_time_period date self.to_java_unit + (Time_Utils.utils_for date).end_of_time_period date self.to_java_unit diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index 8c5fd491cbd9..da51ccd6668e 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -1,5 +1,11 @@ package org.enso.base; +import org.enso.base.time.Date_Time_Utils; +import org.enso.base.time.Date_Utils; +import org.enso.base.time.TimeUtilsBase; +import org.enso.base.time.Time_Of_Day_Utils; +import org.graalvm.polyglot.Value; + import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -205,59 +211,18 @@ public static LocalTime parse_time(String text, String pattern, Locale locale) { return (LocalTime.parse(text, formatter.withLocale(locale))); } - public static LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { - return date.with(adjuster); - } - - public static ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) { - return date.with(adjuster); - } - - public static Temporal quarter_start(Temporal temporal) { - int month = temporal.get(ChronoField.MONTH_OF_YEAR); - int quarter = (month - 1) / 3; - int firstMonth = quarter * 3 + 1; - return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth).with(TemporalAdjusters.firstDayOfMonth()); - } - - public static Temporal quarter_end(Temporal temporal) { - int month = temporal.get(ChronoField.MONTH_OF_YEAR); - int quarter = (month - 1) / 3; - int lastMonth = quarter * 3 + 3; - return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); - } - - // These methods are added because the polyglot conversion only happens for exact types and not supertypes. - // TODO: check if that is the desired design, as it seems weird to have to add these methods. - public static LocalDate quarter_start(LocalDate date) { - return (LocalDate) quarter_start((Temporal) date); - } - - public static LocalDate quarter_end(LocalDate date) { - return (LocalDate) quarter_end((Temporal) date); - } - - public static ZonedDateTime quarter_start(ZonedDateTime date) { - return (ZonedDateTime) quarter_start((Temporal) date); - } - - public static ZonedDateTime quarter_end(ZonedDateTime date) { - return (ZonedDateTime) quarter_end((Temporal) date); - } - - public static ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { - return date.truncatedTo(unit); - } - - public static LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { - return date.truncatedTo(unit); - } - - public static ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) { - return date.truncatedTo(unit).plus(1, unit).minusNanos(1); - } - - public static LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { - return date.truncatedTo(unit).plus(1, unit).minusNanos(1); + /** Normally this method could be done in Enso by pattern matching, + * but currently matching on Time types is not supported, so this is a workaround. + * + * TODO once the related issue is fixed, this workaround may be replaced with pattern matching in Enso; + * the related Pivotal issue: https://www.pivotaltracker.com/story/show/183219169 + */ + public static TimeUtilsBase utils_for(Value value) { + boolean isDate = value.isDate(); + boolean isTime = value.isTime(); + if (isDate && isTime) return Date_Time_Utils.INSTANCE; + if (isDate) return Date_Utils.INSTANCE; + if (isTime) return Time_Of_Day_Utils.INSTANCE; + throw new IllegalArgumentException("Unexpected argument type: " + value); } } diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java new file mode 100644 index 000000000000..81822b1df031 --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java @@ -0,0 +1,21 @@ +package org.enso.base.time; + +import java.time.temporal.ChronoField; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAdjusters; + +public class Date_Period_Utils implements TimeUtilsBase { + public static Temporal quarter_start(Temporal temporal) { + int month = temporal.get(ChronoField.MONTH_OF_YEAR); + int quarter = (month - 1) / 3; + int firstMonth = quarter * 3 + 1; + return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth).with(TemporalAdjusters.firstDayOfMonth()); + } + + public static Temporal quarter_end(Temporal temporal) { + int month = temporal.get(ChronoField.MONTH_OF_YEAR); + int quarter = (month - 1) / 3; + int lastMonth = quarter * 3 + 3; + return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); + } +} diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java new file mode 100644 index 000000000000..cd96cdc9e0e6 --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java @@ -0,0 +1,29 @@ +package org.enso.base.time; + +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalUnit; + +public class Date_Time_Utils implements TimeUtilsBase { + public static final Date_Time_Utils INSTANCE = new Date_Time_Utils(); + + public static ZonedDateTime quarter_start(ZonedDateTime date) { + return (ZonedDateTime) Date_Period_Utils.quarter_start(date); + } + + public static ZonedDateTime quarter_end(ZonedDateTime date) { + return (ZonedDateTime) Date_Period_Utils.quarter_end(date); + } + + public static ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { + return date.truncatedTo(unit); + } + + public static ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) { + return date.truncatedTo(unit).plus(1, unit).minusNanos(1); + } + + public static ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) { + return date.with(adjuster); + } +} diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java new file mode 100644 index 000000000000..f248656b7516 --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java @@ -0,0 +1,20 @@ +package org.enso.base.time; + +import java.time.LocalDate; +import java.time.temporal.TemporalAdjuster; + +public class Date_Utils implements TimeUtilsBase { + public static final Date_Utils INSTANCE = new Date_Utils(); + + public static LocalDate quarter_start(LocalDate date) { + return (LocalDate) Date_Period_Utils.quarter_start(date); + } + + public static LocalDate quarter_end(LocalDate date) { + return (LocalDate) Date_Period_Utils.quarter_end(date); + } + + public static LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { + return date.with(adjuster); + } +} diff --git a/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java b/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java new file mode 100644 index 000000000000..49770e687040 --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java @@ -0,0 +1,3 @@ +package org.enso.base.time; + +public interface TimeUtilsBase {} diff --git a/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java new file mode 100644 index 000000000000..73c05aca1d38 --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java @@ -0,0 +1,16 @@ +package org.enso.base.time; + +import java.time.LocalTime; +import java.time.temporal.TemporalUnit; + +public class Time_Of_Day_Utils implements TimeUtilsBase { + public static final Time_Of_Day_Utils INSTANCE = new Time_Of_Day_Utils(); + + public static LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { + return date.truncatedTo(unit); + } + + public static LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { + return date.truncatedTo(unit).plus(1, unit).minusNanos(1); + } +} diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 367e4ad2839a..5d0dc0f62c5a 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -1,9 +1,8 @@ from Standard.Base import all import Standard.Base.Data.Text.Text_Sub_Range -import Standard.Base.Data.Time.Duration import Standard.Base.Data.Time.Date_Period -import Standard.Base.Data.Time.Time_Period +import Standard.Base.Data.Time.Duration from Standard.Base.Error.Common import Time_Error import Standard.Test @@ -134,10 +133,7 @@ spec_with name create_new_date parse_date = date_1>date_2 . should_be_true date_1time_2 . should_be_true time_1 Date: Fri, 9 Sep 2022 12:34:43 +0200 Subject: [PATCH 06/18] fix utils resolution --- .../Base/0.0.0-dev/src/Data/Time/Date_Period.enso | 9 ++++----- .../Base/0.0.0-dev/src/Data/Time/Time_Period.enso | 3 +-- .../main/java/org/enso/base/time/Date_Time_Utils.java | 10 +++++----- .../src/main/java/org/enso/base/time/Date_Utils.java | 6 +++--- .../java/org/enso/base/time/Time_Of_Day_Utils.java | 4 ++-- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso index eda83399cb82..0d1bcdc72104 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso @@ -1,7 +1,6 @@ from Standard.Base import all -polyglot java import org.enso.base.time.Date_Utils -polyglot java import org.enso.base.time.Date_Time_Utils +polyglot java import org.enso.base.Time_Utils polyglot java import java.time.temporal.TemporalAdjusters ## Represents a period of time longer on the scale of days (longer than a day). @@ -20,11 +19,11 @@ type Date_Period adjust_start : (Date | Date_Time) -> (Date | Date_Time) adjust_start self date = case self of Year -> - (Time_Utils.utils_for date).apply_adjuster_datetime date TemporalAdjusters.firstDayOfYear + (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.firstDayOfYear Quarter -> - (Time_Utils.utils_for date).quarter_start_datetime date + (Time_Utils.utils_for date).quarter_start date Month -> - (Time_Utils.utils_for date).apply_adjuster_datetime date TemporalAdjusters.firstDayOfMonth + (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.firstDayOfMonth ## PRIVATE adjust_end : (Date | Date_Time) -> (Date | Date_Time) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso index 78ff7a785c2b..881493fbb098 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Period.enso @@ -1,7 +1,6 @@ from Standard.Base import all -polyglot java import org.enso.base.time.Time_Of_Day_Utils -polyglot java import org.enso.base.time.Date_Time_Utils +polyglot java import org.enso.base.Time_Utils polyglot java import java.time.temporal.ChronoUnit ## Represents a period of time of a day or shorter. diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java index cd96cdc9e0e6..9066ade74a6e 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java @@ -7,23 +7,23 @@ public class Date_Time_Utils implements TimeUtilsBase { public static final Date_Time_Utils INSTANCE = new Date_Time_Utils(); - public static ZonedDateTime quarter_start(ZonedDateTime date) { + public ZonedDateTime quarter_start(ZonedDateTime date) { return (ZonedDateTime) Date_Period_Utils.quarter_start(date); } - public static ZonedDateTime quarter_end(ZonedDateTime date) { + public ZonedDateTime quarter_end(ZonedDateTime date) { return (ZonedDateTime) Date_Period_Utils.quarter_end(date); } - public static ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { + public ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { return date.truncatedTo(unit); } - public static ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) { + public ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) { return date.truncatedTo(unit).plus(1, unit).minusNanos(1); } - public static ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) { + public ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) { return date.with(adjuster); } } diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java index f248656b7516..9605a5d0360a 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java @@ -6,15 +6,15 @@ public class Date_Utils implements TimeUtilsBase { public static final Date_Utils INSTANCE = new Date_Utils(); - public static LocalDate quarter_start(LocalDate date) { + public LocalDate quarter_start(LocalDate date) { return (LocalDate) Date_Period_Utils.quarter_start(date); } - public static LocalDate quarter_end(LocalDate date) { + public LocalDate quarter_end(LocalDate date) { return (LocalDate) Date_Period_Utils.quarter_end(date); } - public static LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { + public LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { return date.with(adjuster); } } diff --git a/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java index 73c05aca1d38..ca0d4a741210 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java @@ -6,11 +6,11 @@ public class Time_Of_Day_Utils implements TimeUtilsBase { public static final Time_Of_Day_Utils INSTANCE = new Time_Of_Day_Utils(); - public static LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { + public LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { return date.truncatedTo(unit); } - public static LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { + public LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { return date.truncatedTo(unit).plus(1, unit).minusNanos(1); } } From 959de5289ea861abe72d7c8228d5dff8ff1bb545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 9 Sep 2022 14:13:05 +0200 Subject: [PATCH 07/18] Add Time_Period tests for Date_Time --- test/Tests/src/Data/Time/Date_Time_Spec.enso | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 0acb97ce88ee..1022070b9173 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -344,6 +344,37 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= (create_new_datetime 2000 7 1 16 50).end_of Date_Period.Quarter . should_equal (Date_Time.new 2000 9 30 23 59 59 max_nanos) (create_new_datetime 2000 6 30 17 40).end_of Date_Period.Quarter . should_equal (Date_Time.new 2000 6 30 23 59 59 max_nanos) + Test.specify "should allow to find start/end of a Time_Period containing the current datetime" <| + d1 = create_new_datetime 2022 9 12 15 37 58 123456789 + d1.start_of Time_Period.Day . should_equal (Date_Time.new 2022 9 12) + d1.end_of Time_Period.Day . should_equal (Date_Time.new 2022 9 12 23 59 59 max_nanos) + d1.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 9 12 15 0 0 0) + d1.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 9 12 15 59 59 max_nanos) + d1.start_of Time_Period.Minute . should_equal (Date_Time.new 2022 9 12 15 37 0 0) + d1.end_of Time_Period.Minute . should_equal (Date_Time.new 2022 9 12 15 37 59 max_nanos) + d1.start_of Time_Period.Second . should_equal (Date_Time.new 2022 9 12 15 37 58 0) + d1.end_of Time_Period.Second . should_equal (Date_Time.new 2022 9 12 15 37 58 max_nanos) + + d2 = create_new_datetime 1970 1 1 0 0 0 + d2.start_of Time_Period.Day . should_equal (Date_Time.new 1970) + d2.end_of Time_Period.Day . should_equal (Date_Time.new 1970 1 1 23 59 59 max_nanos) + d2.start_of Time_Period.Hour . should_equal (Date_Time.new 1970 1 1 0 0 0 0) + d2.end_of Time_Period.Hour . should_equal (Date_Time.new 1970 1 1 0 59 59 max_nanos) + d2.start_of Time_Period.Minute . should_equal (Date_Time.new 1970 1 1 0 0 0 0) + d2.end_of Time_Period.Minute . should_equal (Date_Time.new 1970 1 1 0 0 59 max_nanos) + d2.start_of Time_Period.Second . should_equal (Date_Time.new 1970 1 1 0 0 0 0) + d2.end_of Time_Period.Second . should_equal (Date_Time.new 1970 1 1 0 0 0 max_nanos) + + d3 = create_new_datetime 2100 12 31 23 59 59 max_nanos + d3.start_of Time_Period.Day . should_equal (Date_Time.new 2100 12 31) + d3.end_of Time_Period.Day . should_equal (Date_Time.new 2100 12 31 23 59 59 max_nanos) + d3.start_of Time_Period.Hour . should_equal (Date_Time.new 2100 12 31 23 0 0 0) + d3.end_of Time_Period.Hour . should_equal (Date_Time.new 2100 12 31 23 59 59 max_nanos) + d3.start_of Time_Period.Minute . should_equal (Date_Time.new 2100 12 31 23 59 0 0) + d3.end_of Time_Period.Minute . should_equal (Date_Time.new 2100 12 31 23 59 59 max_nanos) + d3.start_of Time_Period.Second . should_equal (Date_Time.new 2100 12 31 23 59 59 0) + d3.end_of Time_Period.Second . should_equal (Date_Time.new 2100 12 31 23 59 59 max_nanos) + Date_Part_Spec.spec name create_new_datetime js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Time_Zone.system = From e0536df419364a236b9e27b69b93d5338383e3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 9 Sep 2022 14:22:51 +0200 Subject: [PATCH 08/18] Add Time_Of_Day tests, fix --- .../org/enso/base/time/Time_Of_Day_Utils.java | 5 ++- .../Tests/src/Data/Time/Time_Of_Day_Spec.enso | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java index ca0d4a741210..b54d5be467b1 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Time_Of_Day_Utils.java @@ -1,6 +1,7 @@ package org.enso.base.time; import java.time.LocalTime; +import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; public class Time_Of_Day_Utils implements TimeUtilsBase { @@ -11,6 +12,8 @@ public LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) { } public LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) { - return date.truncatedTo(unit).plus(1, unit).minusNanos(1); + LocalTime truncated = date.truncatedTo(unit); + LocalTime adjusted = unit.equals(ChronoUnit.DAYS) ? truncated : truncated.plus(1, unit); + return adjusted.minusNanos(1); } } diff --git a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso index 369b8e10a610..be0c53e18e58 100644 --- a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso @@ -1,5 +1,6 @@ from Standard.Base import all import Standard.Base.Data.Time.Duration +import Standard.Base.Data.Time.Time_Period from Standard.Base.Error.Common import Time_Error_Data import Standard.Test @@ -116,6 +117,39 @@ specWith name create_new_time parse_time = time_1>time_2 . should_be_true time_1 Date: Fri, 9 Sep 2022 18:04:29 +0200 Subject: [PATCH 09/18] Add offset, test period handling near DST changes. Some changes not working yet - WIP --- .../0.0.0-dev/src/Data/Time/Date_Time.enso | 7 +++ .../runtime/data/EnsoTimeZone.java | 6 ++ .../main/java/org/enso/base/Time_Utils.java | 4 ++ test/Tests/src/Data/Time/Date_Time_Spec.enso | 63 +++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index c0fbdafe66ea..cc0a5e418fca 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -246,6 +246,13 @@ type Date_Time zone : Time_Zone zone self = @Builtin_Method "Date_Time.zone" + ## PRIVATE + Get the offset from UTC/Greenwich associated with this time. + + Currently this function is used only for testing. + offset : ZoneOffset + offset self = Time_Utils.get_datetime_offset self + ## Return the number of seconds from the Unix epoch. > Example diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeZone.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeZone.java index 06ac036d6f15..f062bc2c9619 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeZone.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoTimeZone.java @@ -14,6 +14,7 @@ import java.time.DateTimeException; import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.time.zone.ZoneRulesException; @ExportLibrary(InteropLibrary.class) @@ -54,6 +55,11 @@ public static EnsoTimeZone system() { return new EnsoTimeZone(ZoneId.systemDefault()); } + @Builtin.Method(description = "Return the text representation of this timezone.") + public Text toText() { + return Text.create(zone.toString()); + } + @ExportMessage boolean isTimeZone() { return true; diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index da51ccd6668e..b63f468e106d 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -225,4 +225,8 @@ public static TimeUtilsBase utils_for(Value value) { if (isTime) return Time_Of_Day_Utils.INSTANCE; throw new IllegalArgumentException("Unexpected argument type: " + value); } + + public static ZoneOffset get_datetime_offset(ZonedDateTime datetime) { + return datetime.getOffset(); + } } diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 1022070b9173..6743b295a24e 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -9,6 +9,7 @@ import project.Data.Time.Date_Part_Spec polyglot java import java.time.ZonedDateTime polyglot java import java.time.LocalDateTime +polyglot java import java.time.ZoneOffset polyglot java import java.time.format.DateTimeFormatter spec = @@ -375,6 +376,68 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= d3.start_of Time_Period.Second . should_equal (Date_Time.new 2100 12 31 23 59 59 0) d3.end_of Time_Period.Second . should_equal (Date_Time.new 2100 12 31 23 59 59 max_nanos) + offset_1_h = ZoneOffset.ofTotalSeconds 3600 + offset_2_h = ZoneOffset.ofTotalSeconds 2*3600 + Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near DST switches" <| + tz = Time_Zone.parse "Europe/Warsaw" + 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 + 1.hour) . should_equal d2 + + check_dates_spring date = + date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 3 27 0 0 0 0 tz) + date.end_of Time_Period.Day . should_equal (Date_Time.new 2022 3 27 23 59 59 max_nanos tz) + + date.start_of Date_Period.Month . should_equal (Date_Time.new 2022 3 1 0 0 0 0 tz) + date.end_of Date_Period.Month . should_equal (Date_Time.new 2022 3 31 23 59 59 max_nanos tz) + + check_dates_spring d1 + check_dates_spring d2 + + d1.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 1 0 0 0 tz) + d1.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 1 59 59 max_nanos tz) + d1.start_of Time_Period.Minute . should_equal (Date_Time.new 2022 3 27 1 34 0 0 tz) + d1.end_of Time_Period.Minute . should_equal (Date_Time.new 2022 3 27 1 34 59 max_nanos tz) + + d2.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 3 0 0 0 tz) + d2.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 3 59 59 max_nanos tz) + + d3 = create_new_datetime 2022 10 30 2 30 15 0 tz + d4 = d3 + 1.hour + IO.println d3 + IO.println d4 + + d3.hour . should_equal 2 + d4.hour . should_equal 2 + d3.minute . should_equal 30 + d4.minute . should_equal 30 + d3.offset . should_equal offset_2_h + d4.offset . should_equal offset_1_h + + check_dates_autumn date = + date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 10 30 0 0 0 0 tz) + date.end_of Time_Period.Day . should_equal (Date_Time.new 2022 10 30 23 59 59 max_nanos tz) + + date.start_of Date_Period.Month . should_equal (Date_Time.new 2022 10 1 0 0 0 0 tz) + date.end_of Date_Period.Month . should_equal (Date_Time.new 2022 10 31 23 59 59 max_nanos tz) + + check_dates_autumn d3 + check_dates_autumn d4 + + d3_start = d3.start_of Time_Period.Hour + d3_start . should_equal (Date_Time.new 2022 3 27 2 0 0 0 tz) + d3_start.offset . should_equal offset_2_h + d3_end = d3.end_of Time_Period.Hour + d3_end . should_equal (Date_Time.new 2022 3 27 2 59 59 max_nanos tz) + d3_end.offset . should_equal offset_2_h + + d4_start = d4.start_of Time_Period.Hour + d4_start . should_equal (Date_Time.new 2022 3 27 2 0 0 0 tz + 1.hour) + d4_start.offset . should_equal offset_1_h + d4_end = d4.end_of Time_Period.Hour + d4_end . should_equal (Date_Time.new 2022 3 27 2 59 59 max_nanos tz + 1.hour) + d4_end.offset . should_equal offset_1_h + Date_Part_Spec.spec name create_new_datetime js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Time_Zone.system = From e3c50e39a83e82e587a3d79bad99ab6691765896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 9 Sep 2022 18:07:05 +0200 Subject: [PATCH 10/18] Formatting --- .../base/src/main/java/org/enso/base/Time_Utils.java | 9 +++++---- .../main/java/org/enso/base/time/Date_Period_Utils.java | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java index b63f468e106d..3f63c7b1a4c2 100644 --- a/std-bits/base/src/main/java/org/enso/base/Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Time_Utils.java @@ -211,11 +211,12 @@ public static LocalTime parse_time(String text, String pattern, Locale locale) { return (LocalTime.parse(text, formatter.withLocale(locale))); } - /** Normally this method could be done in Enso by pattern matching, - * but currently matching on Time types is not supported, so this is a workaround. + /** + * Normally this method could be done in Enso by pattern matching, but currently matching on Time + * types is not supported, so this is a workaround. * - * TODO once the related issue is fixed, this workaround may be replaced with pattern matching in Enso; - * the related Pivotal issue: https://www.pivotaltracker.com/story/show/183219169 + *

TODO once the related issue is fixed, this workaround may be replaced with pattern matching + * in Enso; the related Pivotal issue: https://www.pivotaltracker.com/story/show/183219169 */ public static TimeUtilsBase utils_for(Value value) { boolean isDate = value.isDate(); diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java index 81822b1df031..b7878ec29785 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java @@ -9,13 +9,17 @@ public static Temporal quarter_start(Temporal temporal) { int month = temporal.get(ChronoField.MONTH_OF_YEAR); int quarter = (month - 1) / 3; int firstMonth = quarter * 3 + 1; - return temporal.with(ChronoField.MONTH_OF_YEAR, firstMonth).with(TemporalAdjusters.firstDayOfMonth()); + return temporal + .with(ChronoField.MONTH_OF_YEAR, firstMonth) + .with(TemporalAdjusters.firstDayOfMonth()); } public static Temporal quarter_end(Temporal temporal) { int month = temporal.get(ChronoField.MONTH_OF_YEAR); int quarter = (month - 1) / 3; int lastMonth = quarter * 3 + 3; - return temporal.with(ChronoField.MONTH_OF_YEAR, lastMonth).with(TemporalAdjusters.lastDayOfMonth()); + return temporal + .with(ChronoField.MONTH_OF_YEAR, lastMonth) + .with(TemporalAdjusters.lastDayOfMonth()); } } From 6f716d805ca513f55a19254dbd2c8497eb736aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 9 Sep 2022 18:08:47 +0200 Subject: [PATCH 11/18] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 894d489f1034..8eff8c138e3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -193,6 +193,8 @@ - [Implemented specialized storage for the in-memory Table.][3673] - [Implemented `Table.distinct` for the in-memory backend.][3684] - [Added `databases`, `schemas`, `tables` support to database Connection.][3632] +- [Implemented `start_of` and `end_of` methods for date/time types allowing to + find start and end of a period of time containing the provided time.][3695] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -309,6 +311,7 @@ [3647]: https://github.com/enso-org/enso/pull/3647 [3673]: https://github.com/enso-org/enso/pull/3673 [3684]: https://github.com/enso-org/enso/pull/3684 +[3695]: https://github.com/enso-org/enso/pull/3695 #### Enso Compiler From b390043d51b11678697f2fece8cfd043b5fdc4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 9 Sep 2022 18:10:18 +0200 Subject: [PATCH 12/18] doc --- .../base/src/main/java/org/enso/base/time/TimeUtilsBase.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java b/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java index 49770e687040..ee1931d62649 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java +++ b/std-bits/base/src/main/java/org/enso/base/time/TimeUtilsBase.java @@ -1,3 +1,6 @@ package org.enso.base.time; +/** + * A base type for date/time util classes. Used just to mark the return type of {@code utils_for}. + */ public interface TimeUtilsBase {} From 20b898a5752e0d772336b77ed3e9aef9494169d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Sat, 10 Sep 2022 17:20:36 +0200 Subject: [PATCH 13/18] Mark broken test as pending --- .../0.0.0-dev/src/Data/Time/Date_Time.enso | 7 --- test/Tests/src/Data/Time/Date_Time_Spec.enso | 56 ++++++++++++------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index cc0a5e418fca..c0fbdafe66ea 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -246,13 +246,6 @@ type Date_Time zone : Time_Zone zone self = @Builtin_Method "Date_Time.zone" - ## PRIVATE - Get the offset from UTC/Greenwich associated with this time. - - Currently this function is used only for testing. - offset : ZoneOffset - offset self = Time_Utils.get_datetime_offset self - ## Return the number of seconds from the Unix epoch. > Example diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 6743b295a24e..d2f3441792d0 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -7,6 +7,7 @@ import Standard.Test import project.Data.Time.Date_Part_Spec +polyglot java import org.enso.base.Time_Utils polyglot java import java.time.ZonedDateTime polyglot java import java.time.LocalDateTime polyglot java import java.time.ZoneOffset @@ -378,8 +379,8 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= offset_1_h = ZoneOffset.ofTotalSeconds 3600 offset_2_h = ZoneOffset.ofTotalSeconds 2*3600 - Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near DST switches" <| - tz = Time_Zone.parse "Europe/Warsaw" + tz = Time_Zone.parse "Europe/Warsaw" + Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the spring DST switch" <| 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 + 1.hour) . should_equal d2 @@ -394,25 +395,34 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= check_dates_spring d1 check_dates_spring d2 - d1.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 1 0 0 0 tz) - d1.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 1 59 59 max_nanos tz) + d1_start = d1.start_of Time_Period.Hour + d1_end = d1.end_of Time_Period.Hour + (d1.to_epoch_seconds - d1_start.to_epoch_seconds) . should_equal (34*60 + 15) + (d1_end.to_epoch_seconds - d1.to_epoch_seconds) . should_equal (60*60 - (34*60 + 15) - 1) + d1_start . should_equal (Date_Time.new 2022 3 27 1 0 0 0 tz) + d1_end . should_equal (Date_Time.new 2022 3 27 1 59 59 max_nanos tz) d1.start_of Time_Period.Minute . should_equal (Date_Time.new 2022 3 27 1 34 0 0 tz) d1.end_of Time_Period.Minute . should_equal (Date_Time.new 2022 3 27 1 34 59 max_nanos tz) - d2.start_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 3 0 0 0 tz) - d2.end_of Time_Period.Hour . should_equal (Date_Time.new 2022 3 27 3 59 59 max_nanos tz) + d2_start = d2.start_of Time_Period.Hour + d2_end = d2.end_of Time_Period.Hour + (d2.to_epoch_seconds - d2_start.to_epoch_seconds) . should_equal (34*60 + 15) + (d2_end.to_epoch_seconds - d2.to_epoch_seconds) . should_equal (60*60 - (34*60 + 15) - 1) + d2_start . should_equal (Date_Time.new 2022 3 27 3 0 0 0 tz) + d2_end . should_equal (Date_Time.new 2022 3 27 3 59 59 max_nanos tz) + 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: TODO" + 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 + 1.hour - IO.println d3 - IO.println d4 + d4 = d3 + (1.hour) d3.hour . should_equal 2 d4.hour . should_equal 2 d3.minute . should_equal 30 d4.minute . should_equal 30 - d3.offset . should_equal offset_2_h - d4.offset . should_equal offset_1_h + (d4.to_epoch_milliseconds - d3.to_epoch_milliseconds) . should_equal 60*60*1000 + Time_Utils.get_datetime_offset d3 . should_equal offset_2_h + Time_Utils.get_datetime_offset d4 . should_equal offset_1_h check_dates_autumn date = date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 10 30 0 0 0 0 tz) @@ -425,18 +435,26 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= check_dates_autumn d4 d3_start = d3.start_of Time_Period.Hour - d3_start . should_equal (Date_Time.new 2022 3 27 2 0 0 0 tz) - d3_start.offset . should_equal offset_2_h d3_end = d3.end_of Time_Period.Hour - d3_end . should_equal (Date_Time.new 2022 3 27 2 59 59 max_nanos tz) - d3_end.offset . should_equal offset_2_h + (d3.to_epoch_seconds - d3_start.to_epoch_seconds) . should_equal (30*60 + 15) + (d3_end.to_epoch_seconds - d3.to_epoch_seconds) . should_equal (60*60 - (30*60 + 15) - 1) + d3_start . should_equal (Date_Time.new 2022 10 30 2 0 0 0 tz) + Time_Utils.get_datetime_offset d3_start . should_equal offset_2_h + d3_end . should_equal (Date_Time.new 2022 10 30 2 59 59 max_nanos tz) + Time_Utils.get_datetime_offset d3_end . should_equal offset_2_h d4_start = d4.start_of Time_Period.Hour - d4_start . should_equal (Date_Time.new 2022 3 27 2 0 0 0 tz + 1.hour) - d4_start.offset . should_equal offset_1_h d4_end = d4.end_of Time_Period.Hour - d4_end . should_equal (Date_Time.new 2022 3 27 2 59 59 max_nanos tz + 1.hour) - d4_end.offset . should_equal offset_1_h + (d4.to_epoch_seconds - d4_start.to_epoch_seconds) . should_equal (30*60 + 15) + (d4_end.to_epoch_seconds - d4.to_epoch_seconds) . should_equal (60*60 - (30*60 + 15) - 1) + d4_start.hour . should_equal 2 + d4_start.minute . should_equal 0 + Time_Utils.get_datetime_offset d4_start . should_equal offset_1_h + d4_end.hour . should_equal 2 + d4_end.minute . should_equal 59 + d4_end.second . should_equal 59 + d4_end.nanosecond . should_equal max_nanos + Time_Utils.get_datetime_offset d4_end . should_equal offset_1_h Date_Part_Spec.spec name create_new_datetime From f5fa2da29e4198ee58acc5ca039493c931a43666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Sat, 10 Sep 2022 17:37:42 +0200 Subject: [PATCH 14/18] cr1 --- .../lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso | 4 ++-- .../Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 4 ++-- test/Tests/src/Data/Time/Date_Spec.enso | 2 +- test/Tests/src/Data/Time/Date_Time_Spec.enso | 2 +- test/Tests/src/Data/Time/Time_Of_Day_Spec.enso | 2 +- test/Tests/src/Semantic/Java_Interop_Spec.enso | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 450ada503805..4ca1bfccd9e2 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -253,8 +253,8 @@ 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 - to_time : Time_Of_Day -> Time_Zone -> Date_Time - to_time self (time_of_day=Time_Of_Day.new) (zone=Time_Zone.system) = self.to_time_builtin time_of_day zone + 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 ## Add the specified amount of time to this instant to get another date. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index 755ca2ec42c9..4020e65e48a0 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -202,8 +202,8 @@ type Time_Of_Day from Standard.Base import Time_Of_Day example_to_time = Time_Of_Day.new 12 30 . to_time (Date.new 2020) - to_time : Date -> Time_Zone -> Date_Time - to_time self date (zone=Time_Zone.system) = self.to_time_builtin date zone + to_date_time : Date -> Time_Zone -> Date_Time + to_date_time self date (zone=Time_Zone.system) = self.to_time_builtin date zone ## Add the specified amount of time to this instant to get a new instant. diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 5d0dc0f62c5a..4f1fc50c7cf5 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -77,7 +77,7 @@ spec_with name create_new_date parse_date = Test.fail ("Unexpected result: " + result.to_text) Test.specify "should convert to time" <| - time = create_new_date 2000 12 21 . to_time (Time_Of_Day.new 12 30 45) Time_Zone.utc + time = create_new_date 2000 12 21 . to_date_time (Time_Of_Day.new 12 30 45) Time_Zone.utc time . year . should_equal 2000 time . month . should_equal 12 time . day . should_equal 21 diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index d2f3441792d0..0fecda22e05c 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -411,7 +411,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= d2_start . should_equal (Date_Time.new 2022 3 27 3 0 0 0 tz) d2_end . should_equal (Date_Time.new 2022 3 27 3 59 59 max_nanos tz) - 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: TODO" + 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 + (1.hour) diff --git a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso index be0c53e18e58..440833f78721 100644 --- a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso @@ -72,7 +72,7 @@ specWith name create_new_time parse_time = Test.fail ("Unexpected result: " + result.to_text) Test.specify "should convert to time" <| - datetime = create_new_time 1 0 0 . to_time (Date.new 2000 12 21) Time_Zone.utc + datetime = create_new_time 1 0 0 . to_date_time (Date.new 2000 12 21) Time_Zone.utc datetime . year . should_equal 2000 datetime . month . should_equal 12 datetime . day . should_equal 21 diff --git a/test/Tests/src/Semantic/Java_Interop_Spec.enso b/test/Tests/src/Semantic/Java_Interop_Spec.enso index 08bab77f3445..c131d26cbec2 100644 --- a/test/Tests/src/Semantic/Java_Interop_Spec.enso +++ b/test/Tests/src/Semantic/Java_Interop_Spec.enso @@ -47,7 +47,7 @@ spec = Test.specify "send Enso date into Java" <| ensodate = Date.new 2022 04 01 javatime = LocalTime.of 10 26 - javatimedate = javatime . to_time ensodate + javatimedate = javatime . to_date_time ensodate april1st = javatimedate . date april1st.year.should_equal 2022 april1st.month.should_equal 4 From be5265e64a77ba94f2566c7ba0669b68e07abd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Sat, 10 Sep 2022 20:20:29 +0200 Subject: [PATCH 15/18] Compute quarters using adjusters for more uniform implementation --- .../0.0.0-dev/src/Data/Time/Date_Period.enso | 28 ++++++++-------- .../org/enso/base/time/Date_Period_Utils.java | 32 ++++++++----------- .../org/enso/base/time/Date_Time_Utils.java | 8 ----- .../java/org/enso/base/time/Date_Utils.java | 8 ----- 4 files changed, 27 insertions(+), 49 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso index 0d1bcdc72104..a4aeaabc0265 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Period.enso @@ -1,6 +1,8 @@ from Standard.Base import all polyglot java import org.enso.base.Time_Utils +polyglot java import org.enso.base.time.Date_Period_Utils +polyglot java import java.time.temporal.TemporalAdjuster polyglot java import java.time.temporal.TemporalAdjusters ## Represents a period of time longer on the scale of days (longer than a day). @@ -17,20 +19,18 @@ type Date_Period ## PRIVATE adjust_start : (Date | Date_Time) -> (Date | Date_Time) - adjust_start self date = case self of - Year -> - (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.firstDayOfYear - Quarter -> - (Time_Utils.utils_for date).quarter_start date - Month -> - (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.firstDayOfMonth + adjust_start self date = + adjuster = case self of + Year -> TemporalAdjusters.firstDayOfYear + Quarter -> Date_Period_Utils.quarter_start + Month -> TemporalAdjusters.firstDayOfMonth + (Time_Utils.utils_for date).apply_adjuster date adjuster ## PRIVATE adjust_end : (Date | Date_Time) -> (Date | Date_Time) - adjust_end self date = case self of - Year -> - (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.lastDayOfYear - Quarter -> - (Time_Utils.utils_for date).quarter_end date - Month -> - (Time_Utils.utils_for date).apply_adjuster date TemporalAdjusters.lastDayOfMonth + adjust_end self date = + adjuster = case self of + Year -> TemporalAdjusters.lastDayOfYear + Quarter -> Date_Period_Utils.quarter_end + Month -> TemporalAdjusters.lastDayOfMonth + (Time_Utils.utils_for date).apply_adjuster date adjuster diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java index b7878ec29785..18ef42a396f8 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java @@ -1,25 +1,19 @@ package org.enso.base.time; -import java.time.temporal.ChronoField; -import java.time.temporal.Temporal; -import java.time.temporal.TemporalAdjusters; +import java.time.YearMonth; +import java.time.temporal.*; public class Date_Period_Utils implements TimeUtilsBase { - public static Temporal quarter_start(Temporal temporal) { - int month = temporal.get(ChronoField.MONTH_OF_YEAR); - int quarter = (month - 1) / 3; - int firstMonth = quarter * 3 + 1; - return temporal - .with(ChronoField.MONTH_OF_YEAR, firstMonth) - .with(TemporalAdjusters.firstDayOfMonth()); - } - public static Temporal quarter_end(Temporal temporal) { - int month = temporal.get(ChronoField.MONTH_OF_YEAR); - int quarter = (month - 1) / 3; - int lastMonth = quarter * 3 + 3; - return temporal - .with(ChronoField.MONTH_OF_YEAR, lastMonth) - .with(TemporalAdjusters.lastDayOfMonth()); - } + public static TemporalAdjuster quarter_start = (Temporal temporal) -> { + int currentQuarter = temporal.get(IsoFields.QUARTER_OF_YEAR); + int month = (currentQuarter - 1) * 3 + 1; + return temporal.with(ChronoField.MONTH_OF_YEAR, month).with(TemporalAdjusters.firstDayOfMonth()); + }; + + public static TemporalAdjuster quarter_end = (Temporal temporal) -> { + int currentQuarter = YearMonth.from(temporal).get(IsoFields.QUARTER_OF_YEAR); + int month = (currentQuarter - 1) * 3 + 3; + return temporal.with(ChronoField.MONTH_OF_YEAR, month).with(TemporalAdjusters.lastDayOfMonth()); + }; } diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java index 9066ade74a6e..048556b48f16 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Time_Utils.java @@ -7,14 +7,6 @@ public class Date_Time_Utils implements TimeUtilsBase { public static final Date_Time_Utils INSTANCE = new Date_Time_Utils(); - public ZonedDateTime quarter_start(ZonedDateTime date) { - return (ZonedDateTime) Date_Period_Utils.quarter_start(date); - } - - public ZonedDateTime quarter_end(ZonedDateTime date) { - return (ZonedDateTime) Date_Period_Utils.quarter_end(date); - } - public ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) { return date.truncatedTo(unit); } diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java index 9605a5d0360a..3a37e428aab9 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Utils.java @@ -6,14 +6,6 @@ public class Date_Utils implements TimeUtilsBase { public static final Date_Utils INSTANCE = new Date_Utils(); - public LocalDate quarter_start(LocalDate date) { - return (LocalDate) Date_Period_Utils.quarter_start(date); - } - - public LocalDate quarter_end(LocalDate date) { - return (LocalDate) Date_Period_Utils.quarter_end(date); - } - public LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) { return date.with(adjuster); } From d65aeb8e4deac4f90d01abd14ce4f62295748991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Mon, 12 Sep 2022 00:32:25 +0200 Subject: [PATCH 16/18] formatting --- .../org/enso/base/time/Date_Period_Utils.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java index 18ef42a396f8..6972c4a6f47a 100644 --- a/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/time/Date_Period_Utils.java @@ -5,15 +5,21 @@ public class Date_Period_Utils implements TimeUtilsBase { - public static TemporalAdjuster quarter_start = (Temporal temporal) -> { - int currentQuarter = temporal.get(IsoFields.QUARTER_OF_YEAR); - int month = (currentQuarter - 1) * 3 + 1; - return temporal.with(ChronoField.MONTH_OF_YEAR, month).with(TemporalAdjusters.firstDayOfMonth()); - }; + public static TemporalAdjuster quarter_start = + (Temporal temporal) -> { + int currentQuarter = temporal.get(IsoFields.QUARTER_OF_YEAR); + int month = (currentQuarter - 1) * 3 + 1; + return temporal + .with(ChronoField.MONTH_OF_YEAR, month) + .with(TemporalAdjusters.firstDayOfMonth()); + }; - public static TemporalAdjuster quarter_end = (Temporal temporal) -> { - int currentQuarter = YearMonth.from(temporal).get(IsoFields.QUARTER_OF_YEAR); - int month = (currentQuarter - 1) * 3 + 3; - return temporal.with(ChronoField.MONTH_OF_YEAR, month).with(TemporalAdjusters.lastDayOfMonth()); - }; + public static TemporalAdjuster quarter_end = + (Temporal temporal) -> { + int currentQuarter = YearMonth.from(temporal).get(IsoFields.QUARTER_OF_YEAR); + int month = (currentQuarter - 1) * 3 + 3; + return temporal + .with(ChronoField.MONTH_OF_YEAR, month) + .with(TemporalAdjusters.lastDayOfMonth()); + }; } From 6d574a44e1316486e9046dd8351789b147d3e41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Mon, 12 Sep 2022 17:21:45 +0200 Subject: [PATCH 17/18] fix typename of Date_Time JSON repr --- .../lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index c0fbdafe66ea..68c471038be2 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -429,7 +429,7 @@ type Date_Time example_to_json = Date_Time.now.to_json to_json : Json.Object - to_json self = Json.from_pairs [["type", "Time"], ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond], ["zone", self.zone]] + to_json self = Json.from_pairs [["type", "Date_Time"], ["year", self.year], ["month", self.month], ["day", self.day], ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond], ["zone", self.zone]] ## Format this time as text using the specified format specifier. From b728f05614e1f044134c83e8b095f1ca2454903e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 13 Sep 2022 10:52:48 +0200 Subject: [PATCH 18/18] Make some JS tests pending as JS date support is currently limited --- test/Tests/src/Data/Time/Date_Time_Spec.enso | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 0fecda22e05c..2a979223cfe5 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -53,7 +53,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= time.to_json.should_equal <| zone_pairs = [["zone", Time_Zone.utc]] time_pairs = [["year", time.year], ["month", time.month], ["day", time.day], ["hour", time.hour], ["minute", time.minute], ["second", time.second], ["nanosecond", time.nanosecond]] - Json.from_pairs ([["type", "Time"]] + time_pairs + zone_pairs) + Json.from_pairs ([["type", "Date_Time"]] + time_pairs + zone_pairs) Test.specify "should parse default time format" <| text = create_new_datetime 1970 (zone = Time_Zone.utc) . to_text @@ -380,10 +380,13 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision= offset_1_h = ZoneOffset.ofTotalSeconds 3600 offset_2_h = ZoneOffset.ofTotalSeconds 2*3600 tz = Time_Zone.parse "Europe/Warsaw" - Test.specify "should find start/end of a Date_Period or Time_Period containing the current datetime correctly near the spring DST switch" <| + js_dst_pending = if name.contains "Javascript" then + "Javascript implementation does not support time zones correctly, so the tests for conversion around DST switches would fail and thus are disabled. We may revisit once JS gets better time support, see project Temporal: https://tc39.es/proposal-temporal/docs/ and our Pivotal issue tracking our integration: https://www.pivotaltracker.com/story/show/183261296" + 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 + 1.hour) . should_equal d2 + d1_plus = d1 + 1.hour + d1_plus . should_equal d2 check_dates_spring date = date.start_of Time_Period.Day . should_equal (Date_Time.new 2022 3 27 0 0 0 0 tz)