From e00afd20731c7b83fb96a1c1444c13b7199bcb02 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Fri, 1 Feb 2019 11:21:46 +0100 Subject: [PATCH] Speed up converting of temporal accessor to zoned date time The existing implementation was slow due to exceptions being thrown if an accessor did not have a time zone. This implementation queries for having a timezone, local time and local date and also checks for an instant preventing to throw an exception and thus speeding up the conversion. This removes the existing method and create a new one named DateFormatters.from(TemporalAccessor accessor) to resemble the naming of the java time ones. Before this change an epoch millis parser using the toZonedDateTime method took approximately 50x longer. Relates #37826 This commit also backports #38171 --- .../time/DateFormatterFromBenchmark.java | 53 ++++++ .../client/ml/job/util/TimeUtil.java | 2 +- .../ingest/common/DateFormat.java | 29 ++- .../ingest/common/DateFormatTests.java | 29 +++ .../common/time/DateFormatters.java | 169 +++++++++--------- .../common/time/JavaDateMathParser.java | 4 +- .../joda/JavaJodaTimeDuellingTests.java | 2 +- .../common/time/DateFormattersTests.java | 12 +- .../common/time/JavaDateMathParserTests.java | 4 +- .../bucket/histogram/DateHistogramTests.java | 6 +- 10 files changed, 210 insertions(+), 100 deletions(-) create mode 100644 benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterFromBenchmark.java diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterFromBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterFromBenchmark.java new file mode 100644 index 0000000000000..86753dba02b0b --- /dev/null +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/time/DateFormatterFromBenchmark.java @@ -0,0 +1,53 @@ +/* + * 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.time.DateFormatter; +import org.elasticsearch.common.time.DateFormatters; +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 DateFormatterFromBenchmark { + + private final TemporalAccessor accessor = DateFormatter.forPattern("epoch_millis").parse("1234567890"); + + @Benchmark + public TemporalAccessor benchmarkFrom() { + // benchmark an accessor that does not contain a timezone + // this used to throw an exception earlier and thus was very very slow + return DateFormatters.from(accessor); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/util/TimeUtil.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/util/TimeUtil.java index 4c21ffb2175b2..254979a360d0f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/util/TimeUtil.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/util/TimeUtil.java @@ -39,7 +39,7 @@ public static Date parseTimeField(XContentParser parser, String fieldName) throw if (parser.currentToken() == XContentParser.Token.VALUE_NUMBER) { return new Date(parser.longValue()); } else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) { - return new Date(DateFormatters.toZonedDateTime(DateTimeFormatter.ISO_INSTANT.parse(parser.text())).toInstant().toEpochMilli()); + return new Date(DateFormatters.from(DateTimeFormatter.ISO_INSTANT.parse(parser.text())).toInstant().toEpochMilli()); } throw new IllegalArgumentException( "unexpected token [" + parser.currentToken() + "] for [" + fieldName + "]"); diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateFormat.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateFormat.java index 220091c4baadc..8f3713b8f88ec 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateFormat.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateFormat.java @@ -31,10 +31,20 @@ import java.time.LocalDate; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.Arrays; +import java.util.List; import java.util.Locale; import java.util.function.Function; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; + enum DateFormat { Iso8601 { @Override @@ -71,6 +81,9 @@ private long parseMillis(String date) { } }, Java { + private final List FIELDS = + Arrays.asList(NANO_OF_SECOND, SECOND_OF_DAY, MINUTE_OF_DAY, HOUR_OF_DAY, DAY_OF_MONTH, MONTH_OF_YEAR); + @Override Function getFunction(String format, DateTimeZone timezone, Locale locale) { // in case you are wondering why we do not call 'DateFormatter.forPattern(format)' for all cases here, but only for the @@ -85,9 +98,21 @@ Function getFunction(String format, DateTimeZone timezone, Loc .withLocale(locale) .withZone(DateUtils.dateTimeZoneToZoneId(timezone)); return text -> { - ZonedDateTime defaultZonedDateTime = Instant.EPOCH.atZone(ZoneOffset.UTC).withYear(year); TemporalAccessor accessor = formatter.parse(text); - long millis = DateFormatters.toZonedDateTime(accessor, defaultZonedDateTime).toInstant().toEpochMilli(); + // if there is no year, we fall back to the current one and + // fill the rest of the date up with the parsed date + if (accessor.isSupported(ChronoField.YEAR) == false) { + ZonedDateTime newTime = Instant.EPOCH.atZone(ZoneOffset.UTC).withYear(year); + for (ChronoField field : FIELDS) { + if (accessor.isSupported(field)) { + newTime = newTime.with(field, accessor.get(field)); + } + } + + accessor = newTime.withZoneSameLocal(DateUtils.dateTimeZoneToZoneId(timezone)); + } + + long millis = DateFormatters.from(accessor).toInstant().toEpochMilli(); return new DateTime(millis, timezone); }; } else { diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateFormatTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateFormatTests.java index 27904a5586e7e..1d611e06430f9 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateFormatTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateFormatTests.java @@ -19,16 +19,20 @@ package org.elasticsearch.ingest.common; +import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.test.ESTestCase; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import java.time.Instant; import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.function.Function; +import static org.hamcrest.Matchers.is; import static org.hamcrest.core.IsEqual.equalTo; public class DateFormatTests extends ESTestCase { @@ -42,6 +46,31 @@ public void testParseJoda() { equalTo("11 24 01:29:01")); } + public void testParseJodaDefaultYear() { + String format = randomFrom("8dd/MM", "dd/MM"); + DateTimeZone timezone = DateUtils.zoneIdToDateTimeZone(ZoneId.of("Europe/Amsterdam")); + Function javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ENGLISH); + int year = ZonedDateTime.now(ZoneOffset.UTC).getYear(); + DateTime dateTime = javaFunction.apply("12/06"); + assertThat(dateTime.getYear(), is(year)); + assertThat(dateTime.toString(), is(year + "-06-12T00:00:00.000+02:00")); + } + + // if there is a time around end of year, which is different in UTC make sure the result is the same + public void testParseDefaultYearBackwardsCompatible() { + ZoneId zoneId = ZoneId.of("America/New_York"); + DateTimeZone timezone = DateUtils.zoneIdToDateTimeZone(zoneId); + int year = ZonedDateTime.now(zoneId).getYear(); + int nextYear = year + 1; + + DateTime javaDateTime = DateFormat.Java.getFunction("8dd/MM HH:mm", timezone, Locale.ENGLISH).apply("31/12 23:59"); + DateTime jodaDateTime = DateFormat.Java.getFunction("dd/MM HH:mm", timezone, Locale.ENGLISH).apply("31/12 23:59"); + assertThat(javaDateTime.getYear(), is(jodaDateTime.getYear())); + assertThat(year, is(jodaDateTime.getYear())); + assertThat(javaDateTime.withZone(DateTimeZone.UTC), is(jodaDateTime.withZone(DateTimeZone.UTC))); + assertThat(nextYear, is(jodaDateTime.withZone(DateTimeZone.UTC).getYear())); + } + public void testParseUnixMs() { assertThat(DateFormat.UnixMs.getFunction(null, DateTimeZone.UTC, null).apply("1000500").getMillis(), equalTo(1000500L)); } 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 16fbe7132e662..f29c3bf616fd7 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -20,11 +20,13 @@ package org.elasticsearch.common.time; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.SuppressForbidden; -import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Year; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -1503,105 +1505,106 @@ static JavaDateFormatter merge(String pattern, List formatters) { dateTimeFormatters.toArray(new DateTimeFormatter[0])); } - private static final ZonedDateTime EPOCH_ZONED_DATE_TIME = Instant.EPOCH.atZone(ZoneOffset.UTC); + private static final LocalDate LOCALDATE_EPOCH = LocalDate.of(1970, 1, 1); - public static ZonedDateTime toZonedDateTime(TemporalAccessor accessor) { - return toZonedDateTime(accessor, EPOCH_ZONED_DATE_TIME); - } - - public static ZonedDateTime toZonedDateTime(TemporalAccessor accessor, ZonedDateTime defaults) { - try { - return ZonedDateTime.from(accessor); - } catch (DateTimeException e ) { + /** + * Convert a temporal accessor to a zoned date time object - as performant as possible. + * The .from() methods from the JDK are throwing exceptions when for example ZonedDateTime.from(accessor) + * or Instant.from(accessor). This results in a huge performance penalty and should be prevented + * This method prevents exceptions by querying the accessor for certain capabilities + * and then act on it accordingly + * + * This action assumes that we can reliably fall back to some defaults if not all parts of a + * zoned date time are set + * + * - If a zoned date time is passed, it is returned + * - If no timezone is found, ZoneOffset.UTC is used + * - If we find a time and a date, converting to a ZonedDateTime is straight forward, + * no defaults will be applied + * - If an accessor only containing of seconds and nanos is found (like epoch_millis/second) + * an Instant is created out of that, that becomes a ZonedDateTime with a time zone + * - If no time is given, the start of the day is used + * - If no month of the year is found, the first day of the year is used + * - If an iso based weekyear is found, but not week is specified, the first monday + * of the new year is chosen (reataining BWC to joda time) + * - If an iso based weekyear is found and an iso based weekyear week, the start + * of the day is used + * + * @param accessor The accessor returned from a parser + * + * @return The converted zoned date time + */ + public static ZonedDateTime from(TemporalAccessor accessor) { + if (accessor instanceof ZonedDateTime) { + return (ZonedDateTime) accessor; } - ZonedDateTime result = defaults; - - // special case epoch seconds - if (accessor.isSupported(ChronoField.INSTANT_SECONDS)) { - result = result.with(ChronoField.INSTANT_SECONDS, accessor.getLong(ChronoField.INSTANT_SECONDS)); - if (accessor.isSupported(ChronoField.NANO_OF_SECOND)) { - result = result.with(ChronoField.NANO_OF_SECOND, accessor.getLong(ChronoField.NANO_OF_SECOND)); - } - return result; + ZoneId zoneId = accessor.query(TemporalQueries.zone()); + if (zoneId == null) { + zoneId = ZoneOffset.UTC; } - // try to set current year - if (accessor.isSupported(ChronoField.YEAR)) { - result = result.with(ChronoField.YEAR, accessor.getLong(ChronoField.YEAR)); - } else if (accessor.isSupported(ChronoField.YEAR_OF_ERA)) { - result = result.with(ChronoField.YEAR_OF_ERA, accessor.getLong(ChronoField.YEAR_OF_ERA)); + LocalDate localDate = accessor.query(TemporalQueries.localDate()); + LocalTime localTime = accessor.query(TemporalQueries.localTime()); + boolean isLocalDateSet = localDate != null; + boolean isLocalTimeSet = localTime != null; + + // the first two cases are the most common, so this allows us to exit early when parsing dates + if (isLocalDateSet && isLocalTimeSet) { + return of(localDate, localTime, zoneId); + } else if (accessor.isSupported(ChronoField.INSTANT_SECONDS) && accessor.isSupported(NANO_OF_SECOND)) { + return Instant.from(accessor).atZone(zoneId); + } else if (isLocalDateSet) { + return localDate.atStartOfDay(zoneId); + } else if (isLocalTimeSet) { + return of(getLocaldate(accessor), localTime, zoneId); + } else if (accessor.isSupported(ChronoField.YEAR)) { + if (accessor.isSupported(MONTH_OF_YEAR)) { + return getFirstOfMonth(accessor).atStartOfDay(zoneId); + } else { + return Year.of(accessor.get(ChronoField.YEAR)).atDay(1).atStartOfDay(zoneId); + } + } else if (accessor.isSupported(MONTH_OF_YEAR)) { + // missing year, falling back to the epoch and then filling + return getLocaldate(accessor).atStartOfDay(zoneId); } else if (accessor.isSupported(WeekFields.ISO.weekBasedYear())) { if (accessor.isSupported(WeekFields.ISO.weekOfWeekBasedYear())) { - return LocalDate.from(result) - .with(WeekFields.ISO.weekBasedYear(), accessor.getLong(WeekFields.ISO.weekBasedYear())) - .withDayOfMonth(1) // makes this compatible with joda + return Year.of(accessor.get(WeekFields.ISO.weekBasedYear())) + .atDay(1) .with(WeekFields.ISO.weekOfWeekBasedYear(), accessor.getLong(WeekFields.ISO.weekOfWeekBasedYear())) - .atStartOfDay(ZoneOffset.UTC); + .atStartOfDay(zoneId); } else { - return LocalDate.from(result) - .with(WeekFields.ISO.weekBasedYear(), accessor.getLong(WeekFields.ISO.weekBasedYear())) - // this exists solely to be BWC compatible with joda -// .with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)) + return Year.of(accessor.get(WeekFields.ISO.weekBasedYear())) + .atDay(1) .with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)) - .atStartOfDay(defaults.getZone()); -// return result.withHour(0).withMinute(0).withSecond(0) -// .with(WeekFields.ISO.weekBasedYear(), 0) -// .with(WeekFields.ISO.weekBasedYear(), accessor.getLong(WeekFields.ISO.weekBasedYear())); -// return ((ZonedDateTime) tmp).with(WeekFields.ISO.weekOfWeekBasedYear(), 1); + .atStartOfDay(zoneId); } - } else if (accessor.isSupported(IsoFields.WEEK_BASED_YEAR)) { - // special case weekbased year - result = result.with(IsoFields.WEEK_BASED_YEAR, accessor.getLong(IsoFields.WEEK_BASED_YEAR)); - if (accessor.isSupported(IsoFields.WEEK_OF_WEEK_BASED_YEAR)) { - result = result.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, accessor.getLong(IsoFields.WEEK_OF_WEEK_BASED_YEAR)); - } - return result; - } - - // month - if (accessor.isSupported(ChronoField.MONTH_OF_YEAR)) { - result = result.with(ChronoField.MONTH_OF_YEAR, accessor.getLong(ChronoField.MONTH_OF_YEAR)); } - // day of month - if (accessor.isSupported(ChronoField.DAY_OF_MONTH)) { - result = result.with(ChronoField.DAY_OF_MONTH, accessor.getLong(ChronoField.DAY_OF_MONTH)); - } - - // hour - if (accessor.isSupported(ChronoField.HOUR_OF_DAY)) { - result = result.with(ChronoField.HOUR_OF_DAY, accessor.getLong(ChronoField.HOUR_OF_DAY)); - } - - // minute - if (accessor.isSupported(ChronoField.MINUTE_OF_HOUR)) { - result = result.with(ChronoField.MINUTE_OF_HOUR, accessor.getLong(ChronoField.MINUTE_OF_HOUR)); - } - - // second - if (accessor.isSupported(ChronoField.SECOND_OF_MINUTE)) { - result = result.with(ChronoField.SECOND_OF_MINUTE, accessor.getLong(ChronoField.SECOND_OF_MINUTE)); - } - - if (accessor.isSupported(ChronoField.OFFSET_SECONDS)) { - result = result.withZoneSameLocal(ZoneOffset.ofTotalSeconds(accessor.get(ChronoField.OFFSET_SECONDS))); - } + // we should not reach this piece of code, everything being parsed we should be able to + // convert to a zoned date time! If not, we have to extend the above methods + throw new IllegalArgumentException("temporal accessor [" + accessor + "] cannot be converted to zoned date time"); + } - // millis - if (accessor.isSupported(ChronoField.MILLI_OF_SECOND)) { - result = result.with(ChronoField.MILLI_OF_SECOND, accessor.getLong(ChronoField.MILLI_OF_SECOND)); + private static LocalDate getLocaldate(TemporalAccessor accessor) { + if (accessor.isSupported(MONTH_OF_YEAR)) { + if (accessor.isSupported(DAY_OF_MONTH)) { + return LocalDate.of(1970, accessor.get(MONTH_OF_YEAR), accessor.get(DAY_OF_MONTH)); + } else { + return LocalDate.of(1970, accessor.get(MONTH_OF_YEAR), 1); + } } - if (accessor.isSupported(ChronoField.NANO_OF_SECOND)) { - result = result.with(ChronoField.NANO_OF_SECOND, accessor.getLong(ChronoField.NANO_OF_SECOND)); - } + return LOCALDATE_EPOCH; + } - ZoneId zoneOffset = accessor.query(TemporalQueries.zone()); - if (zoneOffset != null) { - result = result.withZoneSameLocal(zoneOffset); - } + @SuppressForbidden(reason = "ZonedDateTime.of is fine here") + private static ZonedDateTime of(LocalDate localDate, LocalTime localTime, ZoneId zoneId) { + return ZonedDateTime.of(localDate, localTime, zoneId); + } - return result; + @SuppressForbidden(reason = "LocalDate.of is fine here") + private static LocalDate getFirstOfMonth(TemporalAccessor accessor) { + return LocalDate.of(accessor.get(ChronoField.YEAR), accessor.get(MONTH_OF_YEAR), 1); } } 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 2070f7e2df8d7..4d740d3e85150 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java @@ -217,7 +217,7 @@ private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTim DateTimeFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter; try { if (timeZone == null) { - return DateFormatters.toZonedDateTime(formatter.parse(value)).toInstant().toEpochMilli(); + return DateFormatters.from(formatter.parse(value)).toInstant().toEpochMilli(); } else { TemporalAccessor accessor = formatter.parse(value); ZoneId zoneId = TemporalQueries.zone().queryFrom(accessor); @@ -225,7 +225,7 @@ private long parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTim timeZone = zoneId; } - return DateFormatters.toZonedDateTime(accessor).withZoneSameLocal(timeZone).toInstant().toEpochMilli(); + return DateFormatters.from(accessor).withZoneSameLocal(timeZone).toInstant().toEpochMilli(); } } catch (IllegalArgumentException | DateTimeException e) { throw new ElasticsearchParseException("failed to parse date field [{}] in format [{}]: [{}]", e, value, format, e.getMessage()); 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 05ea6758edd6e..c8667f9c3d350 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -641,7 +641,7 @@ private void assertSameDate(String input, String format, DateFormatter jodaForma DateTime jodaDateTime = jodaFormatter.parseJoda(input); TemporalAccessor javaTimeAccessor = javaFormatter.parse(input); - ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(javaTimeAccessor); + ZonedDateTime zonedDateTime = DateFormatters.from(javaTimeAccessor); String msg = String.format(Locale.ROOT, "Input [%s] Format [%s] Joda [%s], Java [%s]", input, format, jodaDateTime, DateTimeFormatter.ISO_INSTANT.format(zonedDateTime.toInstant())); 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 feb406c61c966..91b82393fd69a 100644 --- a/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java +++ b/server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java @@ -85,14 +85,14 @@ public void testEpochSecondParser() { public void testEpochMilliParsersWithDifferentFormatters() { DateFormatter formatter = DateFormatter.forPattern("strict_date_optional_time||epoch_millis"); TemporalAccessor accessor = formatter.parse("123"); - assertThat(DateFormatters.toZonedDateTime(accessor).toInstant().toEpochMilli(), is(123L)); + assertThat(DateFormatters.from(accessor).toInstant().toEpochMilli(), is(123L)); assertThat(formatter.pattern(), is("strict_date_optional_time||epoch_millis")); } public void testParsersWithMultipleInternalFormats() throws Exception { - ZonedDateTime first = DateFormatters.toZonedDateTime( + ZonedDateTime first = DateFormatters.from( DateFormatters.forPattern("strict_date_optional_time_nanos").parse("2018-05-15T17:14:56+0100")); - ZonedDateTime second = DateFormatters.toZonedDateTime( + ZonedDateTime second = DateFormatters.from( DateFormatters.forPattern("strict_date_optional_time_nanos").parse("2018-05-15T17:14:56+01:00")); assertThat(first, is(second)); } @@ -164,7 +164,7 @@ 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(); - Instant epochMilliInstant = DateFormatters.toZonedDateTime(roundUpFormatter.parse("1234567890")).toInstant(); + Instant epochMilliInstant = DateFormatters.from(roundUpFormatter.parse("1234567890")).toInstant(); assertThat(epochMilliInstant.getLong(ChronoField.NANO_OF_SECOND), is(890_999_999L)); assertRoundupFormatter("8strict_date_optional_time||epoch_millis", "2018-10-10T12:13:14.123Z", 1539173594123L); @@ -175,7 +175,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(); - Instant epochSecondInstant = DateFormatters.toZonedDateTime(epochSecondRoundupParser.parse("1234567890")).toInstant(); + Instant epochSecondInstant = DateFormatters.from(epochSecondRoundupParser.parse("1234567890")).toInstant(); assertThat(epochSecondInstant.getLong(ChronoField.NANO_OF_SECOND), is(999_999_999L)); assertRoundupFormatter("8strict_date_optional_time||epoch_second", "2018-10-10T12:13:14.123Z", 1539173594123L); @@ -188,7 +188,7 @@ private void assertRoundupFormatter(String format, String input, long expectedMi JavaDateFormatter dateFormatter = (JavaDateFormatter) DateFormatter.forPattern(format); dateFormatter.parse(input); DateTimeFormatter roundUpFormatter = dateFormatter.getRoundupParser(); - long millis = DateFormatters.toZonedDateTime(roundUpFormatter.parse(input)).toInstant().toEpochMilli(); + long millis = DateFormatters.from(roundUpFormatter.parse(input)).toInstant().toEpochMilli(); assertThat(millis, is(expectedMilliSeconds)); } 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 ad5da8c8293c1..b836fd1251bf5 100644 --- a/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java +++ b/server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java @@ -138,12 +138,12 @@ public void testRoundingPreservesEpochAsBaseDate() { // If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding DateFormatter formatter = DateFormatters.forPattern("HH:mm:ss"); DateMathParser parser = formatter.toDateMathParser(); - ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(formatter.parse("04:52:20")); + ZonedDateTime zonedDateTime = DateFormatters.from(formatter.parse("04:52:20")); assertThat(zonedDateTime.getYear(), is(1970)); long millisStart = zonedDateTime.toInstant().toEpochMilli(); assertEquals(millisStart, parser.parse("04:52:20", () -> 0, false, (ZoneId) null)); // due to rounding up, we have to add the number of milliseconds here manually - long millisEnd = DateFormatters.toZonedDateTime(formatter.parse("04:52:20")).toInstant().toEpochMilli() + 999; + long millisEnd = DateFormatters.from(formatter.parse("04:52:20")).toInstant().toEpochMilli() + 999; assertEquals(millisEnd, parser.parse("04:52:20", () -> 0, true, (ZoneId) null)); } diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramTests.java index 1461ddd9dd026..5db38479bb365 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramTests.java @@ -144,14 +144,14 @@ public void testRewriteTimeZone() throws IOException { try (Directory dir = newDirectory(); IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) { - long millis1 = DateFormatters.toZonedDateTime(format.parse("2018-03-11T11:55:00")).toInstant().toEpochMilli(); + long millis1 = DateFormatters.from(format.parse("2018-03-11T11:55:00")).toInstant().toEpochMilli(); w.addDocument(documentForDate(DATE_FIELD_NAME, millis1)); - long millis2 = DateFormatters.toZonedDateTime(format.parse("2017-10-30T18:13:00")).toInstant().toEpochMilli(); + long millis2 = DateFormatters.from(format.parse("2017-10-30T18:13:00")).toInstant().toEpochMilli(); w.addDocument(documentForDate(DATE_FIELD_NAME, millis2)); try (IndexReader readerThatDoesntCross = DirectoryReader.open(w)) { - long millis3 = DateFormatters.toZonedDateTime(format.parse("2018-03-25T02:44:00")).toInstant().toEpochMilli(); + long millis3 = DateFormatters.from(format.parse("2018-03-25T02:44:00")).toInstant().toEpochMilli(); w.addDocument(documentForDate(DATE_FIELD_NAME, millis3)); try (IndexReader readerThatCrosses = DirectoryReader.open(w)) {