diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java index 26ec15559b803..b936329f0f650 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -108,31 +108,31 @@ public class DateFormatters { private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER = 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() + .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() + .optionalEnd() + .optionalStart() + .appendZoneOrOffsetId() + .optionalEnd() + .optionalStart() + .append(TIME_ZONE_FORMATTER_NO_COLON) + .optionalEnd() + .optionalEnd() .optionalEnd() .toFormatter(Locale.ROOT) .withResolverStyle(ResolverStyle.STRICT); diff --git a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java index 49e069ceb6f5a..7e3ed3abcb30f 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -46,9 +46,42 @@ public class JavaJodaTimeDuellingTests extends ESTestCase { protected boolean enableWarningsCheck() { return false; } + + public void testTimezoneParsing() { + /** this testcase won't work in joda. See comment in {@link #testPartialTimeParsing()} + * assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); + */ + assertSameDateAs("2016-11-30T00+01", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T00+0100", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T00+01:00", "strict_date_optional_time", "strict_date_optional_time"); + } + + public void testPartialTimeParsing() { + /* + This does not work in Joda as it reports 2016-11-30T01:00:00Z + because StrictDateOptionalTime confuses +01 with an hour (which is a signed fixed length digit) + assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); + ES java.time implementation does not suffer from this, + but we intentionally not allow parsing timezone without an time part as it is not allowed in iso8601 + */ + assertJavaTimeParseException("2016-11-30T+01","strict_date_optional_time"); + + assertSameDateAs("2016-11-30T12+01", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00+01", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00+01", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00.000+01", "strict_date_optional_time", "strict_date_optional_time"); + + //without timezone + assertSameDateAs("2016-11-30T", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00", "strict_date_optional_time", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00.000", "strict_date_optional_time", "strict_date_optional_time"); + } + // date_optional part of a parser names "strict_date_optional_time" or "date_optional"time // means that date part can be partially parsed. - public void testPartialParsing() { + public void testPartialDateParsing() { assertSameDateAs("2001", "strict_date_optional_time_nanos", "strict_date_optional_time"); assertSameDateAs("2001-01", "strict_date_optional_time_nanos", "strict_date_optional_time"); assertSameDateAs("2001-01-01", "strict_date_optional_time_nanos", "strict_date_optional_time"); @@ -883,9 +916,28 @@ public void testParsingMissingTimezone() { } private void assertSamePrinterOutput(String format, ZonedDateTime javaDate, DateTime jodaDate) { + DateFormatter dateFormatter = DateFormatter.forPattern(format); + JodaDateFormatter jodaDateFormatter = Joda.forPattern(format); + + assertSamePrinterOutput(format, javaDate, jodaDate, dateFormatter, jodaDateFormatter); + } + + private void assertSamePrinterOutput(String format, ZonedDateTime javaDate, DateTime jodaDate, Locale locale) { + DateFormatter dateFormatter = DateFormatter.forPattern(format).withLocale(locale); + DateFormatter jodaDateFormatter = Joda.forPattern(format).withLocale(locale); + + assertSamePrinterOutput(format, javaDate, jodaDate, dateFormatter, jodaDateFormatter); + } + + private void assertSamePrinterOutput(String format, + ZonedDateTime javaDate, + DateTime jodaDate, + DateFormatter dateFormatter, + DateFormatter jodaDateFormatter) { + String javaTimeOut = dateFormatter.format(javaDate); + String jodaTimeOut = jodaDateFormatter.formatJoda(jodaDate); + assertThat(jodaDate.getMillis(), is(javaDate.toInstant().toEpochMilli())); - String javaTimeOut = DateFormatter.forPattern(format).format(javaDate); - String jodaTimeOut = Joda.forPattern(format).formatJoda(jodaDate); if (JavaVersion.current().getVersion().get(0) == 8 && javaTimeOut.endsWith(".0") && (format.equals("epoch_second") || format.equals("epoch_millis"))) { @@ -904,6 +956,12 @@ private void assertSameDate(String input, String format) { assertSameDate(input, format, jodaFormatter, javaFormatter); } + private void assertSameDate(String input, String format, Locale locale) { + DateFormatter jodaFormatter = Joda.forPattern(format).withLocale(locale); + DateFormatter javaFormatter = DateFormatter.forPattern(format).withLocale(locale); + assertSameDate(input, format, jodaFormatter, javaFormatter); + } + private void assertSameDate(String input, String format, DateFormatter jodaFormatter, DateFormatter javaFormatter) { DateTime jodaDateTime = jodaFormatter.parseJoda(input);