From a78549d41cca2fa097aaf0919ae9c4372ed56549 Mon Sep 17 00:00:00 2001 From: Morten Haraldsen Date: Fri, 5 Jan 2024 13:56:04 +0100 Subject: [PATCH] Have DateTime implement TemporalAccessor (#20) * Have DateTime implement TemporalAccessor --------- Co-authored-by: Morten Haraldsen --- src/main/java/com/ethlo/time/DateTime.java | 89 ++++++-- src/main/java/com/ethlo/time/Field.java | 36 ++++ .../com/ethlo/time/internal/EthloITU.java | 14 +- .../internal/LimitedCharArrayIntegerUtil.java | 13 -- .../java/com/ethlo/time/CorrectnessTest.java | 191 +++++++++++++----- 5 files changed, 258 insertions(+), 85 deletions(-) diff --git a/src/main/java/com/ethlo/time/DateTime.java b/src/main/java/com/ethlo/time/DateTime.java index 23ccbe8..64fbac0 100644 --- a/src/main/java/com/ethlo/time/DateTime.java +++ b/src/main/java/com/ethlo/time/DateTime.java @@ -31,6 +31,10 @@ import java.time.OffsetDateTime; import java.time.Year; import java.time.YearMonth; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Objects; import java.util.Optional; @@ -39,7 +43,7 @@ /** * Container class for parsed date/date-time data. The {@link #getMostGranularField()} contains the highest granularity field found, like MONTH, MINUTE, SECOND. */ -public class DateTime +public class DateTime implements TemporalAccessor { private final Field field; private final int year; @@ -69,10 +73,10 @@ public DateTime(final Field field, final int year, final int month, final int da /** * Create a new instance with second granularity from the input parameters * - * @param year year - * @param month month - * @param day day - * @param hour hour + * @param year year + * @param month month + * @param day day + * @param hour hour * @param minute minute * @param second second * @param offset timezone offset @@ -86,14 +90,14 @@ public static DateTime of(int year, int month, int day, int hour, int minute, in /** * Create a new instance with nanosecond granularity from the input parameters * - * @param year year - * @param month month - * @param day day - * @param hour hour - * @param minute minute - * @param second second - * @param nanos nanos - * @param offset timezone offset + * @param year year + * @param month month + * @param day day + * @param hour hour + * @param minute minute + * @param second second + * @param nanos nanos + * @param offset timezone offset * @param fractionDigits The granularity of the fractional seconds field * @return A DateTime with nanosecond granularity */ @@ -104,6 +108,7 @@ public static DateTime of(int year, int month, int day, int hour, int minute, in /** * Create a new instance with year granularity from the input parameters + * * @param year The year * @return a new instance with year granularity from the input parameters */ @@ -114,7 +119,8 @@ public static DateTime ofYear(int year) /** * Create a new instance with year-month granularity from the input parameters - * @param year The year + * + * @param year The year * @param month The month * @return a new instance with year-month granularity from the input parameters */ @@ -125,9 +131,10 @@ public static DateTime ofYearMonth(int year, int month) /** * Create a new instance with day granularity from the input parameters - * @param year The year + * + * @param year The year * @param month The month - * @param day The day + * @param day The day * @return a new instance with day granularity from the input parameters */ public static DateTime ofDate(int year, int month, int day) @@ -137,10 +144,11 @@ public static DateTime ofDate(int year, int month, int day) /** * Create a new instance with minute granularity from the input parameters - * @param year The year - * @param month The month - * @param day The day - * @param hour The hour + * + * @param year The year + * @param month The month + * @param day The day + * @param hour The hour * @param minute The minute * @param offset The timezone offset * @return a new instance with minute granularity from the input parameters @@ -459,4 +467,45 @@ public int hashCode() { return Objects.hash(field.ordinal(), year, month, day, hour, minute, second, nano, offset, fractionDigits); } + + @Override + public boolean isSupported(final TemporalField field) + { + return Field.of(field).ordinal() <= this.field.ordinal(); + } + + @SuppressWarnings("DuplicatedCode") + @Override + public long getLong(final TemporalField temporalField) + { + if (temporalField.equals(ChronoField.YEAR)) + { + return year; + } + else if (temporalField.equals(ChronoField.MONTH_OF_YEAR)) + { + return month; + } + else if (temporalField.equals(ChronoField.DAY_OF_MONTH)) + { + return day; + } + else if (temporalField.equals(ChronoField.HOUR_OF_DAY)) + { + return hour; + } + else if (temporalField.equals(ChronoField.MINUTE_OF_HOUR)) + { + return minute; + } + else if (temporalField.equals(ChronoField.SECOND_OF_MINUTE)) + { + return second; + } + else if (temporalField.equals(ChronoField.NANO_OF_SECOND)) + { + return nano; + } + throw new UnsupportedTemporalTypeException("Unsupported field: " + temporalField); + } } diff --git a/src/main/java/com/ethlo/time/Field.java b/src/main/java/com/ethlo/time/Field.java index fdbdd14..f6b4739 100644 --- a/src/main/java/com/ethlo/time/Field.java +++ b/src/main/java/com/ethlo/time/Field.java @@ -24,7 +24,10 @@ import java.time.OffsetDateTime; import java.time.Year; import java.time.YearMonth; +import java.time.temporal.ChronoField; import java.time.temporal.Temporal; +import java.time.temporal.TemporalField; +import java.time.temporal.UnsupportedTemporalTypeException; /** * Enumeration of the fields that makes up the date/date-time @@ -72,4 +75,37 @@ public int getRequiredLength() { return requiredLength; } + + public static Field of(TemporalField temporalField) + { + if (temporalField.equals(ChronoField.YEAR)) + { + return YEAR; + } + else if (temporalField.equals(ChronoField.MONTH_OF_YEAR)) + { + return MONTH; + } + else if (temporalField.equals(ChronoField.DAY_OF_MONTH)) + { + return DAY; + } + else if (temporalField.equals(ChronoField.HOUR_OF_DAY)) + { + return HOUR; + } + else if (temporalField.equals(ChronoField.MINUTE_OF_HOUR)) + { + return MINUTE; + } + else if (temporalField.equals(ChronoField.SECOND_OF_MINUTE)) + { + return SECOND; + } + else if (temporalField.equals(ChronoField.NANO_OF_SECOND)) + { + return NANO; + } + throw new UnsupportedTemporalTypeException("Unsupported field: " + temporalField); + } } diff --git a/src/main/java/com/ethlo/time/internal/EthloITU.java b/src/main/java/com/ethlo/time/internal/EthloITU.java index 3d0754e..039d524 100644 --- a/src/main/java/com/ethlo/time/internal/EthloITU.java +++ b/src/main/java/com/ethlo/time/internal/EthloITU.java @@ -130,14 +130,11 @@ private static Object handleTime(String chars, int year, int month, int day, int case ZULU_UPPER: case ZULU_LOWER: final TimezoneOffset zoneOffset = parseTimezone(chars, 16); - if (! raw) + if (!raw) { throw raiseMissingField(Field.SECOND); } return DateTime.of(year, month, day, hour, minute, zoneOffset); - - default: - assertPositionContains(chars, 16, TIME_SEPARATOR, PLUS, MINUS, ZULU_UPPER); } throw new DateTimeException(chars); } @@ -373,7 +370,7 @@ private static Object parse(String chars, boolean raw) final int months = parsePositiveInt(chars, 5, 7); if (7 == len) { - if (! raw) + if (!raw) { throw raiseMissingField(Field.SECOND); } @@ -385,7 +382,7 @@ private static Object parse(String chars, boolean raw) final int days = parsePositiveInt(chars, 8, 10); if (10 == len) { - if (! raw) + if (!raw) { throw raiseMissingField(Field.SECOND); } @@ -466,7 +463,10 @@ private static Object handleTime(int year, int month, int day, int hour, int min { fractionDigits = idx - 20; fractions = scale(-result, fractionDigits); - offset = parseTimezone(chars, idx); + if (!raw) + { + offset = parseTimezone(chars, idx); + } } } else if (remaining == 1 && (c == ZULU_UPPER || c == ZULU_LOWER)) diff --git a/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java b/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java index b989b85..f5e1bbe 100644 --- a/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java +++ b/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java @@ -129,17 +129,4 @@ private static void copy(char[] buf, int srcPos, char[] target, int offset, int { System.arraycopy(buf, srcPos, target, offset, length); } - - public static int indexOfNonDigit(final char[] text, int offset) - { - for (int i = offset; i < text.length; i++) - { - char c = text[i]; - if (c < ZERO || c > DIGIT_9) - { - return i; - } - } - return -1; - } } diff --git a/src/test/java/com/ethlo/time/CorrectnessTest.java b/src/test/java/com/ethlo/time/CorrectnessTest.java index 60fd84b..dc425cc 100644 --- a/src/test/java/com/ethlo/time/CorrectnessTest.java +++ b/src/test/java/com/ethlo/time/CorrectnessTest.java @@ -25,6 +25,9 @@ import java.time.DateTimeException; import java.time.OffsetDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -54,24 +57,33 @@ void testValid(String valid) @ParameterizedTest @ValueSource(strings = { - "2017-02-21T15:27:39.+00:00", - "2017-02-21T15:27:39", "2017-02-21T15:27:39.123", - "2017-02-21T15:27:39.123456", "2017-02-21T15:27:39.123456789", - "2017-02-21T15:27:39+0000", "2017-02-21T15:27:39.123+0000", - "201702-21T15:27:39.123456+0000", "20170221T15:27:39.123456789+0000"}) + "2017-02-21T15", + "2017-02-21T15Z", + "2017-02-21T15:27", + "2017-02-21T15:27Z", + "2017-02-21T15:27:19~10:00", + "2017-02-21T15:27:39.+00:00", // No fractions after dot + "2017-02-21T15:27:39", // No timezone + "2017-02-21T15:27:39.123", + "2017-02-21T15:27:39.123456", + "2017-02-21T15:27:39.123456789", + "2017-02-21T15:27:39+0000", + "2017-02-21T15:27:39.123+0000", + "201702-21T15:27:39.123456+0000", + "20170221T15:27:39.123456789+0000"}) void testInvalid(final String invalid) { assertThrows(DateTimeException.class, () -> parser.parseDateTime(invalid)); } @Test - public void testParseLeapSecondUTC() + void testParseLeapSecondUTC() { verifyLeapSecondDateTime("1990-12-31T23:59:60Z", "1991-01-01T00:00:00Z", true); } @Test - public void testParseDoubleLeapSecondUTC() + void testParseDoubleLeapSecondUTC() { assertThrows(DateTimeException.class, () -> verifyLeapSecondDateTime("1990-12-31T23:59:61Z", "1991-01-01T00:00:01Z", true)); } @@ -85,46 +97,46 @@ private void verifyLeapSecondDateTime(String input, String expectedInUtc, boolea } @Test - public void testParseLeapSecondPST() + void testParseLeapSecondPST() { verifyLeapSecondDateTime("1990-12-31T15:59:60-08:00", "1991-01-01T00:00:00Z", true); } @Test - public void parseWithFragmentsNoTimezone() + void parseWithFragmentsNoTimezone() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T12:20:45.987")); assertThat(exception.getMessage()).isEqualTo("No timezone information: 2017-12-21T12:20:45.987"); } @Test - public void parseWithFragmentsNonDigit() + void parseWithFragmentsNonDigit() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T12:20:45.9b7Z")); assertThat(exception.getMessage()).isEqualTo("Invalid character starting at position 21: 2017-12-21T12:20:45.9b7Z"); } @Test - public void testParseLeapSecondUTCJune() + void testParseLeapSecondUTCJune() { final String leapSecondUTC = "1992-06-30T23:59:60Z"; verifyLeapSecondDateTime(leapSecondUTC, "1992-07-01T00:00:00Z", true); } @Test - public void testParseLeapSecondPSTJune() + void testParseLeapSecondPSTJune() { verifyLeapSecondDateTime("1992-06-30T15:59:60-08:00", "1992-07-01T00:00:00Z", true); } @Test - public void testParseLeapSecond() + void testParseLeapSecond() { verifyLeapSecondDateTime("1990-12-31T15:59:60-08:00", "1991-01-01T00:00:00Z", true); } @Test - public void testParseLeapSecondPotentiallyCorrect() + void testParseLeapSecondPotentiallyCorrect() { verifyLeapSecondDateTime("2032-06-30T15:59:60-08:00", "2032-07-01T00:00:00Z", false); } @@ -143,7 +155,7 @@ private LeapSecondException getLeapSecondsException(final String dateTime) } @Test - public void testFormat1() + void testFormat1() { final String s = "2017-02-21T15:27:39.0000000"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(s)); @@ -151,7 +163,7 @@ public void testFormat1() } @Test - public void testFormat2() + void testFormat2() { final String s = "2017-02-21T15:27:39.000+30:00"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(s)); @@ -159,7 +171,7 @@ public void testFormat2() } @Test - public void testFormat3() + void testFormat3() { final String s = "2017-02-21T10:00:00.000+12:00"; final OffsetDateTime date = parser.parseDateTime(s); @@ -167,7 +179,7 @@ public void testFormat3() } @Test - public void testInvalidNothingAfterFractionalSeconds() + void testInvalidNothingAfterFractionalSeconds() { final String s = "2017-02-21T10:00:00.12345"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(s)); @@ -175,14 +187,14 @@ public void testInvalidNothingAfterFractionalSeconds() } @Test - public void parseWithoutSeconds() + void parseWithoutSeconds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T12:20Z")); assertThat(exception.getMessage()).isEqualTo("No SECOND field found"); } @Test - public void testFormat4() + void testFormat4() { final String s = "2017-02-21T15:00:00.123Z"; final OffsetDateTime date = parser.parseDateTime(s); @@ -190,49 +202,49 @@ public void testFormat4() } @Test - public void testParseMoreThanNanoResolutionFails() + void testParseMoreThanNanoResolutionFails() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-02-21T15:00:00.1234567891Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for NanoOfSecond (valid values 0 - 999999999): 1234567891"); } @Test - public void testParseMonthOutOfBounds() + void testParseMonthOutOfBounds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-13-21T15:00:00Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for MonthOfYear (valid values 1 - 12): 13"); } @Test - public void testParseDayOutOfBounds() + void testParseDayOutOfBounds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-11-32T15:00:00Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for DayOfMonth (valid values 1 - 28/31): 32"); } @Test - public void testParseHourOutOfBounds() + void testParseHourOutOfBounds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T24:00:00Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for HourOfDay (valid values 0 - 23): 24"); } @Test - public void testParseMinuteOfBounds() + void testParseMinuteOfBounds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T23:60:00Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for MinuteOfHour (valid values 0 - 59): 60"); } @Test - public void testParseSecondOutOfBounds() + void testParseSecondOutOfBounds() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T23:00:61Z")); assertThat(exception.getMessage()).isEqualTo("Invalid value for SecondOfMinute (valid values 0 - 59): 61"); } @Test - public void testFormatMoreThanNanoResolutionFails() + void testFormatMoreThanNanoResolutionFails() { final OffsetDateTime d = parser.parseDateTime("2017-02-21T15:00:00.123456789Z"); final int fractionDigits = 10; @@ -241,7 +253,7 @@ public void testFormatMoreThanNanoResolutionFails() } @Test - public void testFormatUtc() + void testFormatUtc() { final String s = "2017-02-21T15:09:03.123456789Z"; final OffsetDateTime date = parser.parseDateTime(s); @@ -251,7 +263,7 @@ public void testFormatUtc() } @Test - public void testFormatUtcMilli() + void testFormatUtcMilli() { final String s = "2017-02-21T15:00:00.123456789Z"; final OffsetDateTime date = parser.parseDateTime(s); @@ -259,7 +271,7 @@ public void testFormatUtcMilli() } @Test - public void testFormatUtcMicro() + void testFormatUtcMicro() { final String s = "2017-02-21T15:00:00.123456789Z"; final OffsetDateTime date = parser.parseDateTime(s); @@ -267,7 +279,7 @@ public void testFormatUtcMicro() } @Test - public void testFormatUtcNano() + void testFormatUtcNano() { final String s = "2017-02-21T15:00:00.987654321Z"; final OffsetDateTime date = parser.parseDateTime(s); @@ -275,7 +287,7 @@ public void testFormatUtcNano() } @Test - public void testFormat4TrailingNoise() + void testFormat4TrailingNoise() { final String s = "2017-02-21T15:00:00.123ZGGG"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(s)); @@ -283,7 +295,7 @@ public void testFormat4TrailingNoise() } @Test - public void testFormat5() + void testFormat5() { final String s = "2017-02-21T15:27:39.123+13:00"; final OffsetDateTime date = parser.parseDateTime(s); @@ -291,21 +303,21 @@ public void testFormat5() } @Test - public void testParseEmptyString() + void testParseEmptyString() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("")); assertThat(exception.getMessage()).isEqualTo("Unexpected end of expression at position 0: ''"); } @Test - public void testParseNull() + void testParseNull() { final NullPointerException exception = assertThrows(NullPointerException.class, () -> parser.parseDateTime(null)); assertThat(exception.getMessage()).isEqualTo("text cannot be null"); } @Test - public void testRfcExample() + void testRfcExample() { // 1994-11-05T08:15:30-05:00 corresponds to November 5, 1994, 8:15:30 am, US Eastern Standard Time/ // 1994-11-05T13:15:30Z corresponds to the same instant. @@ -317,14 +329,14 @@ public void testRfcExample() } @Test - public void testBadSeparator() + void testBadSeparator() { final String a = "1994 11-05T08:15:30-05:00"; assertThrows(DateTimeException.class, () -> parser.parseDateTime(a)); } @Test - public void testParseNonDigit() + void testParseNonDigit() { final String a = "199g-11-05T08:15:30-05:00"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(a)); @@ -332,7 +344,7 @@ public void testParseNonDigit() } @Test - public void testInvalidDateTimeSeparator() + void testInvalidDateTimeSeparator() { final String a = "1994-11-05X08:15:30-05:00"; final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime(a)); @@ -340,34 +352,123 @@ public void testInvalidDateTimeSeparator() } @Test - public void testLowerCaseTseparator() + void testLowerCaseTseparator() { final String a = "1994-11-05t08:15:30z"; assertThat(parser.parseDateTime(a)).isNotNull(); } @Test - public void testSpaceAsSeparator() + void testSpaceAsSeparator() { assertThat(parser.parseDateTime("1994-11-05 08:15:30z")).isNotNull(); } @Test - public void testMilitaryOffset() + void testMilitaryOffset() { assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-02-21T15:27:39+0000")); } @Test - public void testParseUnknownLocalOffsetConvention() + void testParseUnknownLocalOffsetConvention() { final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-02-21T15:27:39-00:00")); assertThat(exception.getMessage()).isEqualTo("Unknown 'Local Offset Convention' date-time not allowed"); } @Test - public void testParseLowercaseZ() + void testParseLowercaseZ() { - parser.parseDateTime("2017-02-21T15:27:39.000z"); + assertThat(parser.parseDateTime("2017-02-21T15:27:39.000z")).isEqualTo(OffsetDateTime.parse("2017-02-21T15:27:39.000z")); + } + + @Test + void testTemporalAccessorYear() + { + final TemporalAccessor parsed = ITU.parseLenient("2017"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isFalse(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isFalse(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isFalse(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isFalse(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isFalse(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isFalse(); + assertThat(parsed.getLong(ChronoField.YEAR)).isEqualTo(2017); + } + + @Test + void testTemporalAccessorMonth() + { + final TemporalAccessor parsed = ITU.parseLenient("2017-01"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isFalse(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isFalse(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isFalse(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isFalse(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isFalse(); + + assertThat(parsed.getLong(ChronoField.MONTH_OF_YEAR)).isEqualTo(1); + } + + @Test + void testTemporalAccessorDay() + { + final TemporalAccessor parsed = ITU.parseLenient("2017-01-27"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isTrue(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isFalse(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isFalse(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isFalse(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isFalse(); + + assertThat(parsed.getLong(ChronoField.DAY_OF_MONTH)).isEqualTo(27); + } + + @Test + void testTemporalAccessorMinute() + { + final TemporalAccessor parsed = ITU.parseLenient("2017-01-27T15:34"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isTrue(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isFalse(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isFalse(); + + assertThat(parsed.getLong(ChronoField.MINUTE_OF_HOUR)).isEqualTo(34); + } + + @Test + void testTemporalAccessorSecond() + { + final TemporalAccessor parsed = ITU.parseLenient("2017-01-27T15:34:49"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isTrue(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isTrue(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isFalse(); + + assertThat(parsed.getLong(ChronoField.SECOND_OF_MINUTE)).isEqualTo(49); + } + + @Test + void testTemporalAccessorNanos() + { + final TemporalAccessor parsed = ITU.parseLenient("2017-01-27T15:34:49.987654321"); + assertThat(parsed.isSupported(ChronoField.YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MONTH_OF_YEAR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.DAY_OF_MONTH)).isTrue(); + assertThat(parsed.isSupported(ChronoField.HOUR_OF_DAY)).isTrue(); + assertThat(parsed.isSupported(ChronoField.MINUTE_OF_HOUR)).isTrue(); + assertThat(parsed.isSupported(ChronoField.SECOND_OF_MINUTE)).isTrue(); + assertThat(parsed.isSupported(ChronoField.NANO_OF_SECOND)).isTrue(); + + assertThat(parsed.getLong(ChronoField.NANO_OF_SECOND)).isEqualTo(987654321); } }