From f25ebce0531f7d254406a46f62fae054c229913b Mon Sep 17 00:00:00 2001 From: Chip Kent Date: Wed, 6 Dec 2023 14:56:42 -0700 Subject: [PATCH] Addressing review comments. Add methods that return durations. --- .../time/calendar/BusinessCalendar.java | 63 ++++++++++++++++++- .../deephaven/time/calendar/CalendarDay.java | 35 +++++++++-- .../time/calendar/TestBusinessCalendar.java | 55 ++++++++++++++++ .../time/calendar/TestCalendarDay.java | 49 +++++++++++++-- 4 files changed, 193 insertions(+), 9 deletions(-) diff --git a/engine/time/src/main/java/io/deephaven/time/calendar/BusinessCalendar.java b/engine/time/src/main/java/io/deephaven/time/calendar/BusinessCalendar.java index 5bbf660acb3..f51d7677f83 100644 --- a/engine/time/src/main/java/io/deephaven/time/calendar/BusinessCalendar.java +++ b/engine/time/src/main/java/io/deephaven/time/calendar/BusinessCalendar.java @@ -211,6 +211,15 @@ public long standardBusinessNanos() { return standardBusinessDay.businessNanos(); } + /** + * Length of a standard business day. + * + * @return length of a standard business day + */ + public Duration standardBusinessDuration() { + return standardBusinessDay.businessDuration(); + } + /** * Business day schedules for all holidays. A holiday is a date that has a schedule that is different from the * schedule for a standard business day or weekend. @@ -218,7 +227,7 @@ public long standardBusinessNanos() { * @return a map of holiday dates and their calendar days */ public Map> holidays() { - return Collections.unmodifiableMap(holidays); + return holidays; } /** @@ -1451,6 +1460,58 @@ public long diffNonBusinessNanos(final ZonedDateTime start, final ZonedDateTime return diffNonBusinessNanos(start.toInstant(), end.toInstant()); } + /** + * Returns the amount of business time between two times. + * + * @param start start of a time range + * @param end end of a time range + * @return the amount of business time between {@code start} and {@code end} + * @throws RequirementFailure if any input is null + * @throws InvalidDateException if the dates are not in the valid range + */ + public Duration diffBusinessDuration(final Instant start, final Instant end) { + return Duration.ofNanos(diffBusinessNanos(start, end)); + } + + /** + * Returns the amount of business time between two times. + * + * @param start start of a time range + * @param end end of a time range + * @return the amount of business time between {@code start} and {@code end} + * @throws RequirementFailure if any input is null + * @throws InvalidDateException if the dates are not in the valid range + */ + public Duration diffBusinessDuration(final ZonedDateTime start, final ZonedDateTime end) { + return Duration.ofNanos(diffBusinessNanos(start, end)); + } + + /** + * Returns the amount of non-business time between two times. + * + * @param start start of a time range + * @param end end of a time range + * @return the amount of non-business time between {@code start} and {@code end} + * @throws RequirementFailure if any input is null + * @throws InvalidDateException if the dates are not in the valid range + */ + public Duration diffNonBusinessDuration(final Instant start, final Instant end) { + return Duration.ofNanos(diffNonBusinessNanos(start, end)); + } + + /** + * Returns the amount of non-business time between two times. + * + * @param start start of a time range + * @param end end of a time range + * @return the amount of non-business time between {@code start} and {@code end} + * @throws RequirementFailure if any input is null + * @throws InvalidDateException if the dates are not in the valid range + */ + public Duration diffNonBusinessDuration(final ZonedDateTime start, final ZonedDateTime end) { + return Duration.ofNanos(diffNonBusinessNanos(start, end)); + } + /** * Returns the amount of business time in standard business days between two times. * diff --git a/engine/time/src/main/java/io/deephaven/time/calendar/CalendarDay.java b/engine/time/src/main/java/io/deephaven/time/calendar/CalendarDay.java index 0458ab62caf..c3142f82600 100644 --- a/engine/time/src/main/java/io/deephaven/time/calendar/CalendarDay.java +++ b/engine/time/src/main/java/io/deephaven/time/calendar/CalendarDay.java @@ -7,10 +7,7 @@ import io.deephaven.base.verify.RequirementFailure; import org.jetbrains.annotations.NotNull; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; +import java.time.*; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.util.*; @@ -111,6 +108,16 @@ public long businessNanos() { return businessTimeRanges.stream().map(TimeRange::nanos).reduce(0L, Long::sum); } + /** + * Gets the length of the business day. If the business day has multiple business time ranges, only the time during + * the ranges is counted. + * + * @return length of the day + */ + public Duration businessDuration() { + return Duration.ofNanos(businessNanos()); + } + /** * Amount of business time in nanoseconds that has elapsed on the given day by the specified time. * @@ -135,6 +142,16 @@ public long businessNanosElapsed(final T time) { return elapsed; } + /** + * Amount of business time that has elapsed on the given day by the specified time. + * + * @param time time + * @return business time that has elapsed on the given day by the specified time + */ + public Duration businessDurationElapsed(final T time) { + return Duration.ofNanos(businessNanosElapsed(time)); + } + /** * Amount of business time in nanoseconds that remains until the end of the day. * @@ -146,6 +163,16 @@ public long businessNanosRemaining(final T time) { return businessNanos() - businessNanosElapsed(time); } + /** + * Amount of business time that remains until the end of the day. + * + * @param time time + * @return business time that remains until the end of the day + */ + public Duration businessDurationRemaining(final T time) { + return Duration.ofNanos(businessNanosRemaining(time)); + } + /** * Is this day a business day? * diff --git a/engine/time/src/test/java/io/deephaven/time/calendar/TestBusinessCalendar.java b/engine/time/src/test/java/io/deephaven/time/calendar/TestBusinessCalendar.java index 0ed0470f574..b29f3736315 100644 --- a/engine/time/src/test/java/io/deephaven/time/calendar/TestBusinessCalendar.java +++ b/engine/time/src/test/java/io/deephaven/time/calendar/TestBusinessCalendar.java @@ -48,6 +48,7 @@ protected void setUp() throws Exception { public void testBusinessGetters() { assertEquals(schedule, bCalendar.standardBusinessDay()); assertEquals(schedule.businessNanos(), bCalendar.standardBusinessNanos()); + assertEquals(schedule.businessDuration(), bCalendar.standardBusinessDuration()); assertEquals(holidays, bCalendar.holidays()); assertEquals(firstValidDate, bCalendar.firstValidDate()); assertEquals(lastValidDate, bCalendar.lastValidDate()); @@ -1057,6 +1058,60 @@ public void testDiffNonBusinessNanos() { bCalendar.diffNonBusinessNanos(zdt3.toInstant(), zdt1.toInstant())); } + public void testDiffBusinessDuration() { + // Same day + final ZonedDateTime zdt1 = LocalDate.of(2023, 7, 3).atTime(9, 27).atZone(timeZone); + final ZonedDateTime zdt2 = LocalDate.of(2023, 7, 3).atTime(12, 10).atZone(timeZone); + + assertEquals(Duration.ofNanos(zdt1.until(zdt2, ChronoUnit.NANOS)), bCalendar.diffBusinessDuration(zdt1, zdt2)); + assertEquals(Duration.ofNanos(-zdt1.until(zdt2, ChronoUnit.NANOS)), bCalendar.diffBusinessDuration(zdt2, zdt1)); + assertEquals(Duration.ofNanos(zdt1.until(zdt2, ChronoUnit.NANOS)), + bCalendar.diffBusinessDuration(zdt1.toInstant(), zdt2.toInstant())); + assertEquals(Duration.ofNanos(-zdt1.until(zdt2, ChronoUnit.NANOS)), + bCalendar.diffBusinessDuration(zdt2.toInstant(), zdt1.toInstant())); + + // Multiple holidays + final ZonedDateTime zdt3 = LocalDate.of(2023, 7, 8).atTime(12, 54).atZone(timeZone); + final long target = schedule.businessNanosRemaining(zdt1.toLocalTime()) // 2023-07-03 + // 2023-07-04 holiday + // 2023-07-05 weekend WED + + halfDay.businessNanos() // 2023-07-06 half day + + schedule.businessNanos() // normal day + + schedule.businessNanosElapsed(zdt3.toLocalTime()); + + assertEquals(Duration.ofNanos(target), bCalendar.diffBusinessDuration(zdt1, zdt3)); + assertEquals(Duration.ofNanos(-target), bCalendar.diffBusinessDuration(zdt3, zdt1)); + assertEquals(Duration.ofNanos(target), bCalendar.diffBusinessDuration(zdt1.toInstant(), zdt3.toInstant())); + assertEquals(Duration.ofNanos(-target), bCalendar.diffBusinessDuration(zdt3.toInstant(), zdt1.toInstant())); + } + + public void testDiffNonBusinessDuration() { + // Same day + final ZonedDateTime zdt1 = LocalDate.of(2023, 7, 3).atTime(6, 27).atZone(timeZone); + final ZonedDateTime zdt2 = LocalDate.of(2023, 7, 3).atTime(15, 10).atZone(timeZone); + + assertEquals(Duration.ofNanos(DateTimeUtils.diffNanos(zdt1, zdt2) - bCalendar.diffBusinessNanos(zdt1, zdt2)), + bCalendar.diffNonBusinessDuration(zdt1, zdt2)); + assertEquals(Duration.ofNanos(-bCalendar.diffNonBusinessNanos(zdt1, zdt2)), + bCalendar.diffNonBusinessDuration(zdt2, zdt1)); + assertEquals(Duration.ofNanos(DateTimeUtils.diffNanos(zdt1, zdt2) - bCalendar.diffBusinessNanos(zdt1, zdt2)), + bCalendar.diffNonBusinessDuration(zdt1.toInstant(), zdt2.toInstant())); + assertEquals(Duration.ofNanos(-bCalendar.diffNonBusinessNanos(zdt1, zdt2)), + bCalendar.diffNonBusinessDuration(zdt2.toInstant(), zdt1.toInstant())); + + // Multiple holidays + final ZonedDateTime zdt3 = LocalDate.of(2023, 7, 8).atTime(12, 54).atZone(timeZone); + + assertEquals(Duration.ofNanos(DateTimeUtils.diffNanos(zdt1, zdt3) - bCalendar.diffBusinessNanos(zdt1, zdt3)), + bCalendar.diffNonBusinessDuration(zdt1, zdt3)); + assertEquals(Duration.ofNanos(-bCalendar.diffNonBusinessNanos(zdt1, zdt3)), + bCalendar.diffNonBusinessDuration(zdt3, zdt1)); + assertEquals(Duration.ofNanos(DateTimeUtils.diffNanos(zdt1, zdt3) - bCalendar.diffBusinessNanos(zdt1, zdt3)), + bCalendar.diffNonBusinessDuration(zdt1.toInstant(), zdt3.toInstant())); + assertEquals(Duration.ofNanos(-bCalendar.diffNonBusinessNanos(zdt1, zdt3)), + bCalendar.diffNonBusinessDuration(zdt3.toInstant(), zdt1.toInstant())); + } + public void testDiffBusinessDays() { final ZonedDateTime zdt1 = LocalDate.of(2023, 7, 3).atTime(6, 27).atZone(timeZone); final ZonedDateTime zdt2 = LocalDate.of(2023, 7, 10).atTime(15, 10).atZone(timeZone); diff --git a/engine/time/src/test/java/io/deephaven/time/calendar/TestCalendarDay.java b/engine/time/src/test/java/io/deephaven/time/calendar/TestCalendarDay.java index a34e342d90f..301381cd3e9 100644 --- a/engine/time/src/test/java/io/deephaven/time/calendar/TestCalendarDay.java +++ b/engine/time/src/test/java/io/deephaven/time/calendar/TestCalendarDay.java @@ -6,10 +6,7 @@ import io.deephaven.base.testing.BaseArrayTestCase; import io.deephaven.time.DateTimeUtils; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; +import java.time.*; import java.util.List; import java.util.Objects; @@ -32,14 +29,20 @@ public void testEmpty() { assertNull(empty.businessEnd()); assertNull(empty.businessEnd()); assertEquals(0L, empty.businessNanos()); + assertEquals(Duration.ofNanos(0), empty.businessDuration()); assertEquals(0L, empty.businessNanos()); + assertEquals(Duration.ofNanos(0), empty.businessDuration()); assertFalse(empty.isBusinessDay()); assertFalse(empty.isBusinessTime(open1)); assertFalse(empty.isBusinessTime(close1)); assertEquals(0L, empty.businessNanosElapsed(open1)); + assertEquals(Duration.ofNanos(0), empty.businessDurationElapsed(open1)); assertEquals(0L, empty.businessNanosElapsed(close1)); + assertEquals(Duration.ofNanos(0), empty.businessDurationElapsed(close1)); assertEquals(0L, empty.businessNanosRemaining(open1)); + assertEquals(Duration.ofNanos(0), empty.businessDurationRemaining(open1)); assertEquals(0L, empty.businessNanosRemaining(close1)); + assertEquals(Duration.ofNanos(0), empty.businessDurationRemaining(close1)); } public void testSinglePeriod() { @@ -50,21 +53,33 @@ public void testSinglePeriod() { assertEquals(close1, single.businessEnd()); assertEquals(close1, single.businessEnd()); assertEquals(DateTimeUtils.HOUR, single.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR), single.businessDuration()); assertEquals(DateTimeUtils.HOUR, single.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR), single.businessDuration()); assertTrue(single.isBusinessDay()); assertTrue(single.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:00:00.000000000 NY"))); assertTrue(single.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:15:00.000000000 NY"))); assertTrue(single.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T11:00:00.000000000 NY"))); assertFalse(single.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T11:10:00.000000000 NY"))); assertEquals(0L, single.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(0), + single.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); assertEquals(DateTimeUtils.MINUTE * 30, single.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.MINUTE * 30), + single.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR, single.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR), + single.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); assertEquals(DateTimeUtils.MINUTE * 30, single.businessNanosRemaining(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.MINUTE * 30), + single.businessDurationRemaining(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); assertEquals(0L, single.businessNanosRemaining(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(0), + single.businessDurationRemaining(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); } public void testMultiPeriod() { @@ -75,7 +90,9 @@ public void testMultiPeriod() { assertEquals(close2, multi.businessEnd()); assertEquals(close2, multi.businessEnd()); assertEquals(DateTimeUtils.HOUR * 6, multi.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 6), multi.businessDuration()); assertEquals(DateTimeUtils.HOUR * 6, multi.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 6), multi.businessDuration()); assertTrue(multi.isBusinessDay()); assertTrue(multi.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:00:00.000000000 NY"))); assertTrue(multi.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:15:00.000000000 NY"))); @@ -83,18 +100,32 @@ public void testMultiPeriod() { assertFalse(multi.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T11:10:00.000000000 NY"))); assertTrue(multi.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T12:10:00.000000000 NY"))); assertEquals(0L, multi.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(0L), + multi.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); assertEquals(DateTimeUtils.MINUTE * 30, multi.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.MINUTE * 30), + multi.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 2, multi.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 2), + multi.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 2, multi.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 2), + multi.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 6, multi.businessNanosRemaining(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 6), + multi.businessDurationRemaining(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 5 + DateTimeUtils.MINUTE * 30, multi.businessNanosRemaining(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 5 + DateTimeUtils.MINUTE * 30), + multi.businessDurationRemaining(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 4, multi.businessNanosRemaining(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 4), + multi.businessDurationRemaining(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); final CalendarDay multi2 = new CalendarDay<>(new TimeRange[] {period2, period1}); @@ -104,7 +135,9 @@ public void testMultiPeriod() { assertEquals(close2, multi2.businessEnd()); assertEquals(close2, multi2.businessEnd()); assertEquals(DateTimeUtils.HOUR * 6, multi2.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 6), multi2.businessDuration()); assertEquals(DateTimeUtils.HOUR * 6, multi2.businessNanos()); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 6), multi2.businessDuration()); assertTrue(multi2.isBusinessDay()); assertTrue(multi2.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:00:00.000000000 NY"))); assertTrue(multi2.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T10:15:00.000000000 NY"))); @@ -112,12 +145,20 @@ public void testMultiPeriod() { assertFalse(multi2.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T11:10:00.000000000 NY"))); assertTrue(multi2.isBusinessTime(DateTimeUtils.parseInstant("2017-03-11T12:10:00.000000000 NY"))); assertEquals(0L, multi2.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(0), + multi2.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T01:00:00.000000000 NY"))); assertEquals(DateTimeUtils.MINUTE * 30, multi2.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.MINUTE * 30), + multi2.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T10:30:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 2, multi2.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 2), + multi2.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); assertEquals(DateTimeUtils.HOUR * 2, multi2.businessNanosElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); + assertEquals(Duration.ofNanos(DateTimeUtils.HOUR * 2), + multi2.businessDurationElapsed(DateTimeUtils.parseInstant("2017-03-11T13:00:00.000000000 NY"))); } public void testPeriodsOverlap() {