Create ISO8601 joda compatible java time formatter (#38434)

The existing formatter being used was not on par with the joda formatter
as it was missing the ability to parse a comma as a separator between
seconds and milliseconds.

While a real iso8601 would be much more complex, this might be
sufficient for some more use-cases.

The ingest date formatter now also uses the iso8601 formatter by
default.

Closes #38345
This commit is contained in:
Alexander Reelsen 2019-02-11 17:10:33 +03:00
parent e7868e92bd
commit 884b5063a4
3 changed files with 59 additions and 2 deletions

View File

@ -45,7 +45,7 @@ enum DateFormat {
Iso8601 {
@Override
Function<String, ZonedDateTime> getFunction(String format, ZoneId timezone, Locale locale) {
return (date) -> DateFormatters.from(DateFormatter.forPattern("strict_date_time").parse(date)).withZoneSameInstant(timezone);
return (date) -> DateFormatters.from(DateFormatter.forPattern("iso8601").parse(date)).withZoneSameInstant(timezone);
}
},
Unix {

View File

@ -175,6 +175,43 @@ public class DateFormatters {
private static final DateFormatter STRICT_DATE_OPTIONAL_TIME_NANOS = new JavaDateFormatter("strict_date_optional_time_nanos",
STRICT_DATE_OPTIONAL_TIME_PRINTER_NANOS, STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS);
/**
* Returns a ISO 8601 compatible date time formatter and parser.
* This is not fully compatible to the existing spec, which would require far more edge cases, but merely compatible with the
* existing joda time ISO data formater
*/
private static final DateFormatter ISO_8601 = new JavaDateFormatter("iso8601", STRICT_DATE_OPTIONAL_TIME_PRINTER,
new DateTimeFormatterBuilder()
.append(STRICT_YEAR_MONTH_DAY_FORMATTER)
.optionalStart()
.appendLiteral('T')
.optionalStart()
.appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 1, 9, true)
.optionalEnd()
.optionalStart()
.appendLiteral(",")
.appendFraction(NANO_OF_SECOND, 1, 9, false)
.optionalEnd()
.optionalEnd()
.optionalStart()
.appendZoneOrOffsetId()
.optionalEnd()
.optionalStart()
.append(TIME_ZONE_FORMATTER_NO_COLON)
.optionalEnd()
.optionalEnd()
.optionalEnd()
.optionalEnd()
.toFormatter(Locale.ROOT));
/////////////////////////////////////////
//
// BEGIN basic time formatters
@ -1363,7 +1400,9 @@ public class DateFormatters {
throw new IllegalArgumentException("No date pattern provided");
}
if ("basicDate".equals(input) || "basic_date".equals(input)) {
if ("iso8601".equals(input)) {
return ISO_8601;
} else if ("basicDate".equals(input) || "basic_date".equals(input)) {
return BASIC_DATE;
} else if ("basicDateTime".equals(input) || "basic_date_time".equals(input)) {
return BASIC_DATE_TIME;

View File

@ -25,6 +25,7 @@ import org.elasticsearch.common.time.DateFormatters;
import org.elasticsearch.test.ESTestCase;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.ISODateTimeFormat;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
@ -665,6 +666,23 @@ public class JavaJodaTimeDuellingTests extends ESTestCase {
}
}
// the iso 8601 parser is available via Joda.forPattern(), so we have to test this slightly differently
public void testIso8601Parsers() {
String format = "iso8601";
org.joda.time.format.DateTimeFormatter isoFormatter = ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.UTC);
JodaDateFormatter jodaFormatter = new JodaDateFormatter(format, isoFormatter, isoFormatter);
DateFormatter javaFormatter = DateFormatter.forPattern(format);
assertSameDate("2018-10-10T", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11:12", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11:12.123", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11:12.123Z", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11:12,123", format, jodaFormatter, javaFormatter);
assertSameDate("2018-10-10T10:11:12,123Z", format, jodaFormatter, javaFormatter);
}
private void assertSamePrinterOutput(String format, ZonedDateTime javaDate, DateTime jodaDate) {
assertThat(jodaDate.getMillis(), is(javaDate.toInstant().toEpochMilli()));
String javaTimeOut = DateFormatter.forPattern(format).format(javaDate);