From d383134c42700fbf16e5961081cf5622271ea3e5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 9 Dec 2019 18:40:14 +0100 Subject: [PATCH] add support for ISO-like date formats Recommind is using a pseudo ISO date format for their log files. It uses a comma instead of a dot for the second to milli second separator and it does not add a timezone. Dates without timezone are assumed to be UTC. --- .../lucares/pdbui/date/FastISODateParser.java | 24 ++++++++++------ .../pdbui/date/FastISODateParserTest.java | 28 +++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/pdb-ui/src/main/java/org/lucares/pdbui/date/FastISODateParser.java b/pdb-ui/src/main/java/org/lucares/pdbui/date/FastISODateParser.java index 7418e25..2a79565 100644 --- a/pdb-ui/src/main/java/org/lucares/pdbui/date/FastISODateParser.java +++ b/pdb-ui/src/main/java/org/lucares/pdbui/date/FastISODateParser.java @@ -39,7 +39,8 @@ public class FastISODateParser { final int nanos = nanosAndCharsRead[0]; final int offsetTimezone = 19 + nanosAndCharsRead[1]; - final ZoneOffset offset = date.charAt(offsetTimezone) == 'Z' ? ZoneOffset.UTC + final ZoneOffset offset = offsetTimezone >= date.length() || date.charAt(offsetTimezone) == 'Z' + ? ZoneOffset.UTC : parseZone(date.subSequence(offsetTimezone, date.length())); return OffsetDateTime.of(year, month, dayOfMonth, hour, minute, second, nanos, offset); } catch (final RuntimeException e) { @@ -61,7 +62,7 @@ public class FastISODateParser { final long nanos = nanosAndCharsRead[0]; final int offsetTimezone = 19 + nanosAndCharsRead[1]; - final long zoneOffsetMillis = date.charAt(offsetTimezone) == 'Z' ? 0 + final long zoneOffsetMillis = offsetTimezone >= date.length() || date.charAt(offsetTimezone) == 'Z' ? 0 : parseZoneToMillis(date.subSequence(offsetTimezone, date.length())); final int epochMilliMonthOffsetKey = (int) (year * 12 + month - 1); @@ -116,21 +117,28 @@ public class FastISODateParser { private int[] parseMilliseconds(final String date, final int start) { int result = 0; + int readChars = 0; int i = start; + + char c = date.charAt(i); + if (c != '.' && c != ',') { + return new int[] { result, readChars }; + } + i++; + readChars++; + while (i < date.length()) { - final char c = date.charAt(i); + c = date.charAt(i); i++; - if (c == '.') { - continue; - } if (c < '0' || c > '9') { + i--; break; } result = result * 10 + (c - '0'); + readChars++; } - final int readChars = i - start - 1; - while (i <= start + 10) { + while (i <= start + 9) { result *= 10; i++; } diff --git a/pdb-ui/src/test/java/org/lucares/pdbui/date/FastISODateParserTest.java b/pdb-ui/src/test/java/org/lucares/pdbui/date/FastISODateParserTest.java index 520e5fb..b2d43de 100644 --- a/pdb-ui/src/test/java/org/lucares/pdbui/date/FastISODateParserTest.java +++ b/pdb-ui/src/test/java/org/lucares/pdbui/date/FastISODateParserTest.java @@ -68,6 +68,34 @@ public class FastISODateParserTest { Assert.assertEquals(actualDate, expectedDate.toInstant().toEpochMilli()); } + @DataProvider(name = "providerPseudoISODate") + public Object[][] providerPseudoISODate() { + return new Object[][] { // + { "2018-11-18T14:42:49,123Z", "2018-11-18T14:42:49.123Z" }, // with comman instead of dot + { "2018-11-18T14:42:49,123+12:34", "2018-11-18T14:42:49.123+12:34" }, // with comman instead of dot + { "2018-11-18T14:42:49.123", "2018-11-18T14:42:49.123Z" }, // without timezone + { "2018-11-18T14:42:49,123", "2018-11-18T14:42:49.123Z" }, // with command, without timezone + }; + } + + @Test(dataProvider = "providerPseudoISODate") + public void testParsePseudoISODate(final String date, final String expectedDate) { + + final OffsetDateTime actualDate = new FastISODateParser().parse(date); + + final OffsetDateTime expected = OffsetDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(expectedDate)); + Assert.assertEquals(actualDate, expected); + } + + @Test(dataProvider = "providerPseudoISODate") + public void testParsePseudoISODateAsEpochMilli(final String date, final String expectedDate) { + + final long actualDate = new FastISODateParser().parseAsEpochMilli(date); + + final OffsetDateTime expected = OffsetDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(expectedDate)); + Assert.assertEquals(actualDate, expected.toInstant().toEpochMilli()); + } + @DataProvider(name = "providerParseInvalidDate") public Object[][] providerParseInvalidDate() { return new Object[][] { //