From 0d0e81438b39623f603088580cdb0b38beaf3425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Raupach?= Date: Sun, 2 Jan 2022 18:40:08 +0100 Subject: [PATCH 1/3] Added a factory method for rounding time. --- .../java/org/threeten/extra/RoundingMode.java | 7 ++ .../java/org/threeten/extra/Temporals.java | 52 +++++++++++++++ .../org/threeten/extra/TestTemporals.java | 64 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 src/main/java/org/threeten/extra/RoundingMode.java diff --git a/src/main/java/org/threeten/extra/RoundingMode.java b/src/main/java/org/threeten/extra/RoundingMode.java new file mode 100644 index 00000000..b743e176 --- /dev/null +++ b/src/main/java/org/threeten/extra/RoundingMode.java @@ -0,0 +1,7 @@ +package org.threeten.extra; + +public enum RoundingMode { + DOWN, + HALF_UP, + UP +} diff --git a/src/main/java/org/threeten/extra/Temporals.java b/src/main/java/org/threeten/extra/Temporals.java index 0b3f21c2..40adba4b 100644 --- a/src/main/java/org/threeten/extra/Temporals.java +++ b/src/main/java/org/threeten/extra/Temporals.java @@ -32,12 +32,16 @@ package org.threeten.extra; import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.WEEKS; import java.text.ParsePosition; +import java.time.Duration; import java.time.DateTimeException; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -59,6 +63,7 @@ *
  • adjusters that ignore Saturday/Sunday weekends *
  • conversion between {@code TimeUnit} and {@code ChronoUnit} *
  • converting an amount to another unit + *
  • adjusters that round time * * *

    Implementation Requirements:

    @@ -126,6 +131,53 @@ public static TemporalAdjuster previousWorkingDayOrSame() { return Adjuster.PREVIOUS_WORKING_OR_SAME; } + public static TemporalAdjuster roundTime(Duration duration, RoundingMode roundingMode) { + Objects.requireNonNull(duration, "duration"); + Objects.requireNonNull(roundingMode, "mode"); + + long minutes = duration.toMinutes(); + if (minutes < 1 || 60 % minutes != 0) { + throw new DateTimeException("duration is not a divisor of 60"); + } + + int divisor = (int) minutes; + + return temporal -> { + int minuteOfHour = temporal.get(MINUTE_OF_HOUR); + + temporal = temporal.with(SECOND_OF_MINUTE, 0) + .with(NANO_OF_SECOND, 0); + + if (minuteOfHour % divisor == 0) { + return temporal; + } + + int down = minuteOfHour / divisor * divisor; + int up = down + divisor; + RoundingMode mode = roundingMode; + + if (roundingMode == RoundingMode.HALF_UP) { + mode = (minuteOfHour - down) < (up - minuteOfHour) ? RoundingMode.DOWN : RoundingMode.UP; + } + + if (mode == RoundingMode.DOWN) { + temporal = temporal.with(MINUTE_OF_HOUR, down); + } + + if (mode == RoundingMode.UP) { + if (up == 60) { + if (temporal.isSupported(ChronoUnit.HOURS)) { + temporal = temporal.plus(1, ChronoUnit.HOURS); + } + up = 0; + } + temporal = temporal.with(MINUTE_OF_HOUR, up); + } + + return temporal; + }; + } + //----------------------------------------------------------------------- /** * Enum implementing the adjusters. diff --git a/src/test/java/org/threeten/extra/TestTemporals.java b/src/test/java/org/threeten/extra/TestTemporals.java index 62e2d99c..5958816d 100644 --- a/src/test/java/org/threeten/extra/TestTemporals.java +++ b/src/test/java/org/threeten/extra/TestTemporals.java @@ -65,7 +65,9 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; +import java.time.LocalTime; import java.time.LocalDate; +import java.time.Duration; import java.time.Month; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -670,4 +672,66 @@ public void test_convertAmountInvalidUnsupported(TemporalUnit fromUnit, Temporal assertThrows(UnsupportedTemporalTypeException.class, () -> Temporals.convertAmount(1, fromUnit, resultUnit)); } + + //----------------------------------------------------------------------- + // roundTime() + //----------------------------------------------------------------------- + @Test + public void test_roundTime_dateTimeException() { + assertThrows(DateTimeException.class, () -> Temporals.roundTime(Duration.ofMinutes(7), RoundingMode.DOWN)); + } + + @Test + public void test_roundTime_down_localTime() { + TemporalAdjuster down = Temporals.roundTime(Duration.ofMinutes(6), RoundingMode.DOWN); + + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 12).with(down)); + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 13).with(down)); + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 14).with(down)); + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 15).with(down)); + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 16).with(down)); + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 17).with(down)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 18).with(down)); + + } + + @Test + public void test_roundTime_up_localTime() { + TemporalAdjuster up = Temporals.roundTime(Duration.ofMinutes(6), RoundingMode.UP); + + assertEquals(LocalTime.of(14, 12), LocalTime.of(14, 12, 7, 1).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 13, 7, 1).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 14).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 15).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 16).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 17).with(up)); + assertEquals(LocalTime.of(14, 18), LocalTime.of(14, 18).with(up)); + } + + @Test + public void test_roundTime_halfUp_localTime() { + TemporalAdjuster halfUp = Temporals.roundTime(Duration.ofMinutes(6), RoundingMode.HALF_UP); + + assertEquals(LocalTime.of(14, 54), LocalTime.of(14, 54, 7, 1).with(halfUp)); + assertEquals(LocalTime.of(14, 54), LocalTime.of(14, 55, 7, 1).with(halfUp)); + assertEquals(LocalTime.of(14, 54), LocalTime.of(14, 56).with(halfUp)); + assertEquals(LocalTime.of(15, 0), LocalTime.of(14, 57).with(halfUp)); + assertEquals(LocalTime.of(15, 0), LocalTime.of(14, 58).with(halfUp)); + assertEquals(LocalTime.of(15, 0), LocalTime.of(14, 59).with(halfUp)); + assertEquals(LocalTime.of(15, 0), LocalTime.of(15, 0).with(halfUp)); + } + + @Test + public void test_roundTime_halfUp_localDateTime() { + TemporalAdjuster halfUp = Temporals.roundTime(Duration.ofMinutes(6), RoundingMode.HALF_UP); + + assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54, 7, 1)).with(halfUp)); + assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 55, 7, 1)).with(halfUp)); + assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 56)).with(halfUp)); + assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 57)).with(halfUp)); + assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 58)).with(halfUp)); + assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 59)).with(halfUp)); + assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)).with(halfUp)); + } + } From 37a70ee2027c04df8f8f9683b3ce387a1ee4c9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Raupach?= Date: Sun, 2 Jan 2022 18:54:18 +0100 Subject: [PATCH 2/3] Removed broken test. --- src/test/java/org/threeten/extra/TestTemporals.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/test/java/org/threeten/extra/TestTemporals.java b/src/test/java/org/threeten/extra/TestTemporals.java index 5958816d..4d14c0e0 100644 --- a/src/test/java/org/threeten/extra/TestTemporals.java +++ b/src/test/java/org/threeten/extra/TestTemporals.java @@ -721,17 +721,4 @@ public void test_roundTime_halfUp_localTime() { assertEquals(LocalTime.of(15, 0), LocalTime.of(15, 0).with(halfUp)); } - @Test - public void test_roundTime_halfUp_localDateTime() { - TemporalAdjuster halfUp = Temporals.roundTime(Duration.ofMinutes(6), RoundingMode.HALF_UP); - - assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54, 7, 1)).with(halfUp)); - assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 55, 7, 1)).with(halfUp)); - assertEquals(LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 54)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 56)).with(halfUp)); - assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 57)).with(halfUp)); - assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 58)).with(halfUp)); - assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2021, DECEMBER, 31).with(LocalTime.of(23, 59)).with(halfUp)); - assertEquals(LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)), LocalDate.of(2022, JANUARY, 1).with(LocalTime.of(0, 0)).with(halfUp)); - } - } From 8669eb6a0256ba2991dde939b93872e381c8733a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Raupach?= Date: Thu, 6 Jan 2022 13:40:20 +0100 Subject: [PATCH 3/3] Added some more documentation. Still a work in progress. --- src/main/java/org/threeten/extra/RoundingMode.java | 10 ++++++++++ src/main/java/org/threeten/extra/Temporals.java | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/org/threeten/extra/RoundingMode.java b/src/main/java/org/threeten/extra/RoundingMode.java index b743e176..27237542 100644 --- a/src/main/java/org/threeten/extra/RoundingMode.java +++ b/src/main/java/org/threeten/extra/RoundingMode.java @@ -1,5 +1,15 @@ package org.threeten.extra; +/** + * A time clock rounding mode. + *

    + * {@code RoundingMode} specifies a time clock rounding strategy. + *

    + * Rounding mode defines the following strategies: + *

  • DOWN - rounds the time towards the start of a fraction + *
  • HALF_UP - rounds towards the "nearest neighbor". UP, if the distance is of equal length + *
  • UP - rounds the time towards the end of a fraction + */ public enum RoundingMode { DOWN, HALF_UP, diff --git a/src/main/java/org/threeten/extra/Temporals.java b/src/main/java/org/threeten/extra/Temporals.java index 40adba4b..db5b7294 100644 --- a/src/main/java/org/threeten/extra/Temporals.java +++ b/src/main/java/org/threeten/extra/Temporals.java @@ -131,6 +131,19 @@ public static TemporalAdjuster previousWorkingDayOrSame() { return Adjuster.PREVIOUS_WORKING_OR_SAME; } + /** + * Returns an adjuster that offers time clock rounding. + *

    + * Time clock rounding divides the minutes of the hour into equal fractions of the same length. Every fraction has + * a start-inclusive and end-exclusive minute of the hour. A rounded result is either at the lower or upper end of + * one of these fractions. + *

    + * Time clock rounding views time as hour-minute. Therefore it always truncates to minutes. + * + * @param duration the fraction of the hour, must be a divisor of 60 + * @param roundingMode the time clock rounding mode + * @return time rounded to a fraction of the hour + */ public static TemporalAdjuster roundTime(Duration duration, RoundingMode roundingMode) { Objects.requireNonNull(duration, "duration"); Objects.requireNonNull(roundingMode, "mode");