From 314f1c7c896d69cd3664775663eaa7ef528a1801 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Tue, 27 Apr 2021 11:31:26 +0200 Subject: [PATCH 1/2] Fix for #183 Interval toString/parse support for unbounded intervals --- .../java/org/threeten/extra/Interval.java | 17 ++++++++-- .../java/org/threeten/extra/TestInterval.java | 33 +++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/threeten/extra/Interval.java b/src/main/java/org/threeten/extra/Interval.java index e7fc5358..3350f36e 100644 --- a/src/main/java/org/threeten/extra/Interval.java +++ b/src/main/java/org/threeten/extra/Interval.java @@ -183,6 +183,9 @@ private static Interval parseSplit(CharSequence startStr, CharSequence endStr) { } } // instant followed by instant or duration + if (isUnbounded(startStr)) { + return parseEndDateTime(Instant.MIN, ZoneOffset.UTC, endStr); + } OffsetDateTime start; try { start = OffsetDateTime.parse(startStr); @@ -199,6 +202,10 @@ private static Interval parseSplit(CharSequence startStr, CharSequence endStr) { return parseEndDateTime(start.toInstant(), start.getOffset(), endStr); } + private static boolean isUnbounded(CharSequence sequence) { + return sequence.length() == 2 && sequence.charAt(0) == '.' && sequence.charAt(1) == '.'; + } + // handle case where Instant is outside the bounds of OffsetDateTime private static Interval parseStartExtended(CharSequence startStr, CharSequence endStr) { Instant start = Instant.parse(startStr); @@ -219,6 +226,10 @@ private static Interval parseStartExtended(CharSequence startStr, CharSequence e // parse when there are two date-times private static Interval parseEndDateTime(Instant start, ZoneOffset offset, CharSequence endStr) { + if (isUnbounded(endStr)) { + return Interval.of(start, Instant.MAX); + } + try { TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(endStr, OffsetDateTime::from, LocalDateTime::from); if (temporal instanceof OffsetDateTime) { @@ -708,13 +719,15 @@ public int hashCode() { *

* The output will be the ISO-8601 format formed by combining the * {@code toString()} methods of the two instants, separated by a forward slash. + * Unbounded intervals will have its open end represented by two periods, as in + * {@code 2007-12-03T10:15:30/..}. * - * @return a string representation of this instant, not null + * @return a string representation of this interval, not null */ @Override @ToString public String toString() { - return start.toString() + '/' + end.toString(); + return (isUnboundedStart() ? ".." : getStart()) + "/" + (isUnboundedEnd() ? ".." : getEnd()); } } diff --git a/src/test/java/org/threeten/extra/TestInterval.java b/src/test/java/org/threeten/extra/TestInterval.java index 5f3c4d5f..cadecd7a 100644 --- a/src/test/java/org/threeten/extra/TestInterval.java +++ b/src/test/java/org/threeten/extra/TestInterval.java @@ -196,8 +196,11 @@ public static Object[][] data_parseValid() { {NOW1.atOffset(ZoneOffset.ofHours(2)) + "/" + NOW2.atOffset(ZoneOffset.ofHours(2)).toLocalDateTime(), NOW1, NOW2}, {MIN_OFFSET_DATE_TIME.toString() + "/" + MAX_OFFSET_DATE_TIME, MIN_OFFSET_DATE_TIME, MAX_OFFSET_DATE_TIME}, {NOW1 + "/" + Instant.MAX, NOW1, Instant.MAX}, - {Instant.MIN.toString() + "/" + NOW2, Instant.MIN, NOW2}, - {Instant.MIN.toString() + "/" + Instant.MAX, Instant.MIN, Instant.MAX} + {Instant.MIN + "/" + NOW2, Instant.MIN, NOW2}, + {Instant.MIN + "/" + Instant.MAX, Instant.MIN, Instant.MAX}, + {"../" + NOW2, Instant.MIN, NOW2}, + {NOW1 + "/..", NOW1, Instant.MAX}, + {"../..", Instant.MIN, Instant.MAX} }; } @@ -214,6 +217,16 @@ public void test_parse_CharSequence_badOrder() { assertThrows(DateTimeException.class, () -> Interval.parse(NOW2 + "/" + NOW1)); } + @Test + public void test_parse_CharSequence_illegalOpenStartDuration() { + assertThrows(DateTimeException.class, () -> Interval.parse("../P1H")); + } + + @Test + public void test_parse_CharSequence_illegalDurationOpenEnd() { + assertThrows(DateTimeException.class, () -> Interval.parse("P1H/..")); + } + @Test public void test_parse_CharSequence_badFormat() { assertThrows(DateTimeParseException.class, () -> Interval.parse(NOW2 + "-" + NOW1)); @@ -843,4 +856,20 @@ public void test_toString() { assertEquals(NOW1 + "/" + NOW2, test.toString()); } + @Test + public void test_toString_open_end() { + Interval test = Interval.ALL.withStart(NOW1); + assertEquals(NOW1 + "/..", test.toString()); + } + @Test + public void test_toString_open_start() { + Interval test = Interval.ALL.withEnd(NOW2); + assertEquals("../" + NOW2, test.toString()); + } + @Test + public void test_toString_open_all() { + Interval test = Interval.ALL; + assertEquals("../..", test.toString()); + } + } From 75e10950cd69b597304d108da79fbab1df380c44 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Tue, 27 Apr 2021 11:40:10 +0200 Subject: [PATCH 2/2] #183 Fix incorrect duration syntax in test to avoid false positives --- src/test/java/org/threeten/extra/TestInterval.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/threeten/extra/TestInterval.java b/src/test/java/org/threeten/extra/TestInterval.java index cadecd7a..6acdaa37 100644 --- a/src/test/java/org/threeten/extra/TestInterval.java +++ b/src/test/java/org/threeten/extra/TestInterval.java @@ -219,12 +219,12 @@ public void test_parse_CharSequence_badOrder() { @Test public void test_parse_CharSequence_illegalOpenStartDuration() { - assertThrows(DateTimeException.class, () -> Interval.parse("../P1H")); + assertThrows(DateTimeException.class, () -> Interval.parse("../PT1H")); } @Test public void test_parse_CharSequence_illegalDurationOpenEnd() { - assertThrows(DateTimeException.class, () -> Interval.parse("P1H/..")); + assertThrows(DateTimeException.class, () -> Interval.parse("PT1H/..")); } @Test