From 891e64892811b5bf7ee7e18a01b40ca01e62710b Mon Sep 17 00:00:00 2001 From: Morten Haraldsen Date: Wed, 31 Jan 2024 17:24:45 +0100 Subject: [PATCH] Fix issue where parsing leniently with ParsePosition and non-zero offset would return wrong position after parsing. --- .../com/ethlo/time/internal/ITUParser.java | 16 +- src/test/java/com/ethlo/time/ITUTest.java | 128 ----------- .../com/ethlo/time/ParsePositionTest.java | 210 ++++++++++++++++++ 3 files changed, 218 insertions(+), 136 deletions(-) create mode 100644 src/test/java/com/ethlo/time/ParsePositionTest.java diff --git a/src/main/java/com/ethlo/time/internal/ITUParser.java b/src/main/java/com/ethlo/time/internal/ITUParser.java index d2bceb1..9b4b254 100644 --- a/src/main/java/com/ethlo/time/internal/ITUParser.java +++ b/src/main/java/com/ethlo/time/internal/ITUParser.java @@ -168,15 +168,15 @@ public static DateTime parseLenient(final String chars, final ParseConfig parseC final int years = parsePositiveInt(chars, offset, offset + 4); if (4 == availableLength) { - return DateTime.ofYear(years); + return new DateTime(Field.YEAR, years, 0, 0, 0, 0, 0, 0, null, 0, availableLength); } // MONTH assertPositionContains(Field.MONTH, chars, offset + 4, DATE_SEPARATOR); - final int months = parsePositiveInt(chars, offset + 5, offset + 7); + final int month = parsePositiveInt(chars, offset + 5, offset + 7); if (7 == availableLength) { - return DateTime.ofYearMonth(years, months); + return new DateTime(Field.MONTH, years, month, 0, 0, 0, 0, 0, null, 0, availableLength); } // DAY @@ -184,7 +184,7 @@ public static DateTime parseLenient(final String chars, final ParseConfig parseC final int days = parsePositiveInt(chars, offset + 8, offset + 10); if (10 == availableLength) { - return DateTime.ofDate(years, months, days); + return new DateTime(Field.DAY, years, month, days, 0, 0, 0, 0, null, 0, availableLength); } // HOURS @@ -197,11 +197,11 @@ public static DateTime parseLenient(final String chars, final ParseConfig parseC if (availableLength == 16) { // Have only minutes - return DateTime.of(years, months, days, hours, minutes, null); + return new DateTime(Field.MINUTE, years, month, days, hours, minutes, 0, 0, null, 0, 16); } // SECONDS or TIMEZONE - return handleTime(offset, parseConfig, chars, years, months, days, hours, minutes); + return handleTime(offset, parseConfig, chars, years, month, days, hours, minutes); } private static DateTime handleTimeResolution(final int offset, ParseConfig parseConfig, int year, int month, int day, int hour, int minute, String chars) @@ -229,10 +229,10 @@ else if (c == PLUS || c == MINUS) throw raiseUnexpectedCharacter(chars, offset + 19); } } - else if (length == offset + 19) + else if (length == 19) { final int seconds = parsePositiveInt(chars, offset + 17, offset + 19); - return DateTime.of(year, month, day, hour, minute, seconds, null); + return new DateTime(Field.SECOND, year, month, day, hour, minute, seconds, 0, null, 0, length); } throw raiseUnexpectedEndOfText(chars, offset + 16); diff --git a/src/test/java/com/ethlo/time/ITUTest.java b/src/test/java/com/ethlo/time/ITUTest.java index 8937e03..908ae41 100644 --- a/src/test/java/com/ethlo/time/ITUTest.java +++ b/src/test/java/com/ethlo/time/ITUTest.java @@ -336,134 +336,6 @@ void testParseCommaFractionSeparator() assertThat(pos.getIndex()).isEqualTo(23); } - @Test - void testParseUnparseable() - { - final ParsePosition pos = new ParsePosition(0); - assertThrows(DateTimeParseException.class, () -> ITU.parseLenient("1999-11-22|11:22:1", ParseConfig.DEFAULT, pos)); - assertThat(pos.getErrorIndex()).isEqualTo(10); - assertThat(pos.getIndex()).isEqualTo(10); - } - - @Test - void testParsePosition() - { - final ParsePosition pos = new ParsePosition(10); - ITU.parseLenient("123456789,1999-11-22T11:22:17Z,22222222222", ParseConfig.STRICT, pos); - assertThat(pos.getIndex()).isEqualTo(30); - } - - @Test - void testParsePositionDateTime() - { - final ParsePosition pos = new ParsePosition(0); - ITU.parseDateTime("1999-11-22T11:22:17.191Z", pos); - assertThat(pos.getIndex()).isEqualTo(24); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionDateTimeInvalid() - { - final ParsePosition pos = new ParsePosition(0); - assertThrows(DateTimeException.class, () -> ITU.parseDateTime("1999-11-22X11:22:17.191Z", pos)); - assertThat(pos.getIndex()).isEqualTo(10); - assertThat(pos.getErrorIndex()).isEqualTo(10); - } - - @Test - void testParsePositionNotZeroDateTimeValidWithMillis() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22:17.191Z,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(32); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionNotZeroDateTimeValidZeroOffsetWithMillis() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22:17.191+00:00,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(37); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithMillis() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22:17.191+05:00,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(37); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithSecond() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22:17+05:00,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(33); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithMinute() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22+05:00,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(30); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionRfc3339Zulu() - { - final ParsePosition pos = new ParsePosition(8); - ITU.parseDateTime("1234567,1999-11-22T11:22:00Z,some-other-data", pos); - assertThat(pos.getIndex()).isEqualTo(28); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParsePositionRfc3339Offset() - { - final ParsePosition pos = new ParsePosition(10); - final String input = "some-data,1999-11-22T11:22:00+05:30,some-other-data"; - ITU.parseDateTime(input, pos); - assertThat(pos.getIndex()).isEqualTo(35); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - assertThat(input.substring(pos.getIndex())).isEqualTo(",some-other-data"); - } - - @Test - void testParsePositionSubsequent() - { - final ParsePosition pos = new ParsePosition(4); - final String input = "abc,2004-11-21T00:00Z1999-11-22T11:22+05:00,some-other-data"; - - ITU.parseDateTime(input, pos); - assertThat(pos.getIndex()).isEqualTo(21); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - - ITU.parseDateTime(input, pos); - assertThat(pos.getIndex()).isEqualTo(43); - assertThat(pos.getErrorIndex()).isEqualTo(-1); - } - - @Test - void testParseOutOfBoundsPosition() - { - final ParsePosition pos = new ParsePosition(40); - assertThrows(IndexOutOfBoundsException.class, () -> ITU.parseDateTime("123", pos)); - } - - @Test - void testParseOutOfBoundsPositionNegative() - { - final ParsePosition pos = new ParsePosition(-3); - assertThrows(IndexOutOfBoundsException.class, () -> ITU.parseDateTime("123", pos)); - } @Test void testParseWithStrictConfig() diff --git a/src/test/java/com/ethlo/time/ParsePositionTest.java b/src/test/java/com/ethlo/time/ParsePositionTest.java new file mode 100644 index 0000000..bc57071 --- /dev/null +++ b/src/test/java/com/ethlo/time/ParsePositionTest.java @@ -0,0 +1,210 @@ +package com.ethlo.time; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.text.ParsePosition; +import java.time.DateTimeException; +import java.time.format.DateTimeParseException; + +import org.junit.jupiter.api.Test; + +public class ParsePositionTest +{ + @Test + void testParseUnparseable() + { + final ParsePosition pos = new ParsePosition(0); + assertThrows(DateTimeParseException.class, () -> ITU.parseLenient("1999-11-22|11:22:1", ParseConfig.DEFAULT, pos)); + assertThat(pos.getErrorIndex()).isEqualTo(10); + assertThat(pos.getIndex()).isEqualTo(10); + } + + @Test + void testParsePositionYear() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionMonth() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionDay() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionMinute() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22T11:22"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionMinuteUnparsable() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22T11:2x"; + final DateTimeParseException exc = assertThrows(DateTimeParseException.class, () -> ITU.parseLenient(input, ParseConfig.STRICT, pos)); + assertThat(pos.getIndex()).isEqualTo(input.length() - 1); + assertThat(pos.getErrorIndex()).isEqualTo(input.length() - 1); + assertThat(exc.getErrorIndex()).isEqualTo(input.length() - 1); + } + + @Test + void testParsePositionSecond() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22T11:22:11"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionSecondUnparsable() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22T11:22:3x"; + final DateTimeParseException exc = assertThrows(DateTimeParseException.class, () -> ITU.parseLenient(input, ParseConfig.STRICT, pos)); + assertThat(pos.getErrorIndex()).isEqualTo(input.length() - 1); + assertThat(exc.getErrorIndex()).isEqualTo(input.length() - 1); + } + + @Test + void testParsePositionMillis() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "123456789,1999-11-22T11:22:12.123"; + ITU.parseLenient(input, ParseConfig.STRICT, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + } + + @Test + void testParsePositionDateTime() + { + final ParsePosition pos = new ParsePosition(0); + final String input = "1999-11-22T11:22:17.191Z"; + ITU.parseDateTime(input, pos); + assertThat(pos.getIndex()).isEqualTo(input.length()); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionDateTimeInvalid() + { + final ParsePosition pos = new ParsePosition(0); + assertThrows(DateTimeException.class, () -> ITU.parseDateTime("1999-11-22X11:22:17.191Z", pos)); + assertThat(pos.getIndex()).isEqualTo(10); + assertThat(pos.getErrorIndex()).isEqualTo(10); + } + + @Test + void testParsePositionNotZeroDateTimeValidWithMillis() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22:17.191Z,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(32); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionNotZeroDateTimeValidZeroOffsetWithMillis() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22:17.191+00:00,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(37); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithMillis() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22:17.191+05:00,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(37); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithSecond() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22:17+05:00,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(33); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionNotZeroDateTimeValidNonZuluOffsetWithMinute() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22+05:00,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(30); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionRfc3339Zulu() + { + final ParsePosition pos = new ParsePosition(8); + ITU.parseDateTime("1234567,1999-11-22T11:22:00Z,some-other-data", pos); + assertThat(pos.getIndex()).isEqualTo(28); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParsePositionRfc3339Offset() + { + final ParsePosition pos = new ParsePosition(10); + final String input = "some-data,1999-11-22T11:22:00+05:30,some-other-data"; + ITU.parseDateTime(input, pos); + assertThat(pos.getIndex()).isEqualTo(35); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + assertThat(input.substring(pos.getIndex())).isEqualTo(",some-other-data"); + } + + @Test + void testParsePositionSubsequent() + { + final ParsePosition pos = new ParsePosition(4); + final String input = "abc,2004-11-21T00:00Z1999-11-22T11:22+05:00,some-other-data"; + + ITU.parseDateTime(input, pos); + assertThat(pos.getIndex()).isEqualTo(21); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + + ITU.parseDateTime(input, pos); + assertThat(pos.getIndex()).isEqualTo(43); + assertThat(pos.getErrorIndex()).isEqualTo(-1); + } + + @Test + void testParseOutOfBoundsPosition() + { + final ParsePosition pos = new ParsePosition(40); + assertThrows(IndexOutOfBoundsException.class, () -> ITU.parseDateTime("123", pos)); + } + + @Test + void testParseOutOfBoundsPositionNegative() + { + final ParsePosition pos = new ParsePosition(-3); + assertThrows(IndexOutOfBoundsException.class, () -> ITU.parseDateTime("123", pos)); + } +}