From c0fbdda4185dd33cce654c4df3c470c2cf6a1275 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Mon, 30 Sep 2019 13:54:52 +0200 Subject: [PATCH] Support optional parsers in any order with DateMathParser Backport(46654) (#47217) Currently DateMathParser with roundUp = true is relying on the DateFormatter build with combined optional sub parsers with defaulted fields (depending on the formatter). That means that for yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS Java.time implementation expects optional parsers in order from most specific to least specific (reverse in the example above). It is causing a problem because the first parsing succeeds but does not consume the full input. The second parser should be used. We can work around this with keeping a list of RoundUpParsers and iterate over them choosing the one that parsed full input. The same approach we used for regular (non date math) in relates #40100 The jdk is not considering this to be a bug https://bugs.openjdk.java.net/browse/JDK-8188771 Those below will expect this change first relates #46242 relates #45284 backport #46654 --- .../common/time/DateFormatter.java | 2 +- .../common/time/DateFormatters.java | 25 +---- .../common/time/JavaDateFormatter.java | 89 ++++++++++++++--- .../common/time/JavaDateMathParser.java | 16 ++- .../joda/JavaJodaTimeDuellingTests.java | 99 ++++++++++++++++++- .../common/joda/JodaDateMathParserTests.java | 14 +++ .../common/time/DateFormattersTests.java | 15 ++- .../common/time/JavaDateMathParserTests.java | 13 +++ 8 files changed, 212 insertions(+), 61 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java index 7e315b8cbe9ac..83881ab4a6af6 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java @@ -148,7 +148,7 @@ static DateFormatter forPattern(String input) { return formatters.get(0); } - return DateFormatters.merge(input, formatters); + return JavaDateFormatter.combined(input, formatters); } static List splitCombinedPatterns(String input) { 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 402ca20402de6..1ef1709025c88 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -40,8 +40,6 @@ import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalQueries; import java.time.temporal.WeekFields; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; import static java.time.temporal.ChronoField.DAY_OF_MONTH; @@ -1021,7 +1019,7 @@ public class DateFormatters { new DateTimeFormatterBuilder().appendValue(WeekFields.ISO.weekBasedYear()).toFormatter(Locale.ROOT)); /* - * Returns a formatter for a four digit weekyear. (uuuu) + * Returns a formatter for a four digit year. (uuuu) */ private static final DateFormatter YEAR = new JavaDateFormatter("year", new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR).toFormatter(Locale.ROOT)); @@ -1593,27 +1591,6 @@ public static DateFormatter forPattern(String input) { } } - static JavaDateFormatter merge(String pattern, List formatters) { - assert formatters.size() > 0; - - List dateTimeFormatters = new ArrayList<>(formatters.size()); - DateTimeFormatterBuilder roundupBuilder = new DateTimeFormatterBuilder(); - DateTimeFormatter printer = null; - for (DateFormatter formatter : formatters) { - assert formatter instanceof JavaDateFormatter; - JavaDateFormatter javaDateFormatter = (JavaDateFormatter) formatter; - if (printer == null) { - printer = javaDateFormatter.getPrinter(); - } - dateTimeFormatters.addAll(javaDateFormatter.getParsers()); - roundupBuilder.appendOptional(javaDateFormatter.getRoundupParser()); - } - DateTimeFormatter roundUpParser = roundupBuilder.toFormatter(Locale.ROOT); - - return new JavaDateFormatter(pattern, printer, builder -> builder.append(roundUpParser), - dateTimeFormatters.toArray(new DateTimeFormatter[0])); - } - private static final LocalDate LOCALDATE_EPOCH = LocalDate.of(1970, 1, 1); /** diff --git a/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java b/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java index 6ae251e091b7c..cc0f1dcd9b5b2 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java @@ -29,6 +29,7 @@ import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -38,6 +39,7 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; +import java.util.stream.Collectors; class JavaDateFormatter implements DateFormatter { @@ -56,13 +58,33 @@ class JavaDateFormatter implements DateFormatter { private final String format; private final DateTimeFormatter printer; private final List parsers; - private final DateTimeFormatter roundupParser; + private final JavaDateFormatter roundupParser; + static class RoundUpFormatter extends JavaDateFormatter{ + + RoundUpFormatter(String format, List roundUpParsers) { + super(format, firstFrom(roundUpParsers),null, roundUpParsers); + } + + private static DateTimeFormatter firstFrom(List roundUpParsers) { + return roundUpParsers.get(0); + } + + @Override + JavaDateFormatter getRoundupParser() { + throw new UnsupportedOperationException("RoundUpFormatter does not have another roundUpFormatter"); + } + } + + // named formatters use default roundUpParser JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) { this(format, printer, builder -> ROUND_UP_BASE_FIELDS.forEach(builder::parseDefaulting), parsers); } - JavaDateFormatter(String format, DateTimeFormatter printer, Consumer roundupParserConsumer, + // subclasses override roundUpParser + JavaDateFormatter(String format, + DateTimeFormatter printer, + Consumer roundupParserConsumer, DateTimeFormatter... parsers) { if (printer == null) { throw new IllegalArgumentException("printer may not be null"); @@ -83,20 +105,51 @@ class JavaDateFormatter implements DateFormatter { } else { this.parsers = Arrays.asList(parsers); } + //this is when the RoundUp Formatter is created. In further merges (with ||) it will only append this one to a list. + List roundUp = createRoundUpParser(format, roundupParserConsumer); + this.roundupParser = new RoundUpFormatter(format, roundUp) ; + } - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); + private List createRoundUpParser(String format, + Consumer roundupParserConsumer) { if (format.contains("||") == false) { + DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); builder.append(this.parsers.get(0)); + roundupParserConsumer.accept(builder); + return Arrays.asList(builder.toFormatter(locale())); } - roundupParserConsumer.accept(builder); - DateTimeFormatter roundupFormatter = builder.toFormatter(locale()); - if (printer.getZone() != null) { - roundupFormatter = roundupFormatter.withZone(zone()); + return null; + } + + public static DateFormatter combined(String input, List formatters) { + assert formatters.size() > 0; + + List parsers = new ArrayList<>(formatters.size()); + List roundUpParsers = new ArrayList<>(formatters.size()); + + DateTimeFormatter printer = null; + for (DateFormatter formatter : formatters) { + assert formatter instanceof JavaDateFormatter; + JavaDateFormatter javaDateFormatter = (JavaDateFormatter) formatter; + if (printer == null) { + printer = javaDateFormatter.getPrinter(); + } + parsers.addAll(javaDateFormatter.getParsers()); + roundUpParsers.addAll(javaDateFormatter.getRoundupParser().getParsers()); } - this.roundupParser = roundupFormatter; + + return new JavaDateFormatter(input, printer, roundUpParsers, parsers); } - DateTimeFormatter getRoundupParser() { + private JavaDateFormatter(String format, DateTimeFormatter printer, List roundUpParsers, + List parsers) { + this.format = format; + this.printer = printer; + this.roundupParser = roundUpParsers != null ? new RoundUpFormatter(format, roundUpParsers ) : null; + this.parsers = parsers; + } + + JavaDateFormatter getRoundupParser() { return roundupParser; } @@ -155,9 +208,12 @@ public DateFormatter withZone(ZoneId zoneId) { if (zoneId.equals(zone())) { return this; } - - return new JavaDateFormatter(format, printer.withZone(zoneId), - parsers.stream().map(p -> p.withZone(zoneId)).toArray(size -> new DateTimeFormatter[size])); + List parsers = this.parsers.stream().map(p -> p.withZone(zoneId)).collect(Collectors.toList()); + List roundUpParsers = this.roundupParser.getParsers() + .stream() + .map(p -> p.withZone(zoneId)) + .collect(Collectors.toList()); + return new JavaDateFormatter(format, printer.withZone(zoneId), roundUpParsers, parsers); } @Override @@ -166,9 +222,12 @@ public DateFormatter withLocale(Locale locale) { if (locale.equals(locale())) { return this; } - - return new JavaDateFormatter(format, printer.withLocale(locale), - parsers.stream().map(p -> p.withLocale(locale)).toArray(size -> new DateTimeFormatter[size])); + List parsers = this.parsers.stream().map(p -> p.withLocale(locale)).collect(Collectors.toList()); + List roundUpParsers = this.roundupParser.getParsers() + .stream() + .map(p -> p.withLocale(locale)) + .collect(Collectors.toList()); + return new JavaDateFormatter(format, printer.withLocale(locale), roundUpParsers, parsers); } @Override diff --git a/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java b/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java index f833319326d5e..4c60bb036a9ab 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java @@ -29,13 +29,11 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalQueries; import java.util.Objects; -import java.util.function.Function; import java.util.function.LongSupplier; /** @@ -48,14 +46,14 @@ public class JavaDateMathParser implements DateMathParser { private final JavaDateFormatter formatter; - private final DateTimeFormatter roundUpFormatter; private final String format; + private final JavaDateFormatter roundupParser; - JavaDateMathParser(String format, JavaDateFormatter formatter, DateTimeFormatter roundUpFormatter) { - Objects.requireNonNull(formatter); + JavaDateMathParser(String format, JavaDateFormatter formatter, JavaDateFormatter roundupParser) { this.format = format; + this.roundupParser = roundupParser; + Objects.requireNonNull(formatter); this.formatter = formatter; - this.roundUpFormatter = roundUpFormatter; } @Override @@ -215,12 +213,12 @@ private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTim throw new IllegalArgumentException("cannot parse empty date"); } - Function formatter = roundUpIfNoTime ? this.roundUpFormatter::parse : this.formatter::parse; + DateFormatter formatter = roundUpIfNoTime ? this.roundupParser : this.formatter; try { if (timeZone == null) { - return DateFormatters.from(formatter.apply(value)).toInstant().toEpochMilli(); + return DateFormatters.from(formatter.parse(value)).toInstant().toEpochMilli(); } else { - TemporalAccessor accessor = formatter.apply(value); + TemporalAccessor accessor = formatter.parse(value); ZoneId zoneId = TemporalQueries.zone().queryFrom(accessor); if (zoneId != null) { timeZone = zoneId; 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 05e83622ae759..5bff26de76c20 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -20,13 +20,17 @@ package org.elasticsearch.common.joda; import org.elasticsearch.client.JavaVersion; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.common.time.DateFormatters; +import org.elasticsearch.common.time.DateMathParser; import org.elasticsearch.test.ESTestCase; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.ISODateTimeFormat; +import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -38,6 +42,35 @@ import static org.hamcrest.Matchers.is; public class JavaJodaTimeDuellingTests extends ESTestCase { + public void testCompositeDateMathParsing(){ + //in all these examples the second pattern will be used + assertDateMathEquals("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertDateMathEquals("2014-06-06T12:01:02.123", "strictDateTimeNoMillis||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertDateMathEquals("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS"); + } + + public void testExceptionWhenCompositeParsingFailsDateMath(){ + //both parsing failures should contain pattern and input text in exception + //both patterns fail parsing the input text due to only 2 digits of millis. Hence full text was not parsed. + String pattern = "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"; + String text = "2014-06-06T12:01:02.123"; + ElasticsearchParseException e1 = expectThrows(ElasticsearchParseException.class, + () -> dateMathToMillis(text, DateFormatter.forPattern(pattern))); + assertThat(e1.getMessage(), containsString(pattern)); + assertThat(e1.getMessage(), containsString(text)); + + ElasticsearchParseException e2 = expectThrows(ElasticsearchParseException.class, + () -> dateMathToMillis(text, Joda.forPattern(pattern))); + assertThat(e2.getMessage(), containsString(pattern)); + assertThat(e2.getMessage(), containsString(text)); + } + + private long dateMathToMillis(String text, DateFormatter dateFormatter) { + DateFormatter javaFormatter = dateFormatter.withLocale(randomLocale(random())); + DateMathParser javaDateMath = javaFormatter.toDateMathParser(); + return javaDateMath.parse(text, () -> 0, true, (ZoneId) null); + } + public void testIncompatiblePatterns() { // in joda 'y' means year, this is changed to 'u' in java.time. difference is in before era yeaers @@ -112,6 +145,20 @@ public void testCustomTimeFormats() { assertSameDate("Nov 24 01:29:01 -0800", "MMM dd HH:mm:ss Z"); } + // this test requires tests to run with -Djava.locale.providers=COMPAT in order to work +// public void testCustomLocales() { +// +// // also ensure that locale based dates are the same +// assertSameDate("Di., 05 Dez. 2000 02:55:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); +// assertSameDate("Mi., 06 Dez. 2000 02:55:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); +// assertSameDate("Do., 07 Dez. 2000 00:00:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); +// assertSameDate("Fr., 08 Dez. 2000 00:00:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); +// +// DateTime dateTimeNow = DateTime.now(DateTimeZone.UTC); +// ZonedDateTime javaTimeNow = Instant.ofEpochMilli(dateTimeNow.getMillis()).atZone(ZoneOffset.UTC); +// assertSamePrinterOutput("E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de"), javaTimeNow, dateTimeNow); +// } + public void testDuellingFormatsValidParsing() { assertSameDate("1522332219", "epoch_second"); assertSameDate("0", "epoch_second"); @@ -463,6 +510,7 @@ public void testDuelingStrictParsing() { assertParseException("2018-12-1", "strict_date_optional_time"); assertParseException("2018-1-31", "strict_date_optional_time"); assertParseException("10000-01-31", "strict_date_optional_time"); + assertSameDate("2010-01-05T02:00", "strict_date_optional_time"); assertSameDate("2018-12-31T10:15:30", "strict_date_optional_time"); assertSameDate("2018-12-31T10:15:30Z", "strict_date_optional_time"); assertSameDate("2018-12-31T10:15:30+0100", "strict_date_optional_time"); @@ -470,6 +518,7 @@ public void testDuelingStrictParsing() { assertParseException("2018-12-31T10:15:3", "strict_date_optional_time"); assertParseException("2018-12-31T10:5:30", "strict_date_optional_time"); assertParseException("2018-12-31T9:15:30", "strict_date_optional_time"); + assertSameDate("2015-01-04T00:00Z", "strict_date_optional_time"); assertSameDate("2018-12-31T10:15:30.1Z", "strict_date_time"); assertSameDate("2018-12-31T10:15:30.123Z", "strict_date_time"); assertSameDate("2018-12-31T10:15:30.123456789Z", "strict_date_time"); @@ -709,13 +758,48 @@ public void testSamePrinterOutput() { assertSamePrinterOutput("strictYear", javaDate, jodaDate); assertSamePrinterOutput("strictYearMonth", javaDate, jodaDate); assertSamePrinterOutput("strictYearMonthDay", javaDate, jodaDate); + assertSamePrinterOutput("strict_date_optional_time", javaDate, jodaDate); + assertSamePrinterOutput("epoch_millis", javaDate, jodaDate); + } + + public void testSamePrinterOutputWithTimeZone() { + String format = "strict_date_optional_time"; + String dateInput = "2017-02-01T08:02:00.000-01:00"; + DateFormatter javaFormatter = DateFormatter.forPattern(format); + TemporalAccessor javaDate = javaFormatter.parse(dateInput); + + DateFormatter jodaFormatter = Joda.forPattern(format); + DateTime dateTime = jodaFormatter.parseJoda(dateInput); + + String javaDateString = javaFormatter.withZone(ZoneOffset.ofHours(-1)).format(javaDate); + String jodaDateString = jodaFormatter.withZone(ZoneOffset.ofHours(-1)).formatJoda(dateTime); + String message = String.format(Locale.ROOT, "expected string representation to be equal for format [%s]: joda [%s], java [%s]", + format, jodaDateString, javaDateString); + assertThat(message, javaDateString, is(jodaDateString)); + } + + public void testDateFormatterWithLocale() { + Locale locale = randomLocale(random()); + String pattern = randomBoolean() ? "strict_date_optional_time||date_time" : "date_time||strict_date_optional_time"; + DateFormatter formatter = DateFormatter.forPattern(pattern).withLocale(locale); + assertThat(formatter.pattern(), is(pattern)); + assertThat(formatter.locale(), is(locale)); } public void testSeveralTimeFormats() { - DateFormatter jodaFormatter = DateFormatter.forPattern("year_month_day||ordinal_date"); - DateFormatter javaFormatter = DateFormatter.forPattern("8year_month_day||ordinal_date"); - assertSameDate("2018-12-12", "year_month_day||ordinal_date", jodaFormatter, javaFormatter); - assertSameDate("2018-128", "year_month_day||ordinal_date", jodaFormatter, javaFormatter); + { + String format = "year_month_day||ordinal_date"; + DateFormatter jodaFormatter = Joda.forPattern(format); + DateFormatter javaFormatter = DateFormatter.forPattern(format); + assertSameDate("2018-12-12", format, jodaFormatter, javaFormatter); + assertSameDate("2018-128", format, jodaFormatter, javaFormatter); + } + { + String format = "strictDateOptionalTime||dd-MM-yyyy"; + DateFormatter jodaFormatter = Joda.forPattern(format); + DateFormatter javaFormatter = DateFormatter.forPattern(format); + assertSameDate("31-01-2014", format, jodaFormatter, javaFormatter); + } } public void testParsingMissingTimezone() { @@ -774,4 +858,11 @@ private void assertJavaTimeParseException(String input, String format) { assertThat(e.getMessage(), containsString(input)); assertThat(e.getMessage(), containsString(format)); } + + private void assertDateMathEquals(String text, String pattern) { + long gotMillisJava = dateMathToMillis(text, DateFormatter.forPattern(pattern)); + long gotMillisJoda = dateMathToMillis(text, Joda.forPattern(pattern)); + + assertEquals(gotMillisJoda, gotMillisJava); + } } diff --git a/server/src/test/java/org/elasticsearch/common/joda/JodaDateMathParserTests.java b/server/src/test/java/org/elasticsearch/common/joda/JodaDateMathParserTests.java index e502dfc6f963f..32d58444c9875 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JodaDateMathParserTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JodaDateMathParserTests.java @@ -27,6 +27,7 @@ import org.joda.time.DateTimeZone; import java.time.ZoneId; +import java.time.ZoneOffset; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.LongSupplier; @@ -59,6 +60,19 @@ void assertDateEquals(long gotMillis, String original, String expected) { } } + public void testOverridingLocaleOrZoneAndCompositeRoundUpParser() { + //the pattern has to be composite and the match should not be on the first one + DateFormatter formatter = Joda.forPattern("date||epoch_millis").withLocale(randomLocale(random())); + DateMathParser parser = formatter.toDateMathParser(); + long gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null); + assertDateEquals(gotMillis, "297276785531", "297276785531"); + + formatter = Joda.forPattern("date||epoch_millis").withZone(ZoneOffset.UTC); + parser = formatter.toDateMathParser(); + gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null); + assertDateEquals(gotMillis, "297276785531", "297276785531"); + } + public void testBasicDates() { assertDateMathEquals("2014", "2014-01-01T00:00:00.000"); assertDateMathEquals("2014-05", "2014-05-01T00:00:00.000"); diff --git a/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java b/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java index f07488d1d5f7c..b82c5cf18d2d8 100644 --- a/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java +++ b/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java @@ -24,7 +24,6 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Locale; @@ -202,7 +201,7 @@ public void testParsingStrictNanoDates() { public void testRoundupFormatterWithEpochDates() { assertRoundupFormatter("8epoch_millis", "1234567890", 1234567890L); // also check nanos of the epoch_millis formatter if it is rounded up to the nano second - DateTimeFormatter roundUpFormatter = ((JavaDateFormatter) DateFormatter.forPattern("8epoch_millis")).getRoundupParser(); + JavaDateFormatter roundUpFormatter = ((JavaDateFormatter) DateFormatter.forPattern("8epoch_millis")).getRoundupParser(); Instant epochMilliInstant = DateFormatters.from(roundUpFormatter.parse("1234567890")).toInstant(); assertThat(epochMilliInstant.getLong(ChronoField.NANO_OF_SECOND), is(890_999_999L)); @@ -213,7 +212,7 @@ public void testRoundupFormatterWithEpochDates() { assertRoundupFormatter("8epoch_second", "1234567890", 1234567890999L); // also check nanos of the epoch_millis formatter if it is rounded up to the nano second - DateTimeFormatter epochSecondRoundupParser = ((JavaDateFormatter) DateFormatter.forPattern("8epoch_second")).getRoundupParser(); + JavaDateFormatter epochSecondRoundupParser = ((JavaDateFormatter) DateFormatter.forPattern("8epoch_second")).getRoundupParser(); Instant epochSecondInstant = DateFormatters.from(epochSecondRoundupParser.parse("1234567890")).toInstant(); assertThat(epochSecondInstant.getLong(ChronoField.NANO_OF_SECOND), is(999_999_999L)); @@ -227,7 +226,7 @@ private void assertRoundupFormatter(String format, String input, long expectedMi assertThat(format, startsWith("8")); JavaDateFormatter dateFormatter = (JavaDateFormatter) DateFormatter.forPattern(format); dateFormatter.parse(input); - DateTimeFormatter roundUpFormatter = dateFormatter.getRoundupParser(); + JavaDateFormatter roundUpFormatter = dateFormatter.getRoundupParser(); long millis = DateFormatters.from(roundUpFormatter.parse(input)).toInstant().toEpochMilli(); assertThat(millis, is(expectedMilliSeconds)); } @@ -237,8 +236,8 @@ public void testRoundupFormatterZone() { String format = randomFrom("8epoch_second", "8epoch_millis", "8strict_date_optional_time", "8uuuu-MM-dd'T'HH:mm:ss.SSS", "8strict_date_optional_time||date_optional_time"); JavaDateFormatter formatter = (JavaDateFormatter) DateFormatter.forPattern(format).withZone(zoneId); - DateTimeFormatter roundUpFormatter = formatter.getRoundupParser(); - assertThat(roundUpFormatter.getZone(), is(zoneId)); + JavaDateFormatter roundUpFormatter = formatter.getRoundupParser(); + assertThat(roundUpFormatter.zone(), is(zoneId)); assertThat(formatter.zone(), is(zoneId)); } @@ -247,8 +246,8 @@ public void testRoundupFormatterLocale() { String format = randomFrom("8epoch_second", "8epoch_millis", "8strict_date_optional_time", "8uuuu-MM-dd'T'HH:mm:ss.SSS", "8strict_date_optional_time||date_optional_time"); JavaDateFormatter formatter = (JavaDateFormatter) DateFormatter.forPattern(format).withLocale(locale); - DateTimeFormatter roundupParser = formatter.getRoundupParser(); - assertThat(roundupParser.getLocale(), is(locale)); + JavaDateFormatter roundupParser = formatter.getRoundupParser(); + assertThat(roundupParser.locale(), is(locale)); assertThat(formatter.locale(), is(locale)); } } diff --git a/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java b/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java index c534f47ca39ff..5d7794b9b7ab0 100644 --- a/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java +++ b/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java @@ -38,6 +38,19 @@ public class JavaDateMathParserTests extends ESTestCase { private final DateFormatter formatter = DateFormatter.forPattern("8dateOptionalTime||epoch_millis"); private final DateMathParser parser = formatter.toDateMathParser(); + public void testOverridingLocaleOrZoneAndCompositeRoundUpParser() { + //the pattern has to be composite and the match should not be on the first one + DateFormatter formatter = DateFormatter.forPattern("date||epoch_millis").withLocale(randomLocale(random())); + DateMathParser parser = formatter.toDateMathParser(); + long gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null); + assertDateEquals(gotMillis, "297276785531", "297276785531"); + + formatter = DateFormatter.forPattern("date||epoch_millis").withZone(ZoneOffset.UTC); + parser = formatter.toDateMathParser(); + gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null); + assertDateEquals(gotMillis, "297276785531", "297276785531"); + } + public void testBasicDates() { assertDateMathEquals("2014-05-30", "2014-05-30T00:00:00.000"); assertDateMathEquals("2014-05-30T20", "2014-05-30T20:00:00.000");