From c2ab633a070cf3f0fd8a116ab5b0a3734441ad6e Mon Sep 17 00:00:00 2001 From: Chip Kent <5250374+chipkent@users.noreply.github.com> Date: Tue, 27 Feb 2024 14:54:53 -0700 Subject: [PATCH] Provide upperBin and lowerBin signatures that accept Durations (#5148) * Added upperBin and lowerBin methods that accept durations. --- .../java/io/deephaven/time/DateTimeUtils.java | 208 ++++++++++++++++-- .../io/deephaven/time/TestDateTimeUtils.java | 43 ++++ 2 files changed, 229 insertions(+), 22 deletions(-) diff --git a/engine/time/src/main/java/io/deephaven/time/DateTimeUtils.java b/engine/time/src/main/java/io/deephaven/time/DateTimeUtils.java index 12a5985a618..b96a3c65746 100644 --- a/engine/time/src/main/java/io/deephaven/time/DateTimeUtils.java +++ b/engine/time/src/main/java/io/deephaven/time/DateTimeUtils.java @@ -3152,8 +3152,8 @@ public static ZonedDateTime atMidnight(@Nullable final ZonedDateTime dateTime) { /** * Returns an {@link Instant} value, which is at the starting (lower) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the instant value for the start of the - * five-minute window that contains the input instant. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the instant value for the start + * of the five-minute window that contains the input instant. * * @param instant instant for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds @@ -3170,10 +3170,30 @@ public static Instant lowerBin(@Nullable final Instant instant, long intervalNan return epochNanosToInstant(Numeric.lowerBin(epochNanos(instant), intervalNanos)); } + /** + * Returns an {@link Instant} value, which is at the starting (lower) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the instant value for the start of + * the five-minute window that contains the input instant. + * + * @param instant instant for which to evaluate the start of the containing window + * @param interval size of the window + * @return {@code null} if either input is {@code null}; otherwise, an {@link Instant} representing the start of the + * window + */ + @ScriptApi + @Nullable + public static Instant lowerBin(@Nullable final Instant instant, Duration interval) { + if (instant == null || interval == null) { + return null; + } + + return lowerBin(instant, interval.toNanos()); + } + /** * Returns a {@link ZonedDateTime} value, which is at the starting (lower) end of a time range defined by the - * interval nanoseconds. For example, a 5*MINUTE intervalNanos value would return the zoned date time value for the - * start of the five-minute window that contains the input zoned date time. + * interval nanoseconds. For example, a five-minute {@code intervalNanos} value would return the zoned date time + * value for the start of the five-minute window that contains the input zoned date time. * * @param dateTime zoned date time for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds @@ -3190,16 +3210,36 @@ public static ZonedDateTime lowerBin(@Nullable final ZonedDateTime dateTime, lon return epochNanosToZonedDateTime(Numeric.lowerBin(epochNanos(dateTime), intervalNanos), dateTime.getZone()); } + /** + * Returns a {@link ZonedDateTime} value, which is at the starting (lower) end of a time range defined by the + * interval nanoseconds. For example, a five-minute {@code interval} value would return the zoned date time value + * for the start of the five-minute window that contains the input zoned date time. + * + * @param dateTime zoned date time for which to evaluate the start of the containing window + * @param interval size of the window + * @return {@code null} if either input is {@code null}; otherwise, a {@link ZonedDateTime} representing the start + * of the window + */ + @ScriptApi + @Nullable + public static ZonedDateTime lowerBin(@Nullable final ZonedDateTime dateTime, Duration interval) { + if (dateTime == null || interval == null) { + return null; + } + + return lowerBin(dateTime, interval.toNanos()); + } + /** * Returns an {@link Instant} value, which is at the starting (lower) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the instant value for the start of the - * five-minute window that contains the input instant. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the instant value for the start + * of the five-minute window that contains the input instant. * * @param instant instant for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds * @param offset The window start offset in nanoseconds. For example, a value of MINUTE would offset all windows by * one minute. - * @return {@code null} if either input is {@code null}; otherwise, an {@link Instant} representing the start of the + * @return {@code null} if any input is {@code null}; otherwise, an {@link Instant} representing the start of the * window */ @ScriptApi @@ -3212,17 +3252,38 @@ public static Instant lowerBin(@Nullable final Instant instant, long intervalNan return epochNanosToInstant(Numeric.lowerBin(epochNanos(instant) - offset, intervalNanos) + offset); } + /** + * Returns an {@link Instant} value, which is at the starting (lower) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the instant value for the start of + * the five-minute window that contains the input instant. + * + * @param instant instant for which to evaluate the start of the containing window + * @param interval size of the window + * @param offset The window start offset. For example, a value of 'PT1m' would offset all windows by one minute. + * @return {@code null} if any input is {@code null}; otherwise, an {@link Instant} representing the start of the + * window + */ + @ScriptApi + @Nullable + public static Instant lowerBin(@Nullable final Instant instant, Duration interval, Duration offset) { + if (instant == null || interval == null || offset == null) { + return null; + } + + return lowerBin(instant, interval.toNanos(), offset.toNanos()); + } + /** * Returns a {@link ZonedDateTime} value, which is at the starting (lower) end of a time range defined by the - * interval nanoseconds. For example, a 5*MINUTE intervalNanos value would return the zoned date time value for the - * start of the five-minute window that contains the input zoned date time. + * interval nanoseconds. For example, a five-minute {@code intervalNanos} value would return the zoned date time + * value for the start of the five-minute window that contains the input zoned date time. * * @param dateTime zoned date time for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds * @param offset The window start offset in nanoseconds. For example, a value of MINUTE would offset all windows by * one minute. - * @return {@code null} if either input is {@code null}; otherwise, a {@link ZonedDateTime} representing the start - * of the window + * @return {@code null} if any input is {@code null}; otherwise, a {@link ZonedDateTime} representing the start of + * the window */ @ScriptApi @Nullable @@ -3235,10 +3296,31 @@ public static ZonedDateTime lowerBin(@Nullable final ZonedDateTime dateTime, lon dateTime.getZone()); } + /** + * Returns a {@link ZonedDateTime} value, which is at the starting (lower) end of a time range defined by the + * interval nanoseconds. For example, a five-minute {@code interval} intervalNanos value would return the zoned date + * time value for the start of the five-minute window that contains the input zoned date time. + * + * @param dateTime zoned date time for which to evaluate the start of the containing window + * @param interval size of the window + * @param offset The window start offset. For example, a value of MINUTE would offset all windows by one minute. + * @return {@code null} if any input is {@code null}; otherwise, a {@link ZonedDateTime} representing the start of + * the window + */ + @ScriptApi + @Nullable + public static ZonedDateTime lowerBin(@Nullable final ZonedDateTime dateTime, Duration interval, Duration offset) { + if (dateTime == null || interval == null || offset == null) { + return null; + } + + return lowerBin(dateTime, interval.toNanos(), offset.toNanos()); + } + /** * Returns an {@link Instant} value, which is at the ending (upper) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the instant value for the end of the - * five-minute window that contains the input instant. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the instant value for the end of + * the five-minute window that contains the input instant. * * @param instant instant for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds @@ -3255,10 +3337,30 @@ public static Instant upperBin(@Nullable final Instant instant, long intervalNan return epochNanosToInstant(Numeric.upperBin(epochNanos(instant), intervalNanos)); } + /** + * Returns an {@link Instant} value, which is at the ending (upper) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the instant value for the end of the + * five-minute window that contains the input instant. + * + * @param instant instant for which to evaluate the start of the containing window + * @param interval size of the window + * @return {@code null} if either input is {@code null}; otherwise, an {@link Instant} representing the end of the + * window + */ + @ScriptApi + @Nullable + public static Instant upperBin(@Nullable final Instant instant, Duration interval) { + if (instant == null || interval == null) { + return null; + } + + return upperBin(instant, interval.toNanos()); + } + /** * Returns a {@link ZonedDateTime} value, which is at the ending (upper) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the zoned date time value for the end of - * the five-minute window that contains the input zoned date time. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the zoned date time value for + * the end of the five-minute window that contains the input zoned date time. * * @param dateTime zoned date time for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds @@ -3275,16 +3377,36 @@ public static ZonedDateTime upperBin(@Nullable final ZonedDateTime dateTime, lon return epochNanosToZonedDateTime(Numeric.upperBin(epochNanos(dateTime), intervalNanos), dateTime.getZone()); } + /** + * Returns a {@link ZonedDateTime} value, which is at the ending (upper) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the zoned date time value for the end + * of the five-minute window that contains the input zoned date time. + * + * @param dateTime zoned date time for which to evaluate the start of the containing window + * @param interval size of the window + * @return {@code null} if either input is {@code null}; otherwise, a {@link ZonedDateTime} representing the end of + * the window + */ + @ScriptApi + @Nullable + public static ZonedDateTime upperBin(@Nullable final ZonedDateTime dateTime, Duration interval) { + if (dateTime == null || interval == null) { + return null; + } + + return upperBin(dateTime, interval.toNanos()); + } + /** * Returns an {@link Instant} value, which is at the ending (upper) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the instant value for the end of the - * five-minute window that contains the input instant. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the instant value for the end of + * the five-minute window that contains the input instant. * * @param instant instant for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds * @param offset The window start offset in nanoseconds. For example, a value of MINUTE would offset all windows by * one minute. - * @return {@code null} if either input is {@code null}; otherwise, an {@link Instant} representing the end of the + * @return {@code null} if any input is {@code null}; otherwise, an {@link Instant} representing the end of the * window */ @ScriptApi @@ -3298,17 +3420,38 @@ public static Instant upperBin(@Nullable final Instant instant, long intervalNan return epochNanosToInstant(Numeric.upperBin(epochNanos(instant) - offset, intervalNanos) + offset); } + /** + * Returns an {@link Instant} value, which is at the ending (upper) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the instant value for the end of the + * five-minute window that contains the input instant. + * + * @param instant instant for which to evaluate the start of the containing window + * @param interval size of the window + * @param offset The window start offset. For example, a value of 'PT1m' would offset all windows by one minute. + * @return {@code null} if any input is {@code null}; otherwise, an {@link Instant} representing the end of the + * window + */ + @ScriptApi + @Nullable + public static Instant upperBin(@Nullable final Instant instant, Duration interval, Duration offset) { + if (instant == null || interval == null || offset == null) { + return null; + } + + return upperBin(instant, interval.toNanos(), offset.toNanos()); + } + /** * Returns a {@link ZonedDateTime} value, which is at the ending (upper) end of a time range defined by the interval - * nanoseconds. For example, a 5*MINUTE intervalNanos value would return the zoned date time value for the end of - * the five-minute window that contains the input zoned date time. + * nanoseconds. For example, a five-minute {@code intervalNanos} value would return the zoned date time value for + * the end of the five-minute window that contains the input zoned date time. * * @param dateTime zoned date time for which to evaluate the start of the containing window * @param intervalNanos size of the window in nanoseconds * @param offset The window start offset in nanoseconds. For example, a value of MINUTE would offset all windows by * one minute. - * @return {@code null} if either input is {@code null}; otherwise, a {@link ZonedDateTime} representing the end of - * the window + * @return {@code null} if any input is {@code null}; otherwise, a {@link ZonedDateTime} representing the end of the + * window */ @ScriptApi @Nullable @@ -3322,6 +3465,27 @@ public static ZonedDateTime upperBin(@Nullable final ZonedDateTime dateTime, lon dateTime.getZone()); } + /** + * Returns a {@link ZonedDateTime} value, which is at the ending (upper) end of a time range defined by the interval + * nanoseconds. For example, a five-minute {@code interval} value would return the zoned date time value for the end + * of the five-minute window that contains the input zoned date time. + * + * @param dateTime zoned date time for which to evaluate the start of the containing window + * @param interval size of the window + * @param offset The window start offset. For example, a value of 'PT1m' would offset all windows by one minute. + * @return {@code null} if any input is {@code null}; otherwise, a {@link ZonedDateTime} representing the end of the + * window + */ + @ScriptApi + @Nullable + public static ZonedDateTime upperBin(@Nullable final ZonedDateTime dateTime, Duration interval, Duration offset) { + if (dateTime == null || interval == null || offset == null) { + return null; + } + + return upperBin(dateTime, interval.toNanos(), offset.toNanos()); + } + // endregion // region Format diff --git a/engine/time/src/test/java/io/deephaven/time/TestDateTimeUtils.java b/engine/time/src/test/java/io/deephaven/time/TestDateTimeUtils.java index 4bbe4dcf693..1e83430e8ac 100644 --- a/engine/time/src/test/java/io/deephaven/time/TestDateTimeUtils.java +++ b/engine/time/src/test/java/io/deephaven/time/TestDateTimeUtils.java @@ -1837,6 +1837,11 @@ public void testLowerBin() { TestCase.assertEquals(DateTimeUtils.lowerBin(instant, second), DateTimeUtils.lowerBin(DateTimeUtils.lowerBin(instant, second), second)); + TestCase.assertEquals(DateTimeUtils.lowerBin(instant, Duration.ofMinutes(1)), + DateTimeUtils.lowerBin(instant, minute)); + TestCase.assertNull(DateTimeUtils.lowerBin((Instant) null, Duration.ofMinutes(1))); + TestCase.assertNull(DateTimeUtils.lowerBin(instant, (Duration) null)); + final ZonedDateTime zdt = DateTimeUtils.toZonedDateTime(instant, TZ_AL); TestCase.assertEquals(DateTimeUtils.toZonedDateTime(DateTimeUtils.lowerBin(instant, second), TZ_AL), @@ -1854,6 +1859,10 @@ public void testLowerBin() { TestCase.assertEquals(DateTimeUtils.lowerBin(zdt, second), DateTimeUtils.lowerBin(DateTimeUtils.lowerBin(zdt, second), second)); + TestCase.assertEquals(DateTimeUtils.lowerBin(zdt, Duration.ofMinutes(1)), + DateTimeUtils.lowerBin(zdt, minute)); + TestCase.assertNull(DateTimeUtils.lowerBin((ZonedDateTime) null, Duration.ofMinutes(1))); + TestCase.assertNull(DateTimeUtils.lowerBin(zdt, (Duration) null)); } public void testLowerBinWithOffset() { @@ -1871,6 +1880,12 @@ public void testLowerBinWithOffset() { TestCase.assertEquals(DateTimeUtils.lowerBin(instant, second, second), DateTimeUtils.lowerBin(DateTimeUtils.lowerBin(instant, second, second), second, second)); + TestCase.assertEquals(DateTimeUtils.lowerBin(instant, Duration.ofMinutes(1), Duration.ofSeconds(2)), + DateTimeUtils.lowerBin(instant, minute, 2 * second)); + TestCase.assertNull(DateTimeUtils.lowerBin((Instant) null, Duration.ofMinutes(1), Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.lowerBin(instant, (Duration) null, Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.lowerBin(instant, Duration.ofMinutes(1), (Duration) null)); + final ZonedDateTime zdt = DateTimeUtils.toZonedDateTime(instant, TZ_AL); TestCase.assertEquals(DateTimeUtils.parseZonedDateTime("2010-06-15T06:11:00 NY").withZoneSameInstant(TZ_AL), @@ -1881,6 +1896,12 @@ public void testLowerBinWithOffset() { TestCase.assertEquals(DateTimeUtils.lowerBin(zdt, second, second), DateTimeUtils.lowerBin(DateTimeUtils.lowerBin(zdt, second, second), second, second)); + + TestCase.assertEquals(DateTimeUtils.lowerBin(zdt, Duration.ofMinutes(1), Duration.ofSeconds(2)), + DateTimeUtils.lowerBin(zdt, minute, 2 * second)); + TestCase.assertNull(DateTimeUtils.lowerBin((ZonedDateTime) null, Duration.ofMinutes(1), Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.lowerBin(zdt, (Duration) null, Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.lowerBin(zdt, Duration.ofMinutes(1), (Duration) null)); } public void testUpperBin() { @@ -1902,6 +1923,11 @@ public void testUpperBin() { TestCase.assertEquals(DateTimeUtils.upperBin(instant, second), DateTimeUtils.upperBin(DateTimeUtils.upperBin(instant, second), second)); + TestCase.assertEquals(DateTimeUtils.upperBin(instant, Duration.ofMinutes(1)), + DateTimeUtils.upperBin(instant, minute)); + TestCase.assertNull(DateTimeUtils.upperBin((Instant) null, Duration.ofMinutes(1))); + TestCase.assertNull(DateTimeUtils.upperBin(instant, (Duration) null)); + final ZonedDateTime zdt = DateTimeUtils.toZonedDateTime(instant, TZ_AL); TestCase.assertEquals(DateTimeUtils.parseZonedDateTime("2010-06-15T06:14:02 NY").withZoneSameInstant(TZ_AL), @@ -1915,6 +1941,11 @@ public void testUpperBin() { TestCase.assertEquals(DateTimeUtils.upperBin(zdt, second), DateTimeUtils.upperBin(DateTimeUtils.upperBin(zdt, second), second)); + + TestCase.assertEquals(DateTimeUtils.upperBin(zdt, Duration.ofMinutes(1)), + DateTimeUtils.upperBin(zdt, minute)); + TestCase.assertNull(DateTimeUtils.upperBin((ZonedDateTime) null, Duration.ofMinutes(1))); + TestCase.assertNull(DateTimeUtils.upperBin(zdt, (Duration) null)); } public void testUpperBinWithOffset() { @@ -1932,6 +1963,12 @@ public void testUpperBinWithOffset() { TestCase.assertEquals(DateTimeUtils.upperBin(instant, second, second), DateTimeUtils.upperBin(DateTimeUtils.upperBin(instant, second, second), second, second)); + TestCase.assertEquals(DateTimeUtils.upperBin(instant, Duration.ofMinutes(1), Duration.ofSeconds(2)), + DateTimeUtils.upperBin(instant, minute, 2 * second)); + TestCase.assertNull(DateTimeUtils.upperBin((Instant) null, Duration.ofMinutes(1), Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.upperBin(instant, (Duration) null, Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.upperBin(instant, Duration.ofMinutes(1), (Duration) null)); + final ZonedDateTime zdt = DateTimeUtils.toZonedDateTime(instant, TZ_AL); TestCase.assertEquals(DateTimeUtils.parseZonedDateTime("2010-06-15T06:16:00 NY").withZoneSameInstant(TZ_AL), @@ -1942,6 +1979,12 @@ public void testUpperBinWithOffset() { TestCase.assertEquals(DateTimeUtils.upperBin(zdt, second, second), DateTimeUtils.upperBin(DateTimeUtils.upperBin(zdt, second, second), second, second)); + + TestCase.assertEquals(DateTimeUtils.upperBin(zdt, Duration.ofMinutes(1), Duration.ofSeconds(2)), + DateTimeUtils.upperBin(zdt, minute, 2 * second)); + TestCase.assertNull(DateTimeUtils.upperBin((ZonedDateTime) null, Duration.ofMinutes(1), Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.upperBin(zdt, (Duration) null, Duration.ofSeconds(2))); + TestCase.assertNull(DateTimeUtils.upperBin(zdt, Duration.ofMinutes(1), (Duration) null)); } public void testPlusLocalDate() {