mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 19:21:54 +03:00
Add Period Start and End functions to Date and DateTime (#3695)
Implements https://www.pivotaltracker.com/story/show/183081152
This commit is contained in:
parent
fba5047acc
commit
b304402d8e
@ -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
|
||||
|
||||
|
29
build.sbt
29
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",
|
||||
|
@ -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
|
||||
@ -230,6 +231,14 @@ 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 = 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 = period.adjust_end self
|
||||
|
||||
## ALIAS Date to Time
|
||||
|
||||
Combine this date with time of day to create a point in time.
|
||||
@ -244,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 (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.
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
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).
|
||||
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 =
|
||||
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 =
|
||||
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
|
@ -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,24 @@ 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 `Time_Period` or `Date_Period`
|
||||
containing self.
|
||||
start_of : (Date_Period|Time_Period) -> Date_Time
|
||||
start_of self period=Date_Period.Month =
|
||||
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 =
|
||||
adjusted = period.adjust_end self
|
||||
case period.is_date_period of
|
||||
True -> Time_Period.Day.adjust_end adjusted
|
||||
False -> adjusted
|
||||
|
||||
## ALIAS Time to Date
|
||||
|
||||
Convert this point in time to date, discarding the time of day
|
||||
@ -409,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.
|
||||
|
||||
|
@ -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
|
||||
@ -170,6 +171,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=Time_Period.Day = period.adjust_end self
|
||||
|
||||
## Extracts the time as the number of seconds, from 0 to 24 * 60 * 60 - 1.
|
||||
|
||||
> Example
|
||||
@ -193,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 -> 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.
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
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.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.utils_for date).end_of_time_period date self.to_java_unit
|
@ -14,6 +14,7 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
||||
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 final class EnsoTimeZone implements TruffleObject {
|
||||
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;
|
||||
|
@ -1,11 +1,15 @@
|
||||
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;
|
||||
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 +210,24 @@ public class Time_Utils {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
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.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
|
||||
public static ZoneOffset get_datetime_offset(ZonedDateTime datetime) {
|
||||
return datetime.getOffset();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package org.enso.base.time;
|
||||
|
||||
import java.time.YearMonth;
|
||||
import java.time.temporal.*;
|
||||
|
||||
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_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());
|
||||
};
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
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 ZonedDateTime start_of_time_period(ZonedDateTime date, TemporalUnit unit) {
|
||||
return date.truncatedTo(unit);
|
||||
}
|
||||
|
||||
public ZonedDateTime end_of_time_period(ZonedDateTime date, TemporalUnit unit) {
|
||||
return date.truncatedTo(unit).plus(1, unit).minusNanos(1);
|
||||
}
|
||||
|
||||
public ZonedDateTime apply_adjuster(ZonedDateTime date, TemporalAdjuster adjuster) {
|
||||
return date.with(adjuster);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
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 LocalDate apply_adjuster(LocalDate date, TemporalAdjuster adjuster) {
|
||||
return date.with(adjuster);
|
||||
}
|
||||
}
|
@ -0,0 +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 {}
|
@ -0,0 +1,19 @@
|
||||
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 {
|
||||
public static final Time_Of_Day_Utils INSTANCE = new Time_Of_Day_Utils();
|
||||
|
||||
public LocalTime start_of_time_period(LocalTime date, TemporalUnit unit) {
|
||||
return date.truncatedTo(unit);
|
||||
}
|
||||
|
||||
public LocalTime end_of_time_period(LocalTime date, TemporalUnit unit) {
|
||||
LocalTime truncated = date.truncatedTo(unit);
|
||||
LocalTime adjusted = unit.equals(ChronoUnit.DAYS) ? truncated : truncated.plus(1, unit);
|
||||
return adjusted.minusNanos(1);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
from Standard.Base import all
|
||||
|
||||
import Standard.Base.Data.Text.Text_Sub_Range
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
import Standard.Base.Data.Time.Duration
|
||||
from Standard.Base.Error.Common import Time_Error
|
||||
|
||||
@ -76,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
|
||||
@ -132,8 +133,52 @@ spec_with name create_new_date parse_date =
|
||||
date_1>date_2 . should_be_true
|
||||
date_1<date_2 . should_be_false
|
||||
|
||||
Test.specify "should allow to find start and end 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.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 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)
|
||||
|
||||
Date_Part_Spec.spec name create_new_date
|
||||
|
||||
|
||||
main = Test.Suite.run_main spec
|
||||
|
||||
parseNormally x y = (Date.parse x y) . to_text
|
||||
|
@ -1,12 +1,16 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.Time.Date_Period
|
||||
import Standard.Base.Data.Time.Time_Period
|
||||
import Standard.Base.Data.Time.Duration
|
||||
|
||||
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
|
||||
polyglot java import java.time.format.DateTimeFormatter
|
||||
|
||||
spec =
|
||||
@ -49,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
|
||||
@ -298,6 +302,163 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
max_nanos = 999999999
|
||||
Test.specify "should allow to find start/end of a Date_Period containing the current datetime" <|
|
||||
d1 = create_new_datetime 2022 9 12 15 37 58
|
||||
d1.start_of Date_Period.Year . should_equal (Date_Time.new 2022 1 1)
|
||||
d1.end_of Date_Period.Year . should_equal (Date_Time.new 2022 12 31 23 59 59 max_nanos)
|
||||
d1.start_of Date_Period.Quarter . should_equal (Date_Time.new 2022 7 1)
|
||||
d1.end_of Date_Period.Quarter . should_equal (Date_Time.new 2022 9 30 23 59 59 max_nanos)
|
||||
d1.start_of Date_Period.Month . should_equal (Date_Time.new 2022 9 1)
|
||||
d1.end_of Date_Period.Month . should_equal (Date_Time.new 2022 9 30 23 59 59 max_nanos)
|
||||
|
||||
d2 = create_new_datetime 2022 2 7 12 34 56 123456789
|
||||
d2.start_of Date_Period.Quarter . should_equal (Date_Time.new 2022 1 1)
|
||||
d2.end_of Date_Period.Quarter . should_equal (Date_Time.new 2022 3 31 23 59 59 max_nanos)
|
||||
d2.start_of Date_Period.Month . should_equal (Date_Time.new 2022 2 1)
|
||||
d2.end_of Date_Period.Month . should_equal (Date_Time.new 2022 2 28 23 59 59 max_nanos)
|
||||
|
||||
d3 = create_new_datetime 2020 2 17
|
||||
d3.start_of Date_Period.Year . should_equal (Date_Time.new 2020 1 1)
|
||||
d3.end_of Date_Period.Year . should_equal (Date_Time.new 2020 12 31 23 59 59 max_nanos)
|
||||
d3.start_of Date_Period.Month . should_equal (Date_Time.new 2020 2 1)
|
||||
d3.end_of Date_Period.Month . should_equal (Date_Time.new 2020 2 29 23 59 59 max_nanos)
|
||||
|
||||
d4 = create_new_datetime 1970 12 31 23 59 59 max_nanos
|
||||
d4.start_of Date_Period.Year . should_equal (Date_Time.new 1970 1 1)
|
||||
d4.end_of Date_Period.Year . should_equal (Date_Time.new 1970 12 31 23 59 59 max_nanos)
|
||||
d4.start_of Date_Period.Quarter . should_equal (Date_Time.new 1970 10 1)
|
||||
d4.end_of Date_Period.Quarter . should_equal (Date_Time.new 1970 12 31 23 59 59 max_nanos)
|
||||
d4.start_of Date_Period.Month . should_equal (Date_Time.new 1970 12 1)
|
||||
d4.end_of Date_Period.Month . should_equal (Date_Time.new 1970 12 31 23 59 59 max_nanos)
|
||||
|
||||
d5 = create_new_datetime 2040 1 1
|
||||
d5.start_of Date_Period.Year . should_equal (Date_Time.new 2040 1 1)
|
||||
d5.end_of Date_Period.Year . should_equal (Date_Time.new 2040 12 31 23 59 59 max_nanos)
|
||||
d5.start_of Date_Period.Quarter . should_equal (Date_Time.new 2040 1 1)
|
||||
d5.end_of Date_Period.Quarter . should_equal (Date_Time.new 2040 3 31 23 59 59 max_nanos)
|
||||
d5.start_of Date_Period.Month . should_equal (Date_Time.new 2040 1 1)
|
||||
d5.end_of Date_Period.Month . should_equal (Date_Time.new 2040 1 31 23 59 59 max_nanos)
|
||||
|
||||
(create_new_datetime 2000 7 1 14 54).start_of Date_Period.Quarter . should_equal (Date_Time.new 2000 7 1)
|
||||
(create_new_datetime 2000 6 30 15 34).start_of Date_Period.Quarter . should_equal (Date_Time.new 2000 4 1)
|
||||
|
||||
(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)
|
||||
|
||||
offset_1_h = ZoneOffset.ofTotalSeconds 3600
|
||||
offset_2_h = ZoneOffset.ofTotalSeconds 2*3600
|
||||
tz = Time_Zone.parse "Europe/Warsaw"
|
||||
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_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)
|
||||
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 = 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 = 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: 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)
|
||||
|
||||
d3.hour . should_equal 2
|
||||
d4.hour . should_equal 2
|
||||
d3.minute . should_equal 30
|
||||
d4.minute . should_equal 30
|
||||
(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)
|
||||
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_end = d3.end_of Time_Period.Hour
|
||||
(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_end = d4.end_of Time_Period.Hour
|
||||
(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
|
||||
|
||||
js_datetime year month=1 day=1 hour=0 minute=0 second=0 nanosecond=0 zone=Time_Zone.system =
|
||||
|
@ -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
|
||||
@ -71,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
|
||||
@ -116,6 +117,39 @@ specWith name create_new_time parse_time =
|
||||
time_1>time_2 . should_be_true
|
||||
time_1<time_2 . should_be_false
|
||||
|
||||
max_nanos = 999999999
|
||||
Test.specify "should allow to find start/end of a Time_Period containing the current time of day" <|
|
||||
d1 = create_new_time 15 37 58 123456789
|
||||
d1.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
|
||||
d1.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
d1.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 15 0 0 0)
|
||||
d1.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 15 59 59 max_nanos)
|
||||
d1.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 15 37 0 0)
|
||||
d1.end_of Time_Period.Minute . should_equal (Time_Of_Day.new 15 37 59 max_nanos)
|
||||
d1.start_of Time_Period.Second . should_equal (Time_Of_Day.new 15 37 58 0)
|
||||
d1.end_of Time_Period.Second . should_equal (Time_Of_Day.new 15 37 58 max_nanos)
|
||||
|
||||
d2 = create_new_time 0 0 0
|
||||
d2.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
|
||||
d2.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
d2.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 0 0 0 0)
|
||||
d2.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 0 59 59 max_nanos)
|
||||
d2.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 0 0 0 0)
|
||||
d2.end_of Time_Period.Minute . should_equal (Time_Of_Day.new 0 0 59 max_nanos)
|
||||
d2.start_of Time_Period.Second . should_equal (Time_Of_Day.new 0 0 0 0)
|
||||
d2.end_of Time_Period.Second . should_equal (Time_Of_Day.new 0 0 0 max_nanos)
|
||||
|
||||
d3 = create_new_time 23 59 59 max_nanos
|
||||
d3.start_of Time_Period.Day . should_equal (Time_Of_Day.new)
|
||||
d3.end_of Time_Period.Day . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
d3.start_of Time_Period.Hour . should_equal (Time_Of_Day.new 23 0 0 0)
|
||||
d3.end_of Time_Period.Hour . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
d3.start_of Time_Period.Minute . should_equal (Time_Of_Day.new 23 59 0 0)
|
||||
d3.end_of Time_Period.Minute . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
d3.start_of Time_Period.Second . should_equal (Time_Of_Day.new 23 59 59 0)
|
||||
d3.end_of Time_Period.Second . should_equal (Time_Of_Day.new 23 59 59 max_nanos)
|
||||
|
||||
|
||||
enso_time hour minute=0 second=0 nanoOfSecond=0 =
|
||||
Time_Of_Day.new hour minute second nanoOfSecond
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user