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.
This commit is contained in:
2019-12-09 18:40:14 +01:00
parent 620f58d8e5
commit d383134c42
2 changed files with 44 additions and 8 deletions

View File

@@ -39,7 +39,8 @@ public class FastISODateParser {
final int nanos = nanosAndCharsRead[0]; final int nanos = nanosAndCharsRead[0];
final int offsetTimezone = 19 + nanosAndCharsRead[1]; 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())); : parseZone(date.subSequence(offsetTimezone, date.length()));
return OffsetDateTime.of(year, month, dayOfMonth, hour, minute, second, nanos, offset); return OffsetDateTime.of(year, month, dayOfMonth, hour, minute, second, nanos, offset);
} catch (final RuntimeException e) { } catch (final RuntimeException e) {
@@ -61,7 +62,7 @@ public class FastISODateParser {
final long nanos = nanosAndCharsRead[0]; final long nanos = nanosAndCharsRead[0];
final int offsetTimezone = 19 + nanosAndCharsRead[1]; 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())); : parseZoneToMillis(date.subSequence(offsetTimezone, date.length()));
final int epochMilliMonthOffsetKey = (int) (year * 12 + month - 1); final int epochMilliMonthOffsetKey = (int) (year * 12 + month - 1);
@@ -116,21 +117,28 @@ public class FastISODateParser {
private int[] parseMilliseconds(final String date, final int start) { private int[] parseMilliseconds(final String date, final int start) {
int result = 0; int result = 0;
int readChars = 0;
int i = start; int i = start;
char c = date.charAt(i);
if (c != '.' && c != ',') {
return new int[] { result, readChars };
}
i++;
readChars++;
while (i < date.length()) { while (i < date.length()) {
final char c = date.charAt(i); c = date.charAt(i);
i++; i++;
if (c == '.') {
continue;
}
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
i--;
break; break;
} }
result = result * 10 + (c - '0'); result = result * 10 + (c - '0');
readChars++;
} }
final int readChars = i - start - 1;
while (i <= start + 10) { while (i <= start + 9) {
result *= 10; result *= 10;
i++; i++;
} }

View File

@@ -68,6 +68,34 @@ public class FastISODateParserTest {
Assert.assertEquals(actualDate, expectedDate.toInstant().toEpochMilli()); 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") @DataProvider(name = "providerParseInvalidDate")
public Object[][] providerParseInvalidDate() { public Object[][] providerParseInvalidDate() {
return new Object[][] { // return new Object[][] { //