diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterBenchmark.java new file mode 100644 index 0000000000000..b30b3ada0ab64 --- /dev/null +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterBenchmark.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.benchmark.time; + +import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.time.DateFormatter; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.time.temporal.TemporalAccessor; +import java.util.concurrent.TimeUnit; + +@Fork(3) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@SuppressWarnings("unused") //invoked by benchmarking framework +public class DateFormatterBenchmark { + + private final DateFormatter javaFormatter = DateFormatter.forPattern("8year_month_day||ordinal_date||epoch_millis"); + private final DateFormatter jodaFormatter = Joda.forPattern("year_month_day||ordinal_date||epoch_millis"); + + @Benchmark + public TemporalAccessor parseJavaDate() { + return javaFormatter.parse("1234567890"); + } + + @Benchmark + public TemporalAccessor parseJodaDate() { + return jodaFormatter.parse("1234567890"); + } +} + 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 49c5e7626072b..e89317ad288c0 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java @@ -145,7 +145,7 @@ static DateFormatter forPattern(String input) { if (formatters.size() == 1) { return formatters.get(0); } - return new DateFormatters.MergedDateFormatter(input, formatters); + return DateFormatters.merge(input, formatters); } } 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 d5a8f4b7a5112..d3bf5eb2a641c 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -19,7 +19,6 @@ package org.elasticsearch.common.time; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Strings; import java.time.DateTimeException; @@ -31,7 +30,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; -import java.time.format.DateTimeParseException; import java.time.format.ResolverStyle; import java.time.format.SignStyle; import java.time.temporal.ChronoField; @@ -40,10 +38,9 @@ import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalQueries; import java.time.temporal.WeekFields; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.stream.Collectors; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; @@ -77,21 +74,17 @@ public class DateFormatters { .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) .toFormatter(Locale.ROOT); - private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_1 = new DateTimeFormatterBuilder() + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_PRINTER = new DateTimeFormatterBuilder() .append(STRICT_YEAR_MONTH_DAY_FORMATTER) - .optionalStart() .appendLiteral('T') .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) - .optionalStart() - .appendFraction(MILLI_OF_SECOND, 3, 3, true) - .optionalEnd() + .appendFraction(NANO_OF_SECOND, 3, 9, true) .optionalStart() .appendZoneOrOffsetId() .optionalEnd() - .optionalEnd() .toFormatter(Locale.ROOT); - private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_2 = new DateTimeFormatterBuilder() + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER = new DateTimeFormatterBuilder() .append(STRICT_YEAR_MONTH_DAY_FORMATTER) .optionalStart() .appendLiteral('T') @@ -100,7 +93,10 @@ public class DateFormatters { .appendFraction(MILLI_OF_SECOND, 3, 3, true) .optionalEnd() .optionalStart() - .appendOffset("+HHmm", "Z") + .appendZoneOrOffsetId() + .optionalEnd() + .optionalStart() + .append(TIME_ZONE_FORMATTER_NO_COLON) .optionalEnd() .optionalEnd() .toFormatter(Locale.ROOT); @@ -109,10 +105,9 @@ public class DateFormatters { * Returns a generic ISO datetime parser where the date is mandatory and the time is optional. */ private static final DateFormatter STRICT_DATE_OPTIONAL_TIME = - new JavaDateFormatter("strict_date_optional_time", STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, - STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_2); + new JavaDateFormatter("strict_date_optional_time", STRICT_DATE_OPTIONAL_TIME_PRINTER, STRICT_DATE_OPTIONAL_TIME_FORMATTER); - private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1 = new DateTimeFormatterBuilder() + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS = new DateTimeFormatterBuilder() .append(STRICT_YEAR_MONTH_DAY_FORMATTER) .optionalStart() .appendLiteral('T') @@ -124,26 +119,17 @@ public class DateFormatters { .appendZoneOrOffsetId() .optionalEnd() .optionalStart() - .appendOffset("+HHmm", "Z") + .append(TIME_ZONE_FORMATTER_NO_COLON) .optionalEnd() .optionalEnd() .toFormatter(Locale.ROOT); - private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_PRINTER = new DateTimeFormatterBuilder() - .append(STRICT_YEAR_MONTH_DAY_FORMATTER) - .appendLiteral('T') - .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) - .appendFraction(NANO_OF_SECOND, 3, 9, true) - .optionalStart() - .appendZoneOrOffsetId() - .optionalEnd() - .toFormatter(Locale.ROOT); - /** * Returns a generic ISO datetime parser where the date is mandatory and the time is optional with nanosecond resolution. */ private static final DateFormatter STRICT_DATE_OPTIONAL_TIME_NANOS = new JavaDateFormatter("strict_date_optional_time_nanos", - STRICT_DATE_OPTIONAL_TIME_PRINTER, STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS_1); + STRICT_DATE_OPTIONAL_TIME_PRINTER, STRICT_DATE_OPTIONAL_TIME_FORMATTER_WITH_NANOS); + ///////////////////////////////////////// // // BEGIN basic time formatters @@ -818,7 +804,7 @@ public class DateFormatters { * yyyy-MM-dd'T'HH:mm:ss.SSSZ */ private static final DateFormatter DATE_OPTIONAL_TIME = new JavaDateFormatter("date_optional_time", - STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, + STRICT_DATE_OPTIONAL_TIME_PRINTER, new DateTimeFormatterBuilder() .append(DATE_FORMATTER) .optionalStart() @@ -836,26 +822,6 @@ public class DateFormatters { .appendFraction(MILLI_OF_SECOND, 1, 3, true) .optionalEnd() .optionalStart().appendZoneOrOffsetId().optionalEnd() - .optionalEnd() - .optionalEnd() - .optionalEnd() - .toFormatter(Locale.ROOT), - new DateTimeFormatterBuilder() - .append(DATE_FORMATTER) - .optionalStart() - .appendLiteral('T') - .optionalStart() - .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) - .optionalStart() - .appendLiteral(':') - .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) - .optionalStart() - .appendLiteral(':') - .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) - .optionalEnd() - .optionalStart() - .appendFraction(MILLI_OF_SECOND, 1, 3, true) - .optionalEnd() .optionalStart().appendOffset("+HHmm", "Z").optionalEnd() .optionalEnd() .optionalEnd() @@ -1006,7 +972,7 @@ public class DateFormatters { * (yyyy-MM-dd'T'HH:mm:ss.SSSZZ). */ private static final DateFormatter DATE_TIME = new JavaDateFormatter("date_time", - STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, + STRICT_DATE_OPTIONAL_TIME_PRINTER, new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).appendZoneOrOffsetId().toFormatter(Locale.ROOT), new DateTimeFormatterBuilder().append(DATE_TIME_FORMATTER).append(TIME_ZONE_FORMATTER_NO_COLON).toFormatter(Locale.ROOT) ); @@ -1483,90 +1449,22 @@ public static DateFormatter forPattern(String input) { } } - static class MergedDateFormatter implements DateFormatter { - - private final String pattern; - // package private for tests - final List formatters; - private final List dateMathParsers; - - MergedDateFormatter(String pattern, List formatters) { - assert formatters.size() > 0; - this.pattern = pattern; - this.formatters = Collections.unmodifiableList(formatters); - this.dateMathParsers = formatters.stream().map(DateFormatter::toDateMathParser).collect(Collectors.toList()); - } - - @Override - public TemporalAccessor parse(String input) { - IllegalArgumentException failure = null; - for (DateFormatter formatter : formatters) { - try { - return formatter.parse(input); - // TODO: remove DateTimeParseException when JavaDateFormatter throws IAE - } catch (IllegalArgumentException | DateTimeParseException e) { - if (failure == null) { - // wrap so the entire multi format is in the message - failure = new IllegalArgumentException("failed to parse date field [" + input + "] with format [" + pattern + "]", - e); - } else { - failure.addSuppressed(e); - } - } + static JavaDateFormatter merge(String pattern, List formatters) { + assert formatters.size() > 0; + + List dateTimeFormatters = new ArrayList<>(formatters.size()); + DateTimeFormatter printer = null; + for (DateFormatter formatter : formatters) { + assert formatter instanceof JavaDateFormatter; + JavaDateFormatter javaDateFormatter = (JavaDateFormatter) formatter; + DateTimeFormatter dateTimeFormatter = javaDateFormatter.getParser(); + if (printer == null) { + printer = javaDateFormatter.getPrinter(); } - throw failure; - } - - @Override - public DateFormatter withZone(ZoneId zoneId) { - return new MergedDateFormatter(pattern, formatters.stream().map(f -> f.withZone(zoneId)).collect(Collectors.toList())); - } - - @Override - public DateFormatter withLocale(Locale locale) { - return new MergedDateFormatter(pattern, formatters.stream().map(f -> f.withLocale(locale)).collect(Collectors.toList())); - } - - @Override - public String format(TemporalAccessor accessor) { - return formatters.get(0).format(accessor); + dateTimeFormatters.add(dateTimeFormatter); } - @Override - public String pattern() { - return pattern; - } - - @Override - public Locale locale() { - return formatters.get(0).locale(); - } - - @Override - public ZoneId zone() { - return formatters.get(0).zone(); - } - - @Override - public DateMathParser toDateMathParser() { - return (text, now, roundUp, tz) -> { - ElasticsearchParseException failure = null; - for (DateMathParser parser : dateMathParsers) { - try { - return parser.parse(text, now, roundUp, tz); - } catch (ElasticsearchParseException e) { - if (failure == null) { - // wrap so the entire multi format is in the message - failure = new ElasticsearchParseException("failed to parse date field [" + text + "] with format [" - + pattern + "]", e); - } else { - failure.addSuppressed(e); - } - } - } - throw failure; - }; - } + return new JavaDateFormatter(pattern, printer, dateTimeFormatters.toArray(new DateTimeFormatter[0])); } private static final ZonedDateTime EPOCH_ZONED_DATE_TIME = Instant.EPOCH.atZone(ZoneOffset.UTC); 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 68e2cfd4fe317..0fce14b764ef1 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java @@ -19,10 +19,11 @@ package org.elasticsearch.common.time; +import org.elasticsearch.common.Strings; + import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; -import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; @@ -47,7 +48,7 @@ class JavaDateFormatter implements DateFormatter { private final String format; private final DateTimeFormatter printer; - private final DateTimeFormatter[] parsers; + private final DateTimeFormatter parser; JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) { if (printer == null) { @@ -62,61 +63,54 @@ class JavaDateFormatter implements DateFormatter { throw new IllegalArgumentException("formatters must have the same locale"); } if (parsers.length == 0) { - this.parsers = new DateTimeFormatter[]{printer}; + this.parser = printer; + } else if (parsers.length == 1) { + this.parser = parsers[0]; } else { - this.parsers = parsers; + DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); + for (DateTimeFormatter parser : parsers) { + builder.appendOptional(parser); + } + this.parser = builder.toFormatter(Locale.ROOT); } this.format = format; this.printer = printer; } + DateTimeFormatter getParser() { + return parser; + } + + DateTimeFormatter getPrinter() { + return printer; + } + @Override public TemporalAccessor parse(String input) { - DateTimeParseException failure = null; - for (int i = 0; i < parsers.length; i++) { - try { - return parsers[i].parse(input); - } catch (DateTimeParseException e) { - if (failure == null) { - failure = e; - } else { - failure.addSuppressed(e); - } - } + if (Strings.isNullOrEmpty(input)) { + throw new IllegalArgumentException("cannot parse empty date"); } - - // ensure that all parsers exceptions are returned instead of only the last one - throw failure; + return parser.parse(input); } @Override public DateFormatter withZone(ZoneId zoneId) { // shortcurt to not create new objects unnecessarily - if (zoneId.equals(parsers[0].getZone())) { + if (zoneId.equals(parser.getZone())) { return this; } - final DateTimeFormatter[] parsersWithZone = new DateTimeFormatter[parsers.length]; - for (int i = 0; i < parsers.length; i++) { - parsersWithZone[i] = parsers[i].withZone(zoneId); - } - - return new JavaDateFormatter(format, printer.withZone(zoneId), parsersWithZone); + return new JavaDateFormatter(format, printer.withZone(zoneId), parser.withZone(zoneId)); } @Override public DateFormatter withLocale(Locale locale) { // shortcurt to not create new objects unnecessarily - if (locale.equals(parsers[0].getLocale())) { + if (locale.equals(parser.getLocale())) { return this; } - final DateTimeFormatter[] parsersWithZone = new DateTimeFormatter[parsers.length]; - for (int i = 0; i < parsers.length; i++) { - parsersWithZone[i] = parsers[i].withLocale(locale); - } - - return new JavaDateFormatter(format, printer.withLocale(locale), parsersWithZone); + return new JavaDateFormatter(format, printer.withLocale(locale), parser.withLocale(locale)); } @Override @@ -132,17 +126,7 @@ public String pattern() { JavaDateFormatter parseDefaulting(Map fields) { final DateTimeFormatterBuilder parseDefaultingBuilder = new DateTimeFormatterBuilder().append(printer); fields.forEach(parseDefaultingBuilder::parseDefaulting); - if (parsers.length == 1 && parsers[0].equals(printer)) { - return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT)); - } else { - final DateTimeFormatter[] parsersWithDefaulting = new DateTimeFormatter[parsers.length]; - for (int i = 0; i < parsers.length; i++) { - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append(parsers[i]); - fields.forEach(builder::parseDefaulting); - parsersWithDefaulting[i] = builder.toFormatter(Locale.ROOT); - } - return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT), parsersWithDefaulting); - } + return new JavaDateFormatter(format, parseDefaultingBuilder.toFormatter(Locale.ROOT)); } @Override 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 a96fee5b6333e..b2370dadb604c 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -72,8 +72,6 @@ public void testCustomTimeFormats() { public void testDuellingFormatsValidParsing() { assertSameDate("1522332219", "epoch_second"); - assertSameDate("1522332219.", "epoch_second"); - assertSameDate("1522332219.0", "epoch_second"); assertSameDate("0", "epoch_second"); assertSameDate("1", "epoch_second"); assertSameDate("1522332219321", "epoch_millis"); 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 8d79f9d3600e7..a2858284593d1 100644 --- a/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java +++ b/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java @@ -23,6 +23,7 @@ import java.time.Instant; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.util.Locale; @@ -42,21 +43,11 @@ public class DateFormattersTests extends ESTestCase { // as this feature is supported it also makes sense to make it exact public void testEpochMillisParser() { DateFormatter formatter = DateFormatters.forPattern("epoch_millis"); - { - Instant instant = Instant.from(formatter.parse("12345.6789")); - assertThat(instant.getEpochSecond(), is(12L)); - assertThat(instant.getNano(), is(345_678_900)); - } { Instant instant = Instant.from(formatter.parse("12345")); assertThat(instant.getEpochSecond(), is(12L)); assertThat(instant.getNano(), is(345_000_000)); } - { - Instant instant = Instant.from(formatter.parse("12345.")); - assertThat(instant.getEpochSecond(), is(12L)); - assertThat(instant.getNano(), is(345_000_000)); - } { Instant instant = Instant.from(formatter.parse("0")); assertThat(instant.getEpochSecond(), is(0L)); @@ -79,25 +70,12 @@ public void testEpochMilliParser() { public void testEpochSecondParser() { DateFormatter formatter = DateFormatters.forPattern("epoch_second"); - assertThat(Instant.from(formatter.parse("1234.567")).toEpochMilli(), is(1234567L)); - assertThat(Instant.from(formatter.parse("1234.")).getNano(), is(0)); - assertThat(Instant.from(formatter.parse("1234.")).getEpochSecond(), is(1234L)); - assertThat(Instant.from(formatter.parse("1234.1")).getNano(), is(100_000_000)); - assertThat(Instant.from(formatter.parse("1234.12")).getNano(), is(120_000_000)); - assertThat(Instant.from(formatter.parse("1234.123")).getNano(), is(123_000_000)); - assertThat(Instant.from(formatter.parse("1234.1234")).getNano(), is(123_400_000)); - assertThat(Instant.from(formatter.parse("1234.12345")).getNano(), is(123_450_000)); - assertThat(Instant.from(formatter.parse("1234.123456")).getNano(), is(123_456_000)); - assertThat(Instant.from(formatter.parse("1234.1234567")).getNano(), is(123_456_700)); - assertThat(Instant.from(formatter.parse("1234.12345678")).getNano(), is(123_456_780)); - assertThat(Instant.from(formatter.parse("1234.123456789")).getNano(), is(123_456_789)); - - DateTimeParseException e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.1234567890")); - assertThat(e.getMessage(), is("Text '1234.1234567890' could not be parsed, unparsed text found at index 4")); - e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.123456789013221")); - assertThat(e.getMessage(), is("Text '1234.123456789013221' could not be parsed, unparsed text found at index 4")); + DateTimeParseException e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.1")); + assertThat(e.getMessage(), is("Text '1234.1' could not be parsed, unparsed text found at index 4")); + e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.")); + assertThat(e.getMessage(), is("Text '1234.' could not be parsed, unparsed text found at index 4")); e = expectThrows(DateTimeParseException.class, () -> formatter.parse("abc")); - assertThat(e.getMessage(), is("Text 'abc' could not be parsed at index 0")); + assertThat(e.getMessage(), is("Text 'abc' could not be parsed, unparsed text found at index 0")); e = expectThrows(DateTimeParseException.class, () -> formatter.parse("1234.abc")); assertThat(e.getMessage(), is("Text '1234.abc' could not be parsed, unparsed text found at index 4")); } @@ -109,6 +87,14 @@ public void testEpochMilliParsersWithDifferentFormatters() { assertThat(formatter.pattern(), is("strict_date_optional_time||epoch_millis")); } + public void testParsersWithMultipleInternalFormats() throws Exception { + ZonedDateTime first = DateFormatters.toZonedDateTime( + DateFormatters.forPattern("strict_date_optional_time_nanos").parse("2018-05-15T17:14:56+0100")); + ZonedDateTime second = DateFormatters.toZonedDateTime( + DateFormatters.forPattern("strict_date_optional_time_nanos").parse("2018-05-15T17:14:56+01:00")); + assertThat(first, is(second)); + } + public void testLocales() { assertThat(DateFormatters.forPattern("strict_date_optional_time").locale(), is(Locale.ROOT)); Locale locale = randomLocale(random()); @@ -157,10 +143,7 @@ public void testForceJava8() { assertThat(DateFormatter.forPattern("8date_optional_time"), instanceOf(JavaDateFormatter.class)); // named formats too DateFormatter formatter = DateFormatter.forPattern("8date_optional_time||ww-MM-dd"); - assertThat(formatter, instanceOf(DateFormatters.MergedDateFormatter.class)); - DateFormatters.MergedDateFormatter mergedFormatter = (DateFormatters.MergedDateFormatter) formatter; - assertThat(mergedFormatter.formatters.get(0), instanceOf(JavaDateFormatter.class)); - assertThat(mergedFormatter.formatters.get(1), instanceOf(JavaDateFormatter.class)); + assertThat(formatter, instanceOf(JavaDateFormatter.class)); } public void testParsingStrictNanoDates() {