From a87ae91717a9b133a33916f2d582da2fa849cc37 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Tue, 17 Jul 2018 00:05:08 +0200 Subject: [PATCH] Core: Backport java time date formatters (#31997) * Date: Add DateFormatters class that uses java.time (#31856) A newly added class called DateFormatters now contains java.time based builders for dates, which also intends to be fully backwards compatible, when the name based date formatters are picked. Also a new class named CompoundDateTimeFormatter for being able to parse multiple different formats has been added. A duelling test class has been added that ensures the same dates when parsing java or joda time formatted dates for the name based dates. Note, that java.time and joda time are not fully backwards compatible, which also means that old formats will currently not work with this setup. * Tests: Remove use of joda time in some tests (#31922) This also extends the dateformatters test to ensure that the printers are acting the same in java time and joda time. * Tests: Fix SearchFieldsIT.testDocValueFields This test produced different implementations of joda time classes, depending on if the data was serialized or not (DateTime vs MutableDateTime). This now uses a common base class to extract the milliseconds from the data. Closes #31992 --- .../ingest/common/DateProcessorTests.java | 35 +- .../cluster/metadata/IndexMetaData.java | 8 +- .../metadata/MetaDataCreateIndexService.java | 5 +- .../time/CompoundDateTimeFormatter.java | 73 ++ .../common/time/DateFormatters.java | 1072 +++++++++++++++++ .../elasticsearch/monitor/jvm/HotThreads.java | 10 +- .../rest/action/cat/RestIndicesAction.java | 7 +- .../elasticsearch/snapshots/SnapshotInfo.java | 12 +- .../admin/indices/rollover/RolloverIT.java | 20 +- .../HumanReadableIndexSettingsTests.java | 9 +- .../joda/JavaJodaTimeDuellingTests.java | 492 ++++++++ .../explain/ExplainActionIT.java | 11 +- .../indices/IndicesRequestCacheIT.java | 12 +- .../aggregations/bucket/MinDocCountIT.java | 12 +- .../highlight/HighlighterSearchIT.java | 6 +- .../search/fields/SearchFieldsIT.java | 25 +- .../functionscore/DecayFunctionScoreIT.java | 14 +- .../search/query/SearchQueryIT.java | 8 +- .../validate/SimpleValidateQueryIT.java | 20 +- .../org/elasticsearch/test/ESTestCase.java | 15 +- 20 files changed, 1767 insertions(+), 99 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/common/time/CompoundDateTimeFormatter.java create mode 100644 server/src/main/java/org/elasticsearch/common/time/DateFormatters.java create mode 100644 server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorTests.java index 8fba759aa16f9..43a5f9245b185 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateProcessorTests.java @@ -24,9 +24,10 @@ import org.elasticsearch.ingest.TestTemplateService; import org.elasticsearch.script.TemplateScript; import org.elasticsearch.test.ESTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -36,19 +37,21 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; -import static org.joda.time.DateTimeZone.UTC; public class DateProcessorTests extends ESTestCase { + private TemplateScript.Factory templatize(Locale locale) { return new TestTemplateService.MockTemplateScript.Factory(locale.getLanguage()); } - private TemplateScript.Factory templatize(DateTimeZone timezone) { - return new TestTemplateService.MockTemplateScript.Factory(timezone.getID()); + private TemplateScript.Factory templatize(ZoneId timezone) { + // prevent writing "UTC" as string, as joda time does not parse it + String id = timezone.equals(ZoneOffset.UTC) ? "UTC" : timezone.getId(); + return new TestTemplateService.MockTemplateScript.Factory(id); } public void testJodaPattern() { DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), - templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH), + templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH), "date_as_string", Collections.singletonList("yyyy dd MM hh:mm:ss"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "2010 12 06 11:05:15"); @@ -63,7 +66,7 @@ public void testJodaPatternMultipleFormats() { matchFormats.add("dd/MM/yyyy"); matchFormats.add("dd-MM-yyyy"); DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), - templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH), + templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH), "date_as_string", matchFormats, "date_as_date"); Map document = new HashMap<>(); @@ -98,7 +101,7 @@ public void testJodaPatternMultipleFormats() { public void testInvalidJodaPattern() { try { DateProcessor processor = new DateProcessor(randomAlphaOfLength(10), - templatize(UTC), templatize(randomLocale(random())), + templatize(ZoneOffset.UTC), templatize(randomLocale(random())), "date_as_string", Collections.singletonList("invalid pattern"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "2010"); @@ -112,7 +115,7 @@ public void testInvalidJodaPattern() { public void testJodaPatternLocale() { DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), - templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ITALIAN), + templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ITALIAN), "date_as_string", Collections.singletonList("yyyy dd MMM"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "2010 12 giugno"); @@ -123,18 +126,18 @@ public void testJodaPatternLocale() { public void testJodaPatternDefaultYear() { DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), - templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH), + templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH), "date_as_string", Collections.singletonList("dd/MM"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "12/06"); IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document); dateProcessor.execute(ingestDocument); assertThat(ingestDocument.getFieldValue("date_as_date", String.class), - equalTo(DateTime.now().getYear() + "-06-12T00:00:00.000+02:00")); + equalTo(ZonedDateTime.now().getYear() + "-06-12T00:00:00.000+02:00")); } public void testTAI64N() { - DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(DateTimeZone.forOffsetHours(2)), + DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.ofHours(2)), templatize(randomLocale(random())), "date_as_string", Collections.singletonList("TAI64N"), "date_as_date"); Map document = new HashMap<>(); @@ -146,8 +149,8 @@ public void testTAI64N() { } public void testUnixMs() { - DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC), templatize(randomLocale(random())), - "date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date"); + DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC), + templatize(randomLocale(random())), "date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "1000500"); IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document); @@ -162,7 +165,7 @@ public void testUnixMs() { } public void testUnix() { - DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC), + DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC), templatize(randomLocale(random())), "date_as_string", Collections.singletonList("UNIX"), "date_as_date"); Map document = new HashMap<>(); @@ -186,7 +189,7 @@ public void testInvalidTimezone() { public void testInvalidLocale() { DateProcessor processor = new DateProcessor(randomAlphaOfLength(10), - templatize(UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"), + templatize(ZoneOffset.UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"), "date_as_string", Collections.singletonList("yyyy"), "date_as_date"); Map document = new HashMap<>(); document.put("date_as_string", "2010"); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java index 17cf34c1e8736..5438e39904cb1 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java @@ -23,7 +23,6 @@ import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.carrotsearch.hppc.cursors.ObjectCursor; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.rollover.RolloverInfo; import org.elasticsearch.action.support.ActiveShardCount; @@ -56,10 +55,11 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -1346,7 +1346,7 @@ public static Settings addHumanReadableSettings(Settings settings) { } Long creationDate = settings.getAsLong(SETTING_CREATION_DATE, null); if (creationDate != null) { - DateTime creationDateTime = new DateTime(creationDate, DateTimeZone.UTC); + ZonedDateTime creationDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(creationDate), ZoneOffset.UTC); builder.put(SETTING_CREATION_DATE_STRING, creationDateTime.toString()); } return builder.build(); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index f63876f836fab..4052d4b2ce883 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -75,11 +75,10 @@ import org.elasticsearch.indices.InvalidIndexNameException; import org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason; import org.elasticsearch.threadpool.ThreadPool; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.io.UnsupportedEncodingException; import java.nio.file.Path; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -390,7 +389,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { } if (indexSettingsBuilder.get(SETTING_CREATION_DATE) == null) { - indexSettingsBuilder.put(SETTING_CREATION_DATE, new DateTime(DateTimeZone.UTC).getMillis()); + indexSettingsBuilder.put(SETTING_CREATION_DATE, Instant.now().toEpochMilli()); } indexSettingsBuilder.put(IndexMetaData.SETTING_INDEX_PROVIDED_NAME, request.getProvidedName()); indexSettingsBuilder.put(SETTING_INDEX_UUID, UUIDs.randomBase64UUID()); diff --git a/server/src/main/java/org/elasticsearch/common/time/CompoundDateTimeFormatter.java b/server/src/main/java/org/elasticsearch/common/time/CompoundDateTimeFormatter.java new file mode 100644 index 0000000000000..df459679c22b4 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/time/CompoundDateTimeFormatter.java @@ -0,0 +1,73 @@ +/* + * 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.common.time; + +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; + +/** + * wrapper class around java.time.DateTimeFormatter that supports multiple formats for easier parsing, + * and one specific format for printing + */ +public class CompoundDateTimeFormatter { + + final DateTimeFormatter printer; + final DateTimeFormatter[] parsers; + + CompoundDateTimeFormatter(DateTimeFormatter ... parsers) { + if (parsers.length == 0) { + throw new IllegalArgumentException("at least one date time formatter is required"); + } + this.printer = parsers[0]; + this.parsers = parsers; + } + + 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); + } + } + } + + // ensure that all parsers exceptions are returned instead of only the last one + throw failure; + } + + public CompoundDateTimeFormatter withZone(ZoneId zoneId) { + final DateTimeFormatter[] parsersWithZone = new DateTimeFormatter[parsers.length]; + for (int i = 0; i < parsers.length; i++) { + parsersWithZone[i] = parsers[i].withZone(zoneId); + } + + return new CompoundDateTimeFormatter(parsersWithZone); + } + + public String format(TemporalAccessor accessor) { + return printer.format(accessor); + } +} diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java new file mode 100644 index 0000000000000..eef2ab5558789 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -0,0 +1,1072 @@ +/* + * 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.common.time; + +import org.elasticsearch.common.Strings; + +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.time.format.SignStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.IsoFields; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdjusters; +import java.time.temporal.WeekFields; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Locale; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; + +public class DateFormatters { + + private static final DateTimeFormatter TIME_ZONE_FORMATTER_ZONE_ID = new DateTimeFormatterBuilder() + .appendZoneId() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_FORMATTER_WITHOUT_COLON = new DateTimeFormatterBuilder() + .appendOffset("+HHmm", "Z") + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_FORMATTER_WITH_COLON = new DateTimeFormatterBuilder() + .appendOffset("+HH:mm", "Z") + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_FORMATTER = new DateTimeFormatterBuilder() + .optionalStart().appendZoneId().optionalEnd() + .optionalStart().appendOffset("+HHmm", "Z").optionalEnd() + .optionalStart().appendOffset("+HH:mm", "Z").optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter OPTIONAL_TIME_ZONE_FORMATTER = new DateTimeFormatterBuilder() + .optionalStart() + .append(TIME_ZONE_FORMATTER) + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter STRICT_YEAR_MONTH_DAY_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral("-") + .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral('-') + .appendValue(DAY_OF_MONTH, 2, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter STRICT_HOUR_MINUTE_SECOND_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_1 = 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() + .optionalStart() + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .optionalEnd() + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_2 = 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() + .optionalStart() + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .optionalEnd() + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_FORMATTER_3 = 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() + .optionalStart() + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .optionalEnd() + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_DATE_OPTIONAL_TIME = + new CompoundDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME_FORMATTER_1, STRICT_DATE_OPTIONAL_TIME_FORMATTER_2, + STRICT_DATE_OPTIONAL_TIME_FORMATTER_3); + + private static final DateTimeFormatter BASIC_TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter BASIC_TIME_NO_MILLIS = new CompoundDateTimeFormatter(BASIC_TIME_NO_MILLIS_FORMATTER); + + private static final DateTimeFormatter BASIC_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter BASIC_TIME = new CompoundDateTimeFormatter(BASIC_TIME_FORMATTER); + + private static final DateTimeFormatter BASIC_T_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(BASIC_TIME_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter BASIC_T_TIME = new CompoundDateTimeFormatter(BASIC_T_TIME_FORMATTER); + + private static final CompoundDateTimeFormatter BASIC_T_TIME_NO_MILLIS = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(BASIC_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 4, SignStyle.NORMAL) + .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(DAY_OF_MONTH, 2, 2, SignStyle.NOT_NEGATIVE) + .append(BASIC_T_TIME_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 4, SignStyle.NORMAL) + .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendValue(DAY_OF_MONTH, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral("T") + .append(BASIC_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE = new CompoundDateTimeFormatter( + DateTimeFormatter.ofPattern("yyyyDDD", Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendPattern("yyyyDDD") + .append(BASIC_T_TIME_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .appendPattern("yyyyDDD") + .appendLiteral("T") + .append(BASIC_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter BASIC_WEEK_DATE_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(IsoFields.WEEK_BASED_YEAR) + .appendLiteral("W") + .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1, 2, SignStyle.NEVER) + .appendValue(ChronoField.DAY_OF_WEEK) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter BASIC_WEEK_DATE = new CompoundDateTimeFormatter(BASIC_WEEK_DATE_FORMATTER); + + private static final CompoundDateTimeFormatter BASIC_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(BASIC_WEEK_DATE_FORMATTER) + .appendLiteral("T") + .append(BASIC_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_WEEK_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(BASIC_WEEK_DATE_FORMATTER) + .append(BASIC_T_TIME_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 1, 4, SignStyle.NORMAL) + .appendLiteral('-') + .appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral('-') + .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter DATE = new CompoundDateTimeFormatter(DATE_FORMATTER); + + private static final CompoundDateTimeFormatter HOUR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter DATE_HOUR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral("T") + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter HOUR_MINUTE_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter HOUR_MINUTE = new CompoundDateTimeFormatter(HOUR_MINUTE_FORMATTER); + + private static final DateTimeFormatter DATE_TIME_PREFIX = new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral('T') + .append(HOUR_MINUTE_FORMATTER) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .optionalEnd() + .toFormatter(Locale.ROOT); + + // only the formatter, nothing optional here + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral('T') + .append(HOUR_MINUTE_FORMATTER) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendZoneId() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_1 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_2 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_3 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_4 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .optionalStart() + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_5 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .optionalStart() + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter DATE_TIME_NO_MILLIS_6 = new DateTimeFormatterBuilder() + .append(DATE_TIME_PREFIX) + .optionalStart() + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .optionalEnd() + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter(DATE_TIME_NO_MILLIS_FORMATTER, + DATE_TIME_NO_MILLIS_1, DATE_TIME_NO_MILLIS_2, DATE_TIME_NO_MILLIS_3, DATE_TIME_NO_MILLIS_4, DATE_TIME_NO_MILLIS_5, + DATE_TIME_NO_MILLIS_6); + + private static final CompoundDateTimeFormatter DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral('T') + .append(HOUR_MINUTE_FORMATTER) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .optionalEnd() + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter DATE_OPTIONAL_TIME = new CompoundDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME.printer, + new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .parseLenient() + .optionalStart() + .appendLiteral('T') + .append(HOUR_MINUTE_FORMATTER) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .optionalEnd() + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .optionalEnd() + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter HOUR_MINUTE_SECOND_FORMATTER = new DateTimeFormatterBuilder() + .append(HOUR_MINUTE_FORMATTER) + .appendLiteral(":") + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter HOUR_MINUTE_SECOND = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(HOUR_MINUTE_FORMATTER) + .appendLiteral(":") + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral("T") + .append(HOUR_MINUTE_SECOND_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral("T") + .append(HOUR_MINUTE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter HOUR_MINUTE_SECOND_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter HOUR_MINUTE_SECOND_MILLIS = + new CompoundDateTimeFormatter(HOUR_MINUTE_SECOND_MILLIS_FORMATTER); + + private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND_MILLIS = + new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral("T") + .append(HOUR_MINUTE_SECOND_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter DATE_HOUR_MINUTE_SECOND_FRACTION = + new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DATE_FORMATTER) + .appendLiteral("T") + .append(HOUR_MINUTE_SECOND_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter ORDINAL_DATE_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(DAY_OF_YEAR, 1, 3, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter ORDINAL_DATE = new CompoundDateTimeFormatter(ORDINAL_DATE_FORMATTER); + + private static final CompoundDateTimeFormatter ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(ORDINAL_DATE_FORMATTER) + .appendLiteral('T') + .append(HOUR_MINUTE_SECOND_FORMATTER) + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter ORDINAL_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(ORDINAL_DATE_FORMATTER) + .appendLiteral('T') + .append(HOUR_MINUTE_FORMATTER) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .optionalEnd() + .append(TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter TIME_FORMATTER_1 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_FORMATTER_2 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_FORMATTER_3 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_PREFIX = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_ID = new DateTimeFormatterBuilder() + .append(TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_WITH_COLON = new DateTimeFormatterBuilder() + .append(TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_ZONE_WITHOUT_COLON = new DateTimeFormatterBuilder() + .append(TIME_PREFIX) + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter T_TIME = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_FORMATTER_1).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_FORMATTER_2).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().appendLiteral("T").append(TIME_FORMATTER_3).toFormatter(Locale.ROOT) + ); + + private static final DateTimeFormatter TIME_NO_MILLIS_FORMATTER_1 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .append(TIME_ZONE_FORMATTER_ZONE_ID) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_NO_MILLIS_FORMATTER_2 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .append(TIME_ZONE_FORMATTER_WITH_COLON) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter TIME_NO_MILLIS_FORMATTER_3 = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE) + .append(TIME_ZONE_FORMATTER_WITHOUT_COLON) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter TIME = new CompoundDateTimeFormatter(TIME_ZONE_ID, TIME_ZONE_WITH_COLON, + TIME_ZONE_WITHOUT_COLON); + + private static final CompoundDateTimeFormatter TIME_NO_MILLIS = + new CompoundDateTimeFormatter(TIME_NO_MILLIS_FORMATTER_1, TIME_NO_MILLIS_FORMATTER_2, TIME_NO_MILLIS_FORMATTER_3); + + private static final DateTimeFormatter T_TIME_NO_MILLIS_FORMATTER_1 = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(TIME_NO_MILLIS_FORMATTER_1) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter T_TIME_NO_MILLIS_FORMATTER_2 = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(TIME_NO_MILLIS_FORMATTER_2) + .toFormatter(Locale.ROOT); + + private static final DateTimeFormatter T_TIME_NO_MILLIS_FORMATTER_3 = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(TIME_NO_MILLIS_FORMATTER_3) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter T_TIME_NO_MILLIS = + new CompoundDateTimeFormatter(T_TIME_NO_MILLIS_FORMATTER_1, T_TIME_NO_MILLIS_FORMATTER_2, T_TIME_NO_MILLIS_FORMATTER_3); + + private static final DateTimeFormatter WEEK_DATE_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral("-W") + .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral('-') + .appendValue(DAY_OF_WEEK, 1) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter WEEK_DATE = new CompoundDateTimeFormatter(WEEK_DATE_FORMATTER); + + private static final CompoundDateTimeFormatter WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER_1).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER_2).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).append(T_TIME_NO_MILLIS_FORMATTER_3).toFormatter(Locale.ROOT) + ); + + private static final CompoundDateTimeFormatter WEEK_DATE_TIME = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_FORMATTER_1).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_FORMATTER_2).toFormatter(Locale.ROOT), + new DateTimeFormatterBuilder().append(WEEK_DATE_FORMATTER).appendLiteral("T").append(TIME_FORMATTER_3).toFormatter(Locale.ROOT) + ); + + private static final CompoundDateTimeFormatter WEEK_YEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(WeekFields.ISO.weekBasedYear()) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter WEEKYEAR_WEEK = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(WeekFields.ISO.weekBasedYear()) + .appendLiteral("-W") + .appendValue(WeekFields.ISO.weekOfWeekBasedYear()) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter WEEKYEAR_WEEK_DAY = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(WeekFields.ISO.weekBasedYear()) + .appendLiteral("-W") + .appendValue(WeekFields.ISO.weekOfWeekBasedYear()) + .appendLiteral("-") + .appendValue(WeekFields.ISO.dayOfWeek()) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter YEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter YEAR_MONTH = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR) + .appendLiteral("-") + .appendValue(MONTH_OF_YEAR) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter YEAR_MONTH_DAY = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR) + .appendLiteral("-") + .appendValue(MONTH_OF_YEAR) + .appendLiteral("-") + .appendValue(DAY_OF_MONTH) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter EPOCH_SECOND = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.INSTANT_SECONDS) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter EPOCH_MILLIS = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER) + .appendValue(ChronoField.MILLI_OF_SECOND, 3) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter STRICT_BASIC_WEEK_DATE_FORMATTER = new DateTimeFormatterBuilder() + .parseStrict() + .appendValue(IsoFields.WEEK_BASED_YEAR, 4) + .appendLiteral("W") + .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 1, 2, SignStyle.NEVER) + .appendValue(ChronoField.DAY_OF_WEEK) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE = new CompoundDateTimeFormatter(STRICT_BASIC_WEEK_DATE_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(STRICT_BASIC_WEEK_DATE_FORMATTER) + .append(DateTimeFormatter.ofPattern("'T'HHmmssX", Locale.ROOT)) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_BASIC_WEEK_DATE_TIME = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(STRICT_BASIC_WEEK_DATE_FORMATTER) + .append(DateTimeFormatter.ofPattern("'T'HHmmss.SSSX", Locale.ROOT)) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_DATE = new CompoundDateTimeFormatter( + DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.LENIENT)); + + private static final CompoundDateTimeFormatter STRICT_DATE_HOUR = new CompoundDateTimeFormatter( + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH", Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE = new CompoundDateTimeFormatter( + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm", Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_YEAR_MONTH_DAY = new CompoundDateTimeFormatter(STRICT_YEAR_MONTH_DAY_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_YEAR_MONTH = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral("-") + .appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_YEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND = + new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE_SECOND_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(STRICT_YEAR_MONTH_DAY_FORMATTER) + .appendLiteral('T') + .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) + .optionalStart() + .appendFraction(MILLI_OF_SECOND, 3, 3, true) + .optionalEnd() + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_ORDINAL_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(DAY_OF_YEAR, 3, 3, SignStyle.NOT_NEGATIVE) + .appendLiteral('T') + .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(STRICT_YEAR_MONTH_DAY_FORMATTER) + .appendLiteral('T') + .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter STRICT_HOUR_MINUTE_SECOND_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .append(STRICT_HOUR_MINUTE_SECOND_FORMATTER) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND_MILLIS = + new CompoundDateTimeFormatter(STRICT_HOUR_MINUTE_SECOND_MILLIS_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE_SECOND_FRACTION = STRICT_HOUR_MINUTE_SECOND_MILLIS; + + private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(STRICT_YEAR_MONTH_DAY_FORMATTER) + .appendLiteral("T") + .append(STRICT_HOUR_MINUTE_SECOND_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS = STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION; + + private static final CompoundDateTimeFormatter STRICT_HOUR = + new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("HH", Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_HOUR_MINUTE = + new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("HH:mm", Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_ORDINAL_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(DAY_OF_YEAR, 3, 3, SignStyle.NOT_NEGATIVE) + .appendLiteral('T') + .appendPattern("HH:mm") + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .optionalEnd() + .append(OPTIONAL_TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter STRICT_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .appendFraction(MILLI_OF_SECOND, 1, 3, true) + .append(TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_TIME = new CompoundDateTimeFormatter(STRICT_TIME_FORMATTER); + + private static final DateTimeFormatter STRICT_T_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(STRICT_TIME_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_T_TIME = new CompoundDateTimeFormatter(STRICT_T_TIME_FORMATTER); + + private static final DateTimeFormatter STRICT_TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2, 2, SignStyle.NOT_NEGATIVE) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + .append(TIME_ZONE_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_TIME_NO_MILLIS = new CompoundDateTimeFormatter(STRICT_TIME_NO_MILLIS_FORMATTER); + + private static final DateTimeFormatter STRICT_T_TIME_NO_MILLIS_FORMATTER = new DateTimeFormatterBuilder() + .appendLiteral("T") + .append(STRICT_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_T_TIME_NO_MILLIS = + new CompoundDateTimeFormatter(STRICT_T_TIME_NO_MILLIS_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_WEEK_DATE = new CompoundDateTimeFormatter(DateTimeFormatter.ISO_WEEK_DATE); + + private static final CompoundDateTimeFormatter STRICT_WEEK_DATE_TIME_NO_MILLIS = new CompoundDateTimeFormatter( + new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ISO_WEEK_DATE) + .append(STRICT_T_TIME_NO_MILLIS_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_WEEK_DATE_TIME = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ISO_WEEK_DATE) + .append(STRICT_T_TIME_FORMATTER) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter STRICT_WEEKYEAR = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .appendValue(WeekFields.ISO.weekBasedYear(), 4, 10, SignStyle.EXCEEDS_PAD) + .toFormatter(Locale.ROOT)); + + private static final DateTimeFormatter STRICT_WEEKYEAR_WEEK_FORMATTER = new DateTimeFormatterBuilder() + .appendValue(WeekFields.ISO.weekBasedYear(), 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral("-W") + .appendValue(WeekFields.ISO.weekOfWeekBasedYear(), 2, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ROOT); + + private static final CompoundDateTimeFormatter STRICT_WEEKYEAR_WEEK = new CompoundDateTimeFormatter(STRICT_WEEKYEAR_WEEK_FORMATTER); + + private static final CompoundDateTimeFormatter STRICT_WEEKYEAR_WEEK_DAY = new CompoundDateTimeFormatter(new DateTimeFormatterBuilder() + .append(STRICT_WEEKYEAR_WEEK_FORMATTER) + .appendLiteral("-") + .appendValue(WeekFields.ISO.dayOfWeek()) + .toFormatter(Locale.ROOT)); + + private static final CompoundDateTimeFormatter BASIC_ISO_DATE = new CompoundDateTimeFormatter(DateTimeFormatter.BASIC_ISO_DATE); + private static final CompoundDateTimeFormatter ISO_ORDINAL_DATE = new CompoundDateTimeFormatter(DateTimeFormatter.ISO_ORDINAL_DATE); + private static final CompoundDateTimeFormatter STRICT_DATE_HOUR_MINUTE_SECOND = + new CompoundDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT)); + + public static CompoundDateTimeFormatter forPattern(String input) { + return forPattern(input, Locale.ROOT); + } + + public static CompoundDateTimeFormatter forPattern(String input, Locale locale) { + if (Strings.hasLength(input)) { + input = input.trim(); + } + if (input == null || input.length() == 0) { + throw new IllegalArgumentException("No date pattern provided"); + } + + if ("basicDate".equals(input) || "basic_date".equals(input)) { + return BASIC_ISO_DATE; + } else if ("basicDateTime".equals(input) || "basic_date_time".equals(input)) { + return BASIC_DATE_TIME; + } else if ("basicDateTimeNoMillis".equals(input) || "basic_date_time_no_millis".equals(input)) { + return BASIC_DATE_TIME_NO_MILLIS; + } else if ("basicOrdinalDate".equals(input) || "basic_ordinal_date".equals(input)) { + return BASIC_ORDINAL_DATE; + } else if ("basicOrdinalDateTime".equals(input) || "basic_ordinal_date_time".equals(input)) { + return BASIC_ORDINAL_DATE_TIME; + } else if ("basicOrdinalDateTimeNoMillis".equals(input) || "basic_ordinal_date_time_no_millis".equals(input)) { + return BASIC_ORDINAL_DATE_TIME_NO_MILLIS; + } else if ("basicTime".equals(input) || "basic_time".equals(input)) { + return BASIC_TIME; + } else if ("basicTimeNoMillis".equals(input) || "basic_time_no_millis".equals(input)) { + return BASIC_TIME_NO_MILLIS; + } else if ("basicTTime".equals(input) || "basic_t_time".equals(input)) { + return BASIC_T_TIME; + } else if ("basicTTimeNoMillis".equals(input) || "basic_t_time_no_millis".equals(input)) { + return BASIC_T_TIME_NO_MILLIS; + } else if ("basicWeekDate".equals(input) || "basic_week_date".equals(input)) { + return BASIC_WEEK_DATE; + } else if ("basicWeekDateTime".equals(input) || "basic_week_date_time".equals(input)) { + return BASIC_WEEK_DATE_TIME; + } else if ("basicWeekDateTimeNoMillis".equals(input) || "basic_week_date_time_no_millis".equals(input)) { + return BASIC_WEEK_DATE_TIME_NO_MILLIS; + } else if ("date".equals(input)) { + return DATE; + } else if ("dateHour".equals(input) || "date_hour".equals(input)) { + return DATE_HOUR; + } else if ("dateHourMinute".equals(input) || "date_hour_minute".equals(input)) { + return DATE_HOUR_MINUTE; + } else if ("dateHourMinuteSecond".equals(input) || "date_hour_minute_second".equals(input)) { + return DATE_HOUR_MINUTE_SECOND; + } else if ("dateHourMinuteSecondFraction".equals(input) || "date_hour_minute_second_fraction".equals(input)) { + return DATE_HOUR_MINUTE_SECOND_FRACTION; + } else if ("dateHourMinuteSecondMillis".equals(input) || "date_hour_minute_second_millis".equals(input)) { + return DATE_HOUR_MINUTE_SECOND_MILLIS; + } else if ("dateOptionalTime".equals(input) || "date_optional_time".equals(input)) { + return DATE_OPTIONAL_TIME; + } else if ("dateTime".equals(input) || "date_time".equals(input)) { + return DATE_TIME; + } else if ("dateTimeNoMillis".equals(input) || "date_time_no_millis".equals(input)) { + return DATE_TIME_NO_MILLIS; + } else if ("hour".equals(input)) { + return HOUR; + } else if ("hourMinute".equals(input) || "hour_minute".equals(input)) { + return HOUR_MINUTE; + } else if ("hourMinuteSecond".equals(input) || "hour_minute_second".equals(input)) { + return HOUR_MINUTE_SECOND; + } else if ("hourMinuteSecondFraction".equals(input) || "hour_minute_second_fraction".equals(input)) { + return HOUR_MINUTE_SECOND_MILLIS; + } else if ("hourMinuteSecondMillis".equals(input) || "hour_minute_second_millis".equals(input)) { + return HOUR_MINUTE_SECOND_MILLIS; + } else if ("ordinalDate".equals(input) || "ordinal_date".equals(input)) { + return ORDINAL_DATE; + } else if ("ordinalDateTime".equals(input) || "ordinal_date_time".equals(input)) { + return ORDINAL_DATE_TIME; + } else if ("ordinalDateTimeNoMillis".equals(input) || "ordinal_date_time_no_millis".equals(input)) { + return ORDINAL_DATE_TIME_NO_MILLIS; + } else if ("time".equals(input)) { + return TIME; + } else if ("timeNoMillis".equals(input) || "time_no_millis".equals(input)) { + return TIME_NO_MILLIS; + } else if ("tTime".equals(input) || "t_time".equals(input)) { + return T_TIME; + } else if ("tTimeNoMillis".equals(input) || "t_time_no_millis".equals(input)) { + return T_TIME_NO_MILLIS; + } else if ("weekDate".equals(input) || "week_date".equals(input)) { + return WEEK_DATE; + } else if ("weekDateTime".equals(input) || "week_date_time".equals(input)) { + return WEEK_DATE_TIME; + } else if ("weekDateTimeNoMillis".equals(input) || "week_date_time_no_millis".equals(input)) { + return WEEK_DATE_TIME_NO_MILLIS; + } else if ("weekyear".equals(input) || "week_year".equals(input)) { + return WEEK_YEAR; + } else if ("weekyearWeek".equals(input) || "weekyear_week".equals(input)) { + return WEEKYEAR_WEEK; + } else if ("weekyearWeekDay".equals(input) || "weekyear_week_day".equals(input)) { + return WEEKYEAR_WEEK_DAY; + } else if ("year".equals(input)) { + return YEAR; + } else if ("yearMonth".equals(input) || "year_month".equals(input)) { + return YEAR_MONTH; + } else if ("yearMonthDay".equals(input) || "year_month_day".equals(input)) { + return YEAR_MONTH_DAY; + } else if ("epoch_second".equals(input)) { + return EPOCH_SECOND; + } else if ("epoch_millis".equals(input)) { + return EPOCH_MILLIS; + // strict date formats here, must be at least 4 digits for year and two for months and two for day + } else if ("strictBasicWeekDate".equals(input) || "strict_basic_week_date".equals(input)) { + return STRICT_BASIC_WEEK_DATE; + } else if ("strictBasicWeekDateTime".equals(input) || "strict_basic_week_date_time".equals(input)) { + return STRICT_BASIC_WEEK_DATE_TIME; + } else if ("strictBasicWeekDateTimeNoMillis".equals(input) || "strict_basic_week_date_time_no_millis".equals(input)) { + return STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS; + } else if ("strictDate".equals(input) || "strict_date".equals(input)) { + return STRICT_DATE; + } else if ("strictDateHour".equals(input) || "strict_date_hour".equals(input)) { + return STRICT_DATE_HOUR; + } else if ("strictDateHourMinute".equals(input) || "strict_date_hour_minute".equals(input)) { + return STRICT_DATE_HOUR_MINUTE; + } else if ("strictDateHourMinuteSecond".equals(input) || "strict_date_hour_minute_second".equals(input)) { + return STRICT_DATE_HOUR_MINUTE_SECOND; + } else if ("strictDateHourMinuteSecondFraction".equals(input) || "strict_date_hour_minute_second_fraction".equals(input)) { + return STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION; + } else if ("strictDateHourMinuteSecondMillis".equals(input) || "strict_date_hour_minute_second_millis".equals(input)) { + return STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS; + } else if ("strictDateOptionalTime".equals(input) || "strict_date_optional_time".equals(input)) { + return STRICT_DATE_OPTIONAL_TIME; + } else if ("strictDateTime".equals(input) || "strict_date_time".equals(input)) { + return STRICT_DATE_TIME; + } else if ("strictDateTimeNoMillis".equals(input) || "strict_date_time_no_millis".equals(input)) { + return STRICT_DATE_TIME_NO_MILLIS; + } else if ("strictHour".equals(input) || "strict_hour".equals(input)) { + return STRICT_HOUR; + } else if ("strictHourMinute".equals(input) || "strict_hour_minute".equals(input)) { + return STRICT_HOUR_MINUTE; + } else if ("strictHourMinuteSecond".equals(input) || "strict_hour_minute_second".equals(input)) { + return STRICT_HOUR_MINUTE_SECOND; + } else if ("strictHourMinuteSecondFraction".equals(input) || "strict_hour_minute_second_fraction".equals(input)) { + return STRICT_HOUR_MINUTE_SECOND_FRACTION; + } else if ("strictHourMinuteSecondMillis".equals(input) || "strict_hour_minute_second_millis".equals(input)) { + return STRICT_HOUR_MINUTE_SECOND_MILLIS; + } else if ("strictOrdinalDate".equals(input) || "strict_ordinal_date".equals(input)) { + return ISO_ORDINAL_DATE; + } else if ("strictOrdinalDateTime".equals(input) || "strict_ordinal_date_time".equals(input)) { + return STRICT_ORDINAL_DATE_TIME; + } else if ("strictOrdinalDateTimeNoMillis".equals(input) || "strict_ordinal_date_time_no_millis".equals(input)) { + return STRICT_ORDINAL_DATE_TIME_NO_MILLIS; + } else if ("strictTime".equals(input) || "strict_time".equals(input)) { + return STRICT_TIME; + } else if ("strictTimeNoMillis".equals(input) || "strict_time_no_millis".equals(input)) { + return STRICT_TIME_NO_MILLIS; + } else if ("strictTTime".equals(input) || "strict_t_time".equals(input)) { + return STRICT_T_TIME; + } else if ("strictTTimeNoMillis".equals(input) || "strict_t_time_no_millis".equals(input)) { + return STRICT_T_TIME_NO_MILLIS; + } else if ("strictWeekDate".equals(input) || "strict_week_date".equals(input)) { + return STRICT_WEEK_DATE; + } else if ("strictWeekDateTime".equals(input) || "strict_week_date_time".equals(input)) { + return STRICT_WEEK_DATE_TIME; + } else if ("strictWeekDateTimeNoMillis".equals(input) || "strict_week_date_time_no_millis".equals(input)) { + return STRICT_WEEK_DATE_TIME_NO_MILLIS; + } else if ("strictWeekyear".equals(input) || "strict_weekyear".equals(input)) { + return STRICT_WEEKYEAR; + } else if ("strictWeekyearWeek".equals(input) || "strict_weekyear_week".equals(input)) { + return STRICT_WEEKYEAR_WEEK; + } else if ("strictWeekyearWeekDay".equals(input) || "strict_weekyear_week_day".equals(input)) { + return STRICT_WEEKYEAR_WEEK_DAY; + } else if ("strictYear".equals(input) || "strict_year".equals(input)) { + return STRICT_YEAR; + } else if ("strictYearMonth".equals(input) || "strict_year_month".equals(input)) { + return STRICT_YEAR_MONTH; + } else if ("strictYearMonthDay".equals(input) || "strict_year_month_day".equals(input)) { + return STRICT_YEAR_MONTH_DAY; + } else if (Strings.hasLength(input) && input.contains("||")) { + String[] formats = Strings.delimitedListToStringArray(input, "||"); + if (formats.length == 1) { + return forPattern(formats[0], locale); + } else { + Collection parsers = new LinkedHashSet<>(formats.length); + for (String format : formats) { + CompoundDateTimeFormatter dateTimeFormatter = forPattern(format, locale); + try { + parsers.addAll(Arrays.asList(dateTimeFormatter.parsers)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e); + } + } + + return new CompoundDateTimeFormatter(parsers.toArray(new DateTimeFormatter[0])); + } + } else { + try { + return new CompoundDateTimeFormatter(new DateTimeFormatterBuilder().appendPattern(input).toFormatter(locale)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e); + } + } + } + + private static final ZonedDateTime EPOCH_ZONED_DATE_TIME = Instant.EPOCH.atZone(ZoneOffset.UTC); + + 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 ) { + } + + 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; + } + + // 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)); + } 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 + .with(WeekFields.ISO.weekOfWeekBasedYear(), accessor.getLong(WeekFields.ISO.weekOfWeekBasedYear())) + .atStartOfDay(ZoneOffset.UTC); + } 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)) + .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); + } + } 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))); + } + + // millis + if (accessor.isSupported(ChronoField.MILLI_OF_SECOND)) { + result = result.with(ChronoField.MILLI_OF_SECOND, accessor.getLong(ChronoField.MILLI_OF_SECOND)); + } + + if (accessor.isSupported(ChronoField.NANO_OF_SECOND)) { + result = result.with(ChronoField.NANO_OF_SECOND, accessor.getLong(ChronoField.NANO_OF_SECOND)); + } + + return result; + } +} diff --git a/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java b/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java index 3b6415437f97c..10a3e81163ab6 100644 --- a/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java +++ b/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java @@ -21,13 +21,15 @@ import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.joda.FormatDateTimeFormatter; -import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.time.CompoundDateTimeFormatter; +import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.unit.TimeValue; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; +import java.time.Clock; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -41,7 +43,7 @@ public class HotThreads { private static final Object mutex = new Object(); - private static final FormatDateTimeFormatter DATE_TIME_FORMATTER = Joda.forPattern("dateOptionalTime"); + private static final CompoundDateTimeFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("dateOptionalTime"); private int busiestThreads = 3; private TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS); @@ -136,7 +138,7 @@ private String innerDetect() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("Hot threads at "); - sb.append(DATE_TIME_FORMATTER.printer().print(System.currentTimeMillis())); + sb.append(DATE_TIME_FORMATTER.format(LocalDateTime.now(Clock.systemUTC()))); sb.append(", interval="); sb.append(interval); sb.append(", busiestThreads="); diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java index 52da10a378576..3a76c7ca0c952 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java @@ -45,9 +45,10 @@ import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.action.RestActionListener; import org.elasticsearch.rest.action.RestResponseListener; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -379,7 +380,7 @@ Table buildTable(RestRequest request, Index[] indices, ClusterHealthResponse res table.addCell(primaryStats.getDocs() == null ? null : primaryStats.getDocs().getDeleted()); table.addCell(indexMetaData.getCreationDate()); - table.addCell(new DateTime(indexMetaData.getCreationDate(), DateTimeZone.UTC)); + table.addCell(ZonedDateTime.ofInstant(Instant.ofEpochMilli(indexMetaData.getCreationDate()), ZoneOffset.UTC)); table.addCell(totalStats.getStore() == null ? null : totalStats.getStore().size()); table.addCell(primaryStats.getStore() == null ? null : primaryStats.getStore().size()); diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index bf3d337c49ec5..67ddabc37fa30 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -27,8 +27,8 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.joda.FormatDateTimeFormatter; -import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.time.CompoundDateTimeFormatter; +import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContent; @@ -37,6 +37,8 @@ import org.elasticsearch.rest.RestStatus; import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -50,7 +52,7 @@ public final class SnapshotInfo implements Comparable, ToXContent, public static final String CONTEXT_MODE_PARAM = "context_mode"; public static final String CONTEXT_MODE_SNAPSHOT = "SNAPSHOT"; - private static final FormatDateTimeFormatter DATE_TIME_FORMATTER = Joda.forPattern("strictDateOptionalTime"); + private static final CompoundDateTimeFormatter DATE_TIME_FORMATTER = DateFormatters.forPattern("strictDateOptionalTime"); private static final String SNAPSHOT = "snapshot"; private static final String UUID = "uuid"; private static final String INDICES = "indices"; @@ -505,11 +507,11 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa builder.field(REASON, reason); } if (verbose || startTime != 0) { - builder.field(START_TIME, DATE_TIME_FORMATTER.printer().print(startTime)); + builder.field(START_TIME, DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(startTime).atZone(ZoneOffset.UTC))); builder.field(START_TIME_IN_MILLIS, startTime); } if (verbose || endTime != 0) { - builder.field(END_TIME, DATE_TIME_FORMATTER.printer().print(endTime)); + builder.field(END_TIME, DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(endTime).atZone(ZoneOffset.UTC))); builder.field(END_TIME_IN_MILLIS, endTime); builder.humanReadableField(DURATION_IN_MILLIS, DURATION, new TimeValue(endTime - startTime)); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java index aa35d9d273a92..3c0f4f47f9c72 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java @@ -25,16 +25,16 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -197,8 +197,8 @@ public void testRolloverOnExistingIndex() throws Exception { } public void testRolloverWithDateMath() { - DateTime now = new DateTime(DateTimeZone.UTC); - String index = "test-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now) + "-1"; + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + String index = "test-" + DateFormatters.forPattern("YYYY.MM.dd").format(now) + "-1"; String dateMathExp = ""; assertAcked(prepareCreate(dateMathExp).addAlias(new Alias("test_alias")).get()); ensureGreen(index); @@ -212,14 +212,14 @@ public void testRolloverWithDateMath() { ensureGreen(index); RolloverResponse response = client().admin().indices().prepareRolloverIndex("test_alias").get(); assertThat(response.getOldIndex(), equalTo(index)); - assertThat(response.getNewIndex(), equalTo("test-" + DateTimeFormat.forPattern("YYYY.MM").print(now) + "-000002")); + assertThat(response.getNewIndex(), equalTo("test-" + DateFormatters.forPattern("YYYY.MM").format(now) + "-000002")); assertThat(response.isDryRun(), equalTo(false)); assertThat(response.isRolledOver(), equalTo(true)); assertThat(response.getConditionStatus().size(), equalTo(0)); response = client().admin().indices().prepareRolloverIndex("test_alias").get(); - assertThat(response.getOldIndex(), equalTo("test-" + DateTimeFormat.forPattern("YYYY.MM").print(now) + "-000002")); - assertThat(response.getNewIndex(), equalTo("test-" + DateTimeFormat.forPattern("YYYY.MM").print(now) + "-000003")); + assertThat(response.getOldIndex(), equalTo("test-" + DateFormatters.forPattern("YYYY.MM").format(now) + "-000002")); + assertThat(response.getNewIndex(), equalTo("test-" + DateFormatters.forPattern("YYYY.MM").format(now) + "-000003")); assertThat(response.isDryRun(), equalTo(false)); assertThat(response.isRolledOver(), equalTo(true)); assertThat(response.getConditionStatus().size(), equalTo(0)); @@ -232,8 +232,8 @@ public void testRolloverWithDateMath() { IndexMetaData.SETTING_INDEX_PROVIDED_NAME)); response = client().admin().indices().prepareRolloverIndex("test_alias").setNewIndexName("").get(); - assertThat(response.getOldIndex(), equalTo("test-" + DateTimeFormat.forPattern("YYYY.MM").print(now) + "-000003")); - assertThat(response.getNewIndex(), equalTo("test-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now) + "-000004")); + assertThat(response.getOldIndex(), equalTo("test-" + DateFormatters.forPattern("YYYY.MM").format(now) + "-000003")); + assertThat(response.getNewIndex(), equalTo("test-" + DateFormatters.forPattern("YYYY.MM.dd").format(now) + "-000004")); assertThat(response.isDryRun(), equalTo(false)); assertThat(response.isRolledOver(), equalTo(true)); assertThat(response.getConditionStatus().size(), equalTo(0)); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/HumanReadableIndexSettingsTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/HumanReadableIndexSettingsTests.java index 9be087e0e5dbc..83c615e48822a 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/HumanReadableIndexSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/HumanReadableIndexSettingsTests.java @@ -22,8 +22,10 @@ import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import static org.elasticsearch.test.VersionUtils.randomVersion; @@ -42,6 +44,7 @@ public void testHumanReadableSettings() { assertEquals(versionCreated.toString(), humanSettings.get(IndexMetaData.SETTING_VERSION_CREATED_STRING, null)); assertEquals(versionUpgraded.toString(), humanSettings.get(IndexMetaData.SETTING_VERSION_UPGRADED_STRING, null)); - assertEquals(new DateTime(created, DateTimeZone.UTC).toString(), humanSettings.get(IndexMetaData.SETTING_CREATION_DATE_STRING, null)); + ZonedDateTime creationDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(created), ZoneOffset.UTC); + assertEquals(creationDate.toString(), humanSettings.get(IndexMetaData.SETTING_CREATION_DATE_STRING, null)); } } diff --git a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java new file mode 100644 index 0000000000000..d6f733d7c1cd4 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -0,0 +1,492 @@ +/* + * 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.common.joda; + +import org.elasticsearch.common.time.CompoundDateTimeFormatter; +import org.elasticsearch.common.time.DateFormatters; +import org.elasticsearch.test.ESTestCase; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.util.Locale; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; + +public class JavaJodaTimeDuellingTests extends ESTestCase { + + public void testTimeZoneFormatting() { + assertSameDate("2001-01-01T00:00:00Z", "date_time_no_millis"); + // the following fail under java 8 but work under java 10, needs investigation + assertSameDate("2001-01-01T00:00:00-0800", "date_time_no_millis"); + assertSameDate("2001-01-01T00:00:00+1030", "date_time_no_millis"); + assertSameDate("2001-01-01T00:00:00-08", "date_time_no_millis"); + assertSameDate("2001-01-01T00:00:00+10:30", "date_time_no_millis"); + + // different timezone parsing styles require a different number of letters + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSXXX", Locale.ROOT); + formatter.parse("20181126T121212.123Z"); + formatter.parse("20181126T121212.123-08:30"); + + DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSXXXX", Locale.ROOT); + formatter2.parse("20181126T121212.123+1030"); + formatter2.parse("20181126T121212.123-0830"); + + // ... and can be combined, note that this is not an XOR, so one could append both timezones with this example + DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSS[XXXX][XXX]", Locale.ROOT); + formatter3.parse("20181126T121212.123Z"); + formatter3.parse("20181126T121212.123-08:30"); + formatter3.parse("20181126T121212.123+1030"); + formatter3.parse("20181126T121212.123-0830"); + } + + public void testCustomTimeFormats() { + assertSameDate("2010 12 06 11:05:15", "yyyy dd MM HH:mm:ss"); + assertSameDate("12/06", "dd/MM"); + assertSameDate("Nov 24 01:29:01 -0800", "MMM dd HH:mm:ss Z"); + } + + public void testDuellingFormatsValidParsing() { + assertSameDate("1522332219", "epoch_second"); + assertSameDate("1522332219321", "epoch_millis"); + + assertSameDate("20181126", "basic_date"); + assertSameDate("20181126T121212.123Z", "basic_date_time"); + assertSameDate("20181126T121212.123+10:00", "basic_date_time"); + assertSameDate("20181126T121212.123-0800", "basic_date_time"); + + assertSameDate("20181126T121212Z", "basic_date_time_no_millis"); + assertSameDate("2018363", "basic_ordinal_date"); + assertSameDate("2018363T121212.123Z", "basic_ordinal_date_time"); + assertSameDate("2018363T121212Z", "basic_ordinal_date_time_no_millis"); + assertSameDate("121212.123Z", "basic_time"); + assertSameDate("121212Z", "basic_time_no_millis"); + assertSameDate("T121212.123Z", "basic_t_time"); + assertSameDate("T121212Z", "basic_t_time_no_millis"); + assertSameDate("2018W313", "basic_week_date"); + assertSameDate("1W313", "basic_week_date"); + assertSameDate("18W313", "basic_week_date"); + assertSameDate("2018W313T121212.123Z", "basic_week_date_time"); + assertSameDate("2018W313T121212Z", "basic_week_date_time_no_millis"); + + assertSameDate("2018-12-31", "date"); + assertSameDate("18-5-6", "date"); + + assertSameDate("2018-12-31T12", "date_hour"); + assertSameDate("2018-12-31T8", "date_hour"); + + assertSameDate("2018-12-31T12:12", "date_hour_minute"); + assertSameDate("2018-12-31T8:3", "date_hour_minute"); + + assertSameDate("2018-12-31T12:12:12", "date_hour_minute_second"); + assertSameDate("2018-12-31T12:12:1", "date_hour_minute_second"); + + assertSameDate("2018-12-31T12:12:12.123", "date_hour_minute_second_fraction"); + assertSameDate("2018-12-31T12:12:12.123", "date_hour_minute_second_millis"); + assertSameDate("2018-12-31T12:12:12.1", "date_hour_minute_second_millis"); + assertSameDate("2018-12-31T12:12:12.1", "date_hour_minute_second_fraction"); + + assertSameDate("2018-12-31", "date_optional_time"); + assertSameDate("2018-12-1", "date_optional_time"); + assertSameDate("2018-12-31T10:15:30", "date_optional_time"); + assertSameDate("2018-12-31T10:15:3", "date_optional_time"); + assertSameDate("2018-12-31T10:5:30", "date_optional_time"); + assertSameDate("2018-12-31T1:15:30", "date_optional_time"); + + assertSameDate("2018-12-31T10:15:30.123Z", "date_time"); + assertSameDate("2018-12-31T10:15:30.11Z", "date_time"); + assertSameDate("2018-12-31T10:15:3.123Z", "date_time"); + + assertSameDate("2018-12-31T10:15:30Z", "date_time_no_millis"); + assertSameDate("2018-12-31T10:5:30Z", "date_time_no_millis"); + assertSameDate("2018-12-31T10:15:3Z", "date_time_no_millis"); + assertSameDate("2018-12-31T1:15:30Z", "date_time_no_millis"); + + assertSameDate("12", "hour"); + assertSameDate("01", "hour"); + assertSameDate("1", "hour"); + + assertSameDate("12:12", "hour_minute"); + assertSameDate("12:01", "hour_minute"); + assertSameDate("12:1", "hour_minute"); + + assertSameDate("12:12:12", "hour_minute_second"); + assertSameDate("12:12:01", "hour_minute_second"); + assertSameDate("12:12:1", "hour_minute_second"); + + assertSameDate("12:12:12.123", "hour_minute_second_fraction"); + assertSameDate("12:12:12.1", "hour_minute_second_fraction"); + assertParseException("12:12:12", "hour_minute_second_fraction"); + assertSameDate("12:12:12.123", "hour_minute_second_millis"); + assertSameDate("12:12:12.1", "hour_minute_second_millis"); + assertParseException("12:12:12", "hour_minute_second_millis"); + + assertSameDate("2018-128", "ordinal_date"); + assertSameDate("2018-1", "ordinal_date"); + + assertSameDate("2018-128T10:15:30.123Z", "ordinal_date_time"); + assertSameDate("2018-1T10:15:30.123Z", "ordinal_date_time"); + + assertSameDate("2018-128T10:15:30Z", "ordinal_date_time_no_millis"); + assertSameDate("2018-1T10:15:30Z", "ordinal_date_time_no_millis"); + + assertSameDate("10:15:30.123Z", "time"); + assertSameDate("1:15:30.123Z", "time"); + assertSameDate("10:1:30.123Z", "time"); + assertSameDate("10:15:3.123Z", "time"); + assertParseException("10:15:3.1", "time"); + assertParseException("10:15:3Z", "time"); + + assertSameDate("10:15:30Z", "time_no_millis"); + assertSameDate("01:15:30Z", "time_no_millis"); + assertSameDate("1:15:30Z", "time_no_millis"); + assertSameDate("10:5:30Z", "time_no_millis"); + assertSameDate("10:15:3Z", "time_no_millis"); + assertParseException("10:15:3", "time_no_millis"); + + assertSameDate("T10:15:30.123Z", "t_time"); + assertSameDate("T1:15:30.123Z", "t_time"); + assertSameDate("T10:1:30.123Z", "t_time"); + assertSameDate("T10:15:3.123Z", "t_time"); + assertParseException("T10:15:3.1", "t_time"); + assertParseException("T10:15:3Z", "t_time"); + + assertSameDate("T10:15:30Z", "t_time_no_millis"); + assertSameDate("T1:15:30Z", "t_time_no_millis"); + assertSameDate("T10:1:30Z", "t_time_no_millis"); + assertSameDate("T10:15:3Z", "t_time_no_millis"); + assertParseException("T10:15:3", "t_time_no_millis"); + + assertSameDate("2012-W48-6", "week_date"); + assertSameDate("2012-W01-6", "week_date"); + assertSameDate("2012-W1-6", "week_date"); + // joda comes up with a different exception message here, so we have to adapt + assertJodaParseException("2012-W1-8", "week_date", + "Cannot parse \"2012-W1-8\": Value 8 for dayOfWeek must be in the range [1,7]"); + assertJavaTimeParseException("2012-W1-8", "week_date", "Text '2012-W1-8' could not be parsed"); + + assertSameDate("2012-W48-6T10:15:30.123Z", "week_date_time"); + assertSameDate("2012-W1-6T10:15:30.123Z", "week_date_time"); + + assertSameDate("2012-W48-6T10:15:30Z", "week_date_time_no_millis"); + assertSameDate("2012-W1-6T10:15:30Z", "week_date_time_no_millis"); + + assertSameDate("2012", "year"); + assertSameDate("1", "year"); + assertSameDate("-2000", "year"); + + assertSameDate("2012-12", "yearMonth"); + assertSameDate("1-1", "yearMonth"); + + assertSameDate("2012-12-31", "yearMonthDay"); + assertSameDate("1-12-31", "yearMonthDay"); + assertSameDate("2012-1-31", "yearMonthDay"); + assertSameDate("2012-12-1", "yearMonthDay"); + + assertSameDate("2018", "week_year"); + assertSameDate("1", "week_year"); + assertSameDate("2017", "week_year"); + + assertSameDate("2018-W29", "weekyear_week"); + assertSameDate("2018-W1", "weekyear_week"); + + assertSameDate("2012-W31-5", "weekyear_week_day"); + assertSameDate("2012-W1-1", "weekyear_week_day"); + } + + public void testDuelingStrictParsing() { + assertSameDate("2018W313", "strict_basic_week_date"); + assertParseException("18W313", "strict_basic_week_date"); + assertSameDate("2018W313T121212.123Z", "strict_basic_week_date_time"); + assertParseException("2018W313T12128.123Z", "strict_basic_week_date_time"); + assertParseException("2018W313T81212.123Z", "strict_basic_week_date_time"); + assertParseException("2018W313T12812.123Z", "strict_basic_week_date_time"); + assertParseException("2018W313T12812.1Z", "strict_basic_week_date_time"); + assertSameDate("2018W313T121212Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12128Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T81212Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12812Z", "strict_basic_week_date_time_no_millis"); + assertSameDate("2018-12-31", "strict_date"); + assertParseException("2018-8-31", "strict_date"); + assertSameDate("2018-12-31T12", "strict_date_hour"); + assertParseException("2018-12-31T8", "strict_date_hour"); + assertSameDate("2018-12-31T12:12", "strict_date_hour_minute"); + assertParseException("2018-12-31T8:3", "strict_date_hour_minute"); + assertSameDate("2018-12-31T12:12:12", "strict_date_hour_minute_second"); + assertParseException("2018-12-31T12:12:1", "strict_date_hour_minute_second"); + assertSameDate("2018-12-31T12:12:12.123", "strict_date_hour_minute_second_fraction"); + assertSameDate("2018-12-31T12:12:12.123", "strict_date_hour_minute_second_millis"); + assertSameDate("2018-12-31T12:12:12.1", "strict_date_hour_minute_second_millis"); + assertSameDate("2018-12-31T12:12:12.1", "strict_date_hour_minute_second_fraction"); + assertParseException("2018-12-31T12:12:12", "strict_date_hour_minute_second_millis"); + assertParseException("2018-12-31T12:12:12", "strict_date_hour_minute_second_fraction"); + assertSameDate("2018-12-31", "strict_date_optional_time"); + assertParseException("2018-12-1", "strict_date_optional_time"); + assertParseException("2018-1-31", "strict_date_optional_time"); + assertSameDate("2018-12-31T10:15:30", "strict_date_optional_time"); + 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("2018-12-31T10:15:30.123Z", "strict_date_time"); + assertSameDate("2018-12-31T10:15:30.11Z", "strict_date_time"); + assertParseException("2018-12-31T10:15:3.123Z", "strict_date_time"); + assertParseException("2018-12-31T10:5:30.123Z", "strict_date_time"); + assertParseException("2018-12-31T1:15:30.123Z", "strict_date_time"); + assertSameDate("2018-12-31T10:15:30Z", "strict_date_time_no_millis"); + assertParseException("2018-12-31T10:5:30Z", "strict_date_time_no_millis"); + assertParseException("2018-12-31T10:15:3Z", "strict_date_time_no_millis"); + assertParseException("2018-12-31T1:15:30Z", "strict_date_time_no_millis"); + assertSameDate("12", "strict_hour"); + assertSameDate("01", "strict_hour"); + assertParseException("1", "strict_hour"); + assertSameDate("12:12", "strict_hour_minute"); + assertSameDate("12:01", "strict_hour_minute"); + assertParseException("12:1", "strict_hour_minute"); + assertSameDate("12:12:12", "strict_hour_minute_second"); + assertSameDate("12:12:01", "strict_hour_minute_second"); + assertParseException("12:12:1", "strict_hour_minute_second"); + assertSameDate("12:12:12.123", "strict_hour_minute_second_fraction"); + assertSameDate("12:12:12.1", "strict_hour_minute_second_fraction"); + assertParseException("12:12:12", "strict_hour_minute_second_fraction"); + assertSameDate("12:12:12.123", "strict_hour_minute_second_millis"); + assertSameDate("12:12:12.1", "strict_hour_minute_second_millis"); + assertParseException("12:12:12", "strict_hour_minute_second_millis"); + assertSameDate("2018-128", "strict_ordinal_date"); + assertParseException("2018-1", "strict_ordinal_date"); + + assertSameDate("2018-128T10:15:30.123Z", "strict_ordinal_date_time"); + assertParseException("2018-1T10:15:30.123Z", "strict_ordinal_date_time"); + + assertSameDate("2018-128T10:15:30Z", "strict_ordinal_date_time_no_millis"); + assertParseException("2018-1T10:15:30Z", "strict_ordinal_date_time_no_millis"); + + assertSameDate("10:15:30.123Z", "strict_time"); + assertParseException("1:15:30.123Z", "strict_time"); + assertParseException("10:1:30.123Z", "strict_time"); + assertParseException("10:15:3.123Z", "strict_time"); + assertParseException("10:15:3.1", "strict_time"); + assertParseException("10:15:3Z", "strict_time"); + + assertSameDate("10:15:30Z", "strict_time_no_millis"); + assertSameDate("01:15:30Z", "strict_time_no_millis"); + assertParseException("1:15:30Z", "strict_time_no_millis"); + assertParseException("10:5:30Z", "strict_time_no_millis"); + assertParseException("10:15:3Z", "strict_time_no_millis"); + assertParseException("10:15:3", "strict_time_no_millis"); + + assertSameDate("T10:15:30.123Z", "strict_t_time"); + assertParseException("T1:15:30.123Z", "strict_t_time"); + assertParseException("T10:1:30.123Z", "strict_t_time"); + assertParseException("T10:15:3.123Z", "strict_t_time"); + assertParseException("T10:15:3.1", "strict_t_time"); + assertParseException("T10:15:3Z", "strict_t_time"); + + assertSameDate("T10:15:30Z", "strict_t_time_no_millis"); + assertParseException("T1:15:30Z", "strict_t_time_no_millis"); + assertParseException("T10:1:30Z", "strict_t_time_no_millis"); + assertParseException("T10:15:3Z", "strict_t_time_no_millis"); + assertParseException("T10:15:3", "strict_t_time_no_millis"); + + assertSameDate("2012-W48-6", "strict_week_date"); + assertSameDate("2012-W01-6", "strict_week_date"); + assertParseException("2012-W1-6", "strict_week_date"); + assertParseException("2012-W1-8", "strict_week_date"); + + assertSameDate("2012-W48-6", "strict_week_date"); + assertSameDate("2012-W01-6", "strict_week_date"); + assertParseException("2012-W1-6", "strict_week_date"); + // joda comes up with a different exception message here, so we have to adapt + assertJodaParseException("2012-W01-8", "strict_week_date", + "Cannot parse \"2012-W01-8\": Value 8 for dayOfWeek must be in the range [1,7]"); + assertJavaTimeParseException("2012-W01-8", "strict_week_date", "Text '2012-W01-8' could not be parsed"); + + assertSameDate("2012-W48-6T10:15:30.123Z", "strict_week_date_time"); + assertParseException("2012-W1-6T10:15:30.123Z", "strict_week_date_time"); + + assertSameDate("2012-W48-6T10:15:30Z", "strict_week_date_time_no_millis"); + assertParseException("2012-W1-6T10:15:30Z", "strict_week_date_time_no_millis"); + + assertSameDate("2012", "strict_year"); + assertParseException("1", "strict_year"); + assertSameDate("-2000", "strict_year"); + + assertSameDate("2012-12", "strict_year_month"); + assertParseException("1-1", "strict_year_month"); + + assertSameDate("2012-12-31", "strict_year_month_day"); + assertParseException("1-12-31", "strict_year_month_day"); + assertParseException("2012-1-31", "strict_year_month_day"); + assertParseException("2012-12-1", "strict_year_month_day"); + + assertSameDate("2018", "strict_weekyear"); + assertParseException("1", "strict_weekyear"); + + assertSameDate("2018", "strict_weekyear"); + assertSameDate("2017", "strict_weekyear"); + assertParseException("1", "strict_weekyear"); + + assertSameDate("2018-W29", "strict_weekyear_week"); + assertSameDate("2018-W01", "strict_weekyear_week"); + assertParseException("2018-W1", "strict_weekyear_week"); + + assertSameDate("2012-W31-5", "strict_weekyear_week_day"); + assertParseException("2012-W1-1", "strict_weekyear_week_day"); + } + + public void testSamePrinterOutput() { + int year = randomIntBetween(1970, 2030); + int month = randomIntBetween(1, 12); + int day = randomIntBetween(1, 28); + int hour = randomIntBetween(0, 23); + int minute = randomIntBetween(0, 59); + int second = randomIntBetween(0, 59); + + ZonedDateTime javaDate = ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC); + DateTime jodaDate = new DateTime(year, month, day, hour, minute, second, DateTimeZone.UTC); + + assertSamePrinterOutput("basicDate", javaDate, jodaDate); + assertSamePrinterOutput("basicDateTime", javaDate, jodaDate); + assertSamePrinterOutput("basicDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("basicOrdinalDate", javaDate, jodaDate); + assertSamePrinterOutput("basicOrdinalDateTime", javaDate, jodaDate); + assertSamePrinterOutput("basicOrdinalDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("basicTime", javaDate, jodaDate); + assertSamePrinterOutput("basicTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("basicTTime", javaDate, jodaDate); + assertSamePrinterOutput("basicTTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("basicWeekDate", javaDate, jodaDate); + assertSamePrinterOutput("basicWeekDateTime", javaDate, jodaDate); + assertSamePrinterOutput("basicWeekDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("date", javaDate, jodaDate); + assertSamePrinterOutput("dateHour", javaDate, jodaDate); + assertSamePrinterOutput("dateHourMinute", javaDate, jodaDate); + assertSamePrinterOutput("dateHourMinuteSecond", javaDate, jodaDate); + assertSamePrinterOutput("dateHourMinuteSecondFraction", javaDate, jodaDate); + assertSamePrinterOutput("dateHourMinuteSecondMillis", javaDate, jodaDate); + assertSamePrinterOutput("dateOptionalTime", javaDate, jodaDate); + assertSamePrinterOutput("dateTime", javaDate, jodaDate); + assertSamePrinterOutput("dateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("hour", javaDate, jodaDate); + assertSamePrinterOutput("hourMinute", javaDate, jodaDate); + assertSamePrinterOutput("hourMinuteSecond", javaDate, jodaDate); + assertSamePrinterOutput("hourMinuteSecondFraction", javaDate, jodaDate); + assertSamePrinterOutput("hourMinuteSecondMillis", javaDate, jodaDate); + assertSamePrinterOutput("ordinalDate", javaDate, jodaDate); + assertSamePrinterOutput("ordinalDateTime", javaDate, jodaDate); + assertSamePrinterOutput("ordinalDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("time", javaDate, jodaDate); + assertSamePrinterOutput("timeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("tTime", javaDate, jodaDate); + assertSamePrinterOutput("tTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("weekDate", javaDate, jodaDate); + assertSamePrinterOutput("weekDateTime", javaDate, jodaDate); + assertSamePrinterOutput("weekDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("weekyear", javaDate, jodaDate); + assertSamePrinterOutput("weekyearWeek", javaDate, jodaDate); + assertSamePrinterOutput("weekyearWeekDay", javaDate, jodaDate); + assertSamePrinterOutput("year", javaDate, jodaDate); + assertSamePrinterOutput("yearMonth", javaDate, jodaDate); + assertSamePrinterOutput("yearMonthDay", javaDate, jodaDate); + assertSamePrinterOutput("epoch_second", javaDate, jodaDate); + assertSamePrinterOutput("epoch_millis", javaDate, jodaDate); + assertSamePrinterOutput("strictBasicWeekDate", javaDate, jodaDate); + assertSamePrinterOutput("strictBasicWeekDateTime", javaDate, jodaDate); + assertSamePrinterOutput("strictBasicWeekDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictDate", javaDate, jodaDate); + assertSamePrinterOutput("strictDateHour", javaDate, jodaDate); + assertSamePrinterOutput("strictDateHourMinute", javaDate, jodaDate); + assertSamePrinterOutput("strictDateHourMinuteSecond", javaDate, jodaDate); + assertSamePrinterOutput("strictDateHourMinuteSecondFraction", javaDate, jodaDate); + assertSamePrinterOutput("strictDateHourMinuteSecondMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictDateOptionalTime", javaDate, jodaDate); + assertSamePrinterOutput("strictDateTime", javaDate, jodaDate); + assertSamePrinterOutput("strictDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictHour", javaDate, jodaDate); + assertSamePrinterOutput("strictHourMinute", javaDate, jodaDate); + assertSamePrinterOutput("strictHourMinuteSecond", javaDate, jodaDate); + assertSamePrinterOutput("strictHourMinuteSecondFraction", javaDate, jodaDate); + assertSamePrinterOutput("strictHourMinuteSecondMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictOrdinalDate", javaDate, jodaDate); + assertSamePrinterOutput("strictOrdinalDateTime", javaDate, jodaDate); + assertSamePrinterOutput("strictOrdinalDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictTime", javaDate, jodaDate); + assertSamePrinterOutput("strictTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictTTime", javaDate, jodaDate); + assertSamePrinterOutput("strictTTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekDate", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekDateTime", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekDateTimeNoMillis", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekyear", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekyearWeek", javaDate, jodaDate); + assertSamePrinterOutput("strictWeekyearWeekDay", javaDate, jodaDate); + assertSamePrinterOutput("strictYear", javaDate, jodaDate); + assertSamePrinterOutput("strictYearMonth", javaDate, jodaDate); + assertSamePrinterOutput("strictYearMonthDay", javaDate, jodaDate); + } + + public void testSeveralTimeFormats() { + assertSameDate("2018-12-12", "year_month_day||ordinal_date"); + assertSameDate("2018-128", "year_month_day||ordinal_date"); + } + + private void assertSamePrinterOutput(String format, ZonedDateTime javaDate, DateTime jodaDate) { + assertThat(jodaDate.getMillis(), is(javaDate.toEpochSecond() * 1000)); + String javaTimeOut = DateFormatters.forPattern("dateOptionalTime").format(javaDate); + String jodaTimeOut = Joda.forPattern("dateOptionalTime").printer().print(jodaDate); + assertThat(javaTimeOut, is(jodaTimeOut)); + } + + private void assertSameDate(String input, String format) { + FormatDateTimeFormatter jodaFormatter = Joda.forPattern(format); + DateTime jodaDateTime = jodaFormatter.parser().parseDateTime(input); + + CompoundDateTimeFormatter javaTimeFormatter = DateFormatters.forPattern(format); + TemporalAccessor javaTimeAccessor = javaTimeFormatter.parse(input); + ZonedDateTime zonedDateTime = DateFormatters.toZonedDateTime(javaTimeAccessor); + + String msg = String.format(Locale.ROOT, "Input [%s] Format [%s] Joda [%s], Java [%s]", input, format, jodaDateTime, + DateTimeFormatter.ISO_INSTANT.format(zonedDateTime.toInstant())); + + assertThat(msg, jodaDateTime.getMillis(), is(zonedDateTime.toInstant().toEpochMilli())); + } + + private void assertParseException(String input, String format) { + assertJodaParseException(input, format, "Invalid format: \"" + input); + assertJavaTimeParseException(input, format, "Text '" + input + "' could not be parsed"); + } + + private void assertJodaParseException(String input, String format, String expectedMessage) { + FormatDateTimeFormatter jodaFormatter = Joda.forPattern(format); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> jodaFormatter.parser().parseDateTime(input)); + assertThat(e.getMessage(), containsString(expectedMessage)); + } + + private void assertJavaTimeParseException(String input, String format, String expectedMessage) { + CompoundDateTimeFormatter javaTimeFormatter = DateFormatters.forPattern(format); + DateTimeParseException dateTimeParseException = expectThrows(DateTimeParseException.class, () -> javaTimeFormatter.parse(input)); + assertThat(dateTimeParseException.getMessage(), startsWith(expectedMessage)); + } +} diff --git a/server/src/test/java/org/elasticsearch/explain/ExplainActionIT.java b/server/src/test/java/org/elasticsearch/explain/ExplainActionIT.java index 6d8e1a41c5b94..229cb99fbfbda 100644 --- a/server/src/test/java/org/elasticsearch/explain/ExplainActionIT.java +++ b/server/src/test/java/org/elasticsearch/explain/ExplainActionIT.java @@ -28,12 +28,12 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.test.ESIntegTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.ISODateTimeFormat; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -249,8 +249,9 @@ public void testExplainWithFilteredAliasFetchSource() throws Exception { public void testExplainDateRangeInQueryString() { createIndex("test"); - String aMonthAgo = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).minusMonths(1)); - String aMonthFromNow = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).plusMonths(1)); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + String aMonthAgo = DateTimeFormatter.ISO_LOCAL_DATE.format(now.minusMonths(1)); + String aMonthFromNow = DateTimeFormatter.ISO_LOCAL_DATE.format(now.plusMonths(1)); client().prepareIndex("test", "type", "1").setSource("past", aMonthAgo, "future", aMonthFromNow).get(); diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesRequestCacheIT.java b/server/src/test/java/org/elasticsearch/indices/IndicesRequestCacheIT.java index 1884361a47f69..c1778ad86266b 100644 --- a/server/src/test/java/org/elasticsearch/indices/IndicesRequestCacheIT.java +++ b/server/src/test/java/org/elasticsearch/indices/IndicesRequestCacheIT.java @@ -30,11 +30,11 @@ import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.elasticsearch.test.ESIntegTestCase; -import org.joda.time.DateTime; import org.joda.time.DateTimeZone; -import org.joda.time.chrono.ISOChronology; -import org.joda.time.format.DateTimeFormat; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram; @@ -254,7 +254,7 @@ public void testQueryRewriteDatesWithNow() throws Exception { .setSettings(settings).get()); assertAcked(client.admin().indices().prepareCreate("index-3").addMapping("type", "d", "type=date") .setSettings(settings).get()); - DateTime now = new DateTime(ISOChronology.getInstanceUTC()); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); indexRandom(true, client.prepareIndex("index-1", "type", "1").setSource("d", now), client.prepareIndex("index-1", "type", "2").setSource("d", now.minusDays(1)), client.prepareIndex("index-1", "type", "3").setSource("d", now.minusDays(2)), @@ -454,9 +454,9 @@ public void testCacheWithFilteredAlias() { .setSettings(settings) .addAlias(new Alias("last_week").filter(QueryBuilders.rangeQuery("created_at").gte("now-7d/d"))) .get()); - DateTime now = new DateTime(DateTimeZone.UTC); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); client.prepareIndex("index", "type", "1").setRouting("1").setSource("created_at", - DateTimeFormat.forPattern("YYYY-MM-dd").print(now)).get(); + DateTimeFormatter.ISO_LOCAL_DATE.format(now)).get(); refresh(); assertThat(client.admin().indices().prepareStats("index").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java index 015664109cdfe..21c582c3af20a 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java @@ -22,10 +22,10 @@ import com.carrotsearch.hppc.LongHashSet; import com.carrotsearch.hppc.LongSet; import com.carrotsearch.randomizedtesting.generators.RandomStrings; - import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; @@ -40,10 +40,9 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.test.ESIntegTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -123,8 +122,9 @@ public void setupSuiteScopeCluster() throws Exception { longTerm = randomInt(cardinality * 2); } while (!longTerms.add(longTerm)); double doubleTerm = longTerm * Math.PI; - String dateTerm = DateTimeFormat.forPattern("yyyy-MM-dd") - .print(new DateTime(2014, 1, ((int) longTerm % 20) + 1, 0, 0, DateTimeZone.UTC)); + + ZonedDateTime time = ZonedDateTime.of(2014, 1, ((int) longTerm % 20) + 1, 0, 0, 0, 0, ZoneOffset.UTC); + String dateTerm = DateFormatters.forPattern("yyyy-MM-dd").format(time); final int frequency = randomBoolean() ? 1 : randomIntBetween(2, 20); for (int j = 0; j < frequency; ++j) { indexRequests.add(client().prepareIndex("idx", "type").setSource(jsonBuilder() diff --git a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 35c5a19cc2e8c..e5af22cd2ae65 100644 --- a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -63,10 +63,10 @@ import org.elasticsearch.test.MockKeywordPlugin; import org.hamcrest.Matcher; import org.hamcrest.Matchers; -import org.joda.time.DateTime; -import org.joda.time.chrono.ISOChronology; import java.io.IOException; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -2865,7 +2865,7 @@ public void testHighlightQueryRewriteDatesWithNow() throws Exception { "field", "type=text,store=true,term_vector=with_positions_offsets") .setSettings(Settings.builder().put("index.number_of_replicas", 0).put("index.number_of_shards", 2)) .get()); - DateTime now = new DateTime(ISOChronology.getInstanceUTC()); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); indexRandom(true, client().prepareIndex("index-1", "type", "1").setSource("d", now, "field", "hello world"), client().prepareIndex("index-1", "type", "2").setSource("d", now.minusDays(1), "field", "hello"), client().prepareIndex("index-1", "type", "3").setSource("d", now.minusDays(2), "field", "world")); diff --git a/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java index ce444be6772eb..f5d91bb68422e 100644 --- a/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java +++ b/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java @@ -28,8 +28,8 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.document.DocumentField; -import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.support.XContentMapValues; @@ -46,10 +46,10 @@ import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; +import org.joda.time.base.BaseDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -565,6 +565,7 @@ public void testStoredFieldsWithoutSource() throws Exception { client().admin().indices().preparePutMapping().setType("type1").setSource(mapping, XContentType.JSON).execute().actionGet(); + ZonedDateTime date = ZonedDateTime.of(2012, 3, 22, 0, 0, 0, 0, ZoneOffset.UTC); client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject() .field("byte_field", (byte) 1) .field("short_field", (short) 2) @@ -572,7 +573,7 @@ public void testStoredFieldsWithoutSource() throws Exception { .field("long_field", 4L) .field("float_field", 5.0f) .field("double_field", 6.0d) - .field("date_field", Joda.forPattern("dateOptionalTime").printer().print(new DateTime(2012, 3, 22, 0, 0, DateTimeZone.UTC))) + .field("date_field", DateFormatters.forPattern("dateOptionalTime").format(date)) .field("boolean_field", true) .field("binary_field", Base64.getEncoder().encodeToString("testing text".getBytes("UTF-8"))) .endObject()).execute().actionGet(); @@ -597,7 +598,6 @@ public void testStoredFieldsWithoutSource() throws Exception { assertThat(fields, equalTo(newHashSet("byte_field", "short_field", "integer_field", "long_field", "float_field", "double_field", "date_field", "boolean_field", "binary_field"))); - SearchHit searchHit = searchResponse.getHits().getAt(0); assertThat(searchHit.getFields().get("byte_field").getValue().toString(), equalTo("1")); assertThat(searchHit.getFields().get("short_field").getValue().toString(), equalTo("2")); @@ -605,7 +605,7 @@ public void testStoredFieldsWithoutSource() throws Exception { assertThat(searchHit.getFields().get("long_field").getValue(), equalTo((Object) 4L)); assertThat(searchHit.getFields().get("float_field").getValue(), equalTo((Object) 5.0f)); assertThat(searchHit.getFields().get("double_field").getValue(), equalTo((Object) 6.0d)); - String dateTime = Joda.forPattern("dateOptionalTime").printer().print(new DateTime(2012, 3, 22, 0, 0, DateTimeZone.UTC)); + String dateTime = DateFormatters.forPattern("dateOptionalTime").format(date); assertThat(searchHit.getFields().get("date_field").getValue(), equalTo((Object) dateTime)); assertThat(searchHit.getFields().get("boolean_field").getValue(), equalTo((Object) Boolean.TRUE)); assertThat(searchHit.getFields().get("binary_field").getValue(), equalTo(new BytesArray("testing text" .getBytes("UTF8")))); @@ -775,7 +775,7 @@ public void testDocValueFields() throws Exception { client().admin().indices().preparePutMapping().setType("type1").setSource(mapping, XContentType.JSON).execute().actionGet(); - ReadableDateTime date = new DateTime(2012, 3, 22, 0, 0, DateTimeZone.UTC); + ZonedDateTime date = ZonedDateTime.of(2012, 3, 22, 0, 0, 0, 0, ZoneOffset.UTC); client().prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject() .field("text_field", "foo") .field("keyword_field", "foo") @@ -785,7 +785,7 @@ public void testDocValueFields() throws Exception { .field("long_field", 4L) .field("float_field", 5.0f) .field("double_field", 6.0d) - .field("date_field", Joda.forPattern("dateOptionalTime").printer().print(date)) + .field("date_field", DateFormatters.forPattern("dateOptionalTime").format(date)) .field("boolean_field", true) .field("binary_field", new byte[] {42, 100}) .field("ip_field", "::1") @@ -821,7 +821,8 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("long_field").getValue(), equalTo((Object) 4L)); assertThat(searchResponse.getHits().getAt(0).getFields().get("float_field").getValue(), equalTo((Object) 5.0)); assertThat(searchResponse.getHits().getAt(0).getFields().get("double_field").getValue(), equalTo((Object) 6.0d)); - assertThat(searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(), equalTo(date)); + BaseDateTime dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(); + assertThat(dateField.getMillis(), equalTo(date.toInstant().toEpochMilli())); assertThat(searchResponse.getHits().getAt(0).getFields().get("boolean_field").getValue(), equalTo((Object) true)); assertThat(searchResponse.getHits().getAt(0).getFields().get("text_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); @@ -858,7 +859,7 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("float_field").getValue(), equalTo((Object) 5.0)); assertThat(searchResponse.getHits().getAt(0).getFields().get("double_field").getValue(), equalTo((Object) 6.0d)); assertThat(searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(), - equalTo(Joda.forPattern("dateOptionalTime").printer().print(date))); + equalTo(DateFormatters.forPattern("dateOptionalTime").format(date))); assertThat(searchResponse.getHits().getAt(0).getFields().get("boolean_field").getValue(), equalTo((Object) true)); assertThat(searchResponse.getHits().getAt(0).getFields().get("text_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); @@ -888,7 +889,7 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("float_field").getValue(), equalTo("5.0")); assertThat(searchResponse.getHits().getAt(0).getFields().get("double_field").getValue(), equalTo("6.0")); assertThat(searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(), - equalTo(Joda.forPattern("epoch_millis").printer().print(date))); + equalTo(DateFormatters.forPattern("epoch_millis").format(date))); } public void testScriptFields() throws Exception { diff --git a/server/src/test/java/org/elasticsearch/search/functionscore/DecayFunctionScoreIT.java b/server/src/test/java/org/elasticsearch/search/functionscore/DecayFunctionScoreIT.java index d6acdf11cb2ab..a21893db3920f 100644 --- a/server/src/test/java/org/elasticsearch/search/functionscore/DecayFunctionScoreIT.java +++ b/server/src/test/java/org/elasticsearch/search/functionscore/DecayFunctionScoreIT.java @@ -43,9 +43,9 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.VersionUtils; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -562,27 +562,27 @@ public void testValueMissingLin() throws Exception { } public void testDateWithoutOrigin() throws Exception { - DateTime dt = new DateTime(DateTimeZone.UTC); + ZonedDateTime dt = ZonedDateTime.now(ZoneOffset.UTC); assertAcked(prepareCreate("test").addMapping( "type1", jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "text") .endObject().startObject("num1").field("type", "date").endObject().endObject().endObject().endObject())); - DateTime docDate = dt.minusDays(1); - String docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + ZonedDateTime docDate = dt.minusDays(1); + String docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthValue()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth()); client().index( indexRequest("test").type("type1").id("1") .source(jsonBuilder().startObject().field("test", "value").field("num1", docDateString).endObject())).actionGet(); docDate = dt.minusDays(2); - docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthValue()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth()); client().index( indexRequest("test").type("type1").id("2") .source(jsonBuilder().startObject().field("test", "value").field("num1", docDateString).endObject())).actionGet(); docDate = dt.minusDays(3); - docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthValue()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth()); client().index( indexRequest("test").type("type1").id("3") diff --git a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index c8a339315f542..367b97eb4da08 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -53,6 +53,9 @@ import org.joda.time.format.ISODateTimeFormat; import java.io.IOException; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.Collections; import java.util.Random; @@ -481,8 +484,9 @@ public void testDateRangeInQueryString() { "type", "past", "type=date", "future", "type=date" )); - String aMonthAgo = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).minusMonths(1)); - String aMonthFromNow = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).plusMonths(1)); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + String aMonthAgo = DateTimeFormatter.ISO_LOCAL_DATE.format(now.minusMonths(1)); + String aMonthFromNow = DateTimeFormatter.ISO_LOCAL_DATE.format(now.plusMonths(1)); client().prepareIndex("test", "type", "1").setSource("past", aMonthAgo, "future", aMonthFromNow).get(); refresh(); diff --git a/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java b/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java index 36902b55f5688..0f86402d844bb 100644 --- a/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java +++ b/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java @@ -35,12 +35,13 @@ import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; import org.hamcrest.Matcher; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.ISODateTimeFormat; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; @@ -124,8 +125,9 @@ public void testExplainDateRangeInQueryString() { .put(indexSettings()) .put("index.number_of_shards", 1))); - String aMonthAgo = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).minusMonths(1)); - String aMonthFromNow = ISODateTimeFormat.yearMonthDay().print(new DateTime(DateTimeZone.UTC).plusMonths(1)); + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + String aMonthAgo = DateTimeFormatter.ISO_LOCAL_DATE.format(now.plus(1, ChronoUnit.MONTHS)); + String aMonthFromNow = DateTimeFormatter.ISO_LOCAL_DATE.format(now.minus(1, ChronoUnit.MONTHS)); client().prepareIndex("test", "type", "1").setSource("past", aMonthAgo, "future", aMonthFromNow).get(); @@ -137,10 +139,10 @@ public void testExplainDateRangeInQueryString() { assertNoFailures(response); assertThat(response.getQueryExplanation().size(), equalTo(1)); assertThat(response.getQueryExplanation().get(0).getError(), nullValue()); - DateTime twoMonthsAgo = new DateTime(DateTimeZone.UTC).minusMonths(2).withTimeAtStartOfDay(); - DateTime now = new DateTime(DateTimeZone.UTC).plusDays(1).withTimeAtStartOfDay().minusMillis(1); - assertThat(response.getQueryExplanation().get(0).getExplanation(), - equalTo("past:[" + twoMonthsAgo.getMillis() + " TO " + now.getMillis() + "]")); + + long twoMonthsAgo = now.minus(2, ChronoUnit.MONTHS).truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1000; + long rangeEnd = (now.plus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1000) - 1; + assertThat(response.getQueryExplanation().get(0).getExplanation(), equalTo("past:[" + twoMonthsAgo + " TO " + rangeEnd + "]")); assertThat(response.isValid(), equalTo(true)); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index edc762632b428..b1843429d4b70 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -124,6 +124,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -176,6 +177,7 @@ public abstract class ESTestCase extends LuceneTestCase { private static final List JODA_TIMEZONE_IDS; private static final List JAVA_TIMEZONE_IDS; + private static final List JAVA_ZONE_IDS; private static final AtomicInteger portGenerator = new AtomicInteger(); @@ -203,6 +205,10 @@ public static void resetPortCounter() { List javaTZIds = Arrays.asList(TimeZone.getAvailableIDs()); Collections.sort(javaTZIds); JAVA_TIMEZONE_IDS = Collections.unmodifiableList(javaTZIds); + + List javaZoneIds = new ArrayList<>(ZoneId.getAvailableZoneIds()); + Collections.sort(javaZoneIds); + JAVA_ZONE_IDS = Collections.unmodifiableList(javaZoneIds); } protected final Logger logger = Loggers.getLogger(getClass()); @@ -701,12 +707,19 @@ public static DateTimeZone randomDateTimeZone() { } /** - * generate a random TimeZone from the ones available in java.time + * generate a random TimeZone from the ones available in java.util */ public static TimeZone randomTimeZone() { return TimeZone.getTimeZone(randomFrom(JAVA_TIMEZONE_IDS)); } + /** + * generate a random TimeZone from the ones available in java.time + */ + public static ZoneId randomZone() { + return ZoneId.of(randomFrom(JAVA_ZONE_IDS)); + } + /** * helper to randomly perform on consumer with value */