diff --git a/CHANGELOG.md b/CHANGELOG.md index e45eb3870b522..ed5b8404886a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,6 +136,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Removed - Remove deprecated classes for Rounding ([#10956](https://github.com/opensearch-project/OpenSearch/issues/10956)) +- [Remove] Remaining Joda and Joda Dependency ([#10949](https://github.com/opensearch-project/OpenSearch/pull/10949)) ### Fixed - Fix failure in dissect ingest processor parsing empty brackets ([#9225](https://github.com/opensearch-project/OpenSearch/pull/9255)) diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/time/DateFormatterBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/time/DateFormatterBenchmark.java index 8f266f8357100..9fa124b1f3c9f 100644 --- a/benchmarks/src/main/java/org/opensearch/benchmark/time/DateFormatterBenchmark.java +++ b/benchmarks/src/main/java/org/opensearch/benchmark/time/DateFormatterBenchmark.java @@ -31,7 +31,6 @@ package org.opensearch.benchmark.time; -import org.opensearch.common.joda.Joda; import org.opensearch.common.time.DateFormatter; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -56,15 +55,9 @@ public class DateFormatterBenchmark { private final DateFormatter javaFormatter = DateFormatter.forPattern("8year_month_day||ordinal_date||epoch_millis"); - private final DateFormatter jodaFormatter = Joda.forPattern("year_month_day||ordinal_date||epoch_millis"); @Benchmark public TemporalAccessor parseJavaDate() { return javaFormatter.parse("1234567890"); } - - @Benchmark - public TemporalAccessor parseJodaDate() { - return jodaFormatter.parse("1234567890"); - } } diff --git a/buildSrc/src/main/resources/forbidden/opensearch-server-signatures.txt b/buildSrc/src/main/resources/forbidden/opensearch-server-signatures.txt index 27fba8069125d..97e1cd35c403e 100644 --- a/buildSrc/src/main/resources/forbidden/opensearch-server-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/opensearch-server-signatures.txt @@ -72,15 +72,6 @@ java.util.concurrent.Future#cancel(boolean) org.opensearch.common.io.PathUtils#get(java.lang.String, java.lang.String[]) org.opensearch.common.io.PathUtils#get(java.net.URI) -@defaultMessage Constructing a DateTime without a time zone is dangerous -org.joda.time.DateTime#() -org.joda.time.DateTime#(long) -org.joda.time.DateTime#(int, int, int, int, int) -org.joda.time.DateTime#(int, int, int, int, int, int) -org.joda.time.DateTime#(int, int, int, int, int, int, int) -org.joda.time.DateTime#now() -org.joda.time.DateTimeZone#getDefault() - @defaultMessage Local times may be ambiguous or nonexistent in a specific time zones. Use ZoneRules#getValidOffsets() instead. java.time.LocalDateTime#atZone(java.time.ZoneId) java.time.ZonedDateTime#of(int, int, int, int, int, int, int, java.time.ZoneId) diff --git a/buildSrc/version.properties b/buildSrc/version.properties index f19437979c852..e965daced5a2c 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -27,7 +27,6 @@ jakarta_annotation = 1.3.5 jna = 5.13.0 netty = 4.1.100.Final -joda = 2.12.2 # project reactor reactor_netty = 1.1.12 diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java index da9f790215669..dc87b303a3941 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java @@ -74,15 +74,17 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.fetch.subphase.FetchSourceContext; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; import java.io.IOException; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -1022,7 +1024,8 @@ private void validateBulkResponses(int nbItems, boolean[] errors, BulkResponse b public void testUrlEncode() throws IOException { String indexPattern = ""; String expectedIndex = "logstash-" - + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(DateTimeZone.UTC).monthOfYear().roundFloorCopy()); + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.now(ZoneOffset.UTC).withDayOfMonth(1).with(LocalTime.MIN)); { IndexRequest indexRequest = new IndexRequest(indexPattern).id("id#1"); indexRequest.source("field", "value"); diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObject.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObject.java index 502e29a5f2b58..34ebfd8e8e6a0 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObject.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObject.java @@ -33,9 +33,12 @@ package org.opensearch.script.expression; import org.apache.lucene.search.DoubleValuesSource; +import org.opensearch.common.time.DateFormatters; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.search.MultiValueMode; -import org.joda.time.ReadableDateTime; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; /** * Expressions API for date objects (.date) @@ -87,41 +90,51 @@ private DateObject() {} static DoubleValuesSource getVariable(IndexFieldData fieldData, String fieldName, String variable) { switch (variable) { case CENTURY_OF_ERA_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getCenturyOfEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.YEAR_OF_ERA) / 100); case DAY_OF_MONTH_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getDayOfMonth); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getDayOfMonth); case DAY_OF_WEEK_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getDayOfWeek); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.getDayOfWeek().getValue()); case DAY_OF_YEAR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getDayOfYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getDayOfYear); case ERA_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.ERA)); case HOUR_OF_DAY_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getHourOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getHour); case MILLIS_OF_DAY_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getMillisOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.MILLI_OF_DAY)); case MILLIS_OF_SECOND_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getMillisOfSecond); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.MILLI_OF_SECOND)); case MINUTE_OF_DAY_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getMinuteOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.MINUTE_OF_DAY)); case MINUTE_OF_HOUR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getMinuteOfHour); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getMinute); case MONTH_OF_YEAR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getMonthOfYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getMonthValue); case SECOND_OF_DAY_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getSecondOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.SECOND_OF_DAY)); case SECOND_OF_MINUTE_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getSecondOfMinute); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getSecond); case WEEK_OF_WEEK_YEAR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getWeekOfWeekyear); + return new DateObjectValueSource( + fieldData, + MultiValueMode.MIN, + variable, + zdt -> zdt.get(DateFormatters.WEEK_FIELDS_ROOT.weekOfWeekBasedYear()) + ); case WEEK_YEAR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getWeekyear); + return new DateObjectValueSource( + fieldData, + MultiValueMode.MIN, + variable, + zdt -> zdt.get(DateFormatters.WEEK_FIELDS_ROOT.weekBasedYear()) + ); case YEAR_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ZonedDateTime::getYear); case YEAR_OF_CENTURY_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getYearOfCentury); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.YEAR_OF_ERA) % 100); case YEAR_OF_ERA_VARIABLE: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, ReadableDateTime::getYearOfEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, variable, zdt -> zdt.get(ChronoField.YEAR_OF_ERA)); default: throw new IllegalArgumentException( "Member variable [" + variable + "] does not exist for date object on field [" + fieldName + "]." @@ -132,41 +145,51 @@ static DoubleValuesSource getVariable(IndexFieldData fieldData, String fieldN static DoubleValuesSource getMethod(IndexFieldData fieldData, String fieldName, String method) { switch (method) { case GETCENTURY_OF_ERA_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getCenturyOfEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.YEAR_OF_ERA) / 100); case GETDAY_OF_MONTH_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getDayOfMonth); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getDayOfMonth); case GETDAY_OF_WEEK_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getDayOfWeek); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.getDayOfWeek().getValue()); case GETDAY_OF_YEAR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getDayOfYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getDayOfYear); case GETERA_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.ERA)); case GETHOUR_OF_DAY_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getHourOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getHour); case GETMILLIS_OF_DAY_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getMillisOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.MILLI_OF_DAY)); case GETMILLIS_OF_SECOND_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getMillisOfSecond); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.MILLI_OF_SECOND)); case GETMINUTE_OF_DAY_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getMinuteOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.MINUTE_OF_DAY)); case GETMINUTE_OF_HOUR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getMinuteOfHour); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getMinute); case GETMONTH_OF_YEAR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getMonthOfYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getMonthValue); case GETSECOND_OF_DAY_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getSecondOfDay); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.SECOND_OF_DAY)); case GETSECOND_OF_MINUTE_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getSecondOfMinute); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getSecond); case GETWEEK_OF_WEEK_YEAR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getWeekOfWeekyear); + return new DateObjectValueSource( + fieldData, + MultiValueMode.MIN, + method, + zdt -> zdt.get(DateFormatters.WEEK_FIELDS_ROOT.weekOfWeekBasedYear()) + ); case GETWEEK_YEAR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getWeekyear); + return new DateObjectValueSource( + fieldData, + MultiValueMode.MIN, + method, + zdt -> zdt.get(DateFormatters.WEEK_FIELDS_ROOT.weekBasedYear()) + ); case GETYEAR_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getYear); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ZonedDateTime::getYear); case GETYEAR_OF_CENTURY_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getYearOfCentury); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.YEAR_OF_ERA) % 100); case GETYEAR_OF_ERA_METHOD: - return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, ReadableDateTime::getYearOfEra); + return new DateObjectValueSource(fieldData, MultiValueMode.MIN, method, zdt -> zdt.get(ChronoField.YEAR_OF_ERA)); default: throw new IllegalArgumentException( "Member method [" + method + "] does not exist for date object on field [" + fieldName + "]." diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java index 69d7b5ad7f769..d02d2617084bb 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java @@ -38,25 +38,25 @@ import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.NumericDoubleValues; import org.opensearch.search.MultiValueMode; -import org.joda.time.DateTimeZone; -import org.joda.time.MutableDateTime; -import org.joda.time.ReadableDateTime; import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Objects; import java.util.function.ToIntFunction; -/** Extracts a portion of a date field with joda time */ +/** Extracts a portion of a date field with java time */ class DateObjectValueSource extends FieldDataValueSource { final String methodName; - final ToIntFunction function; + final ToIntFunction function; DateObjectValueSource( IndexFieldData indexFieldData, MultiValueMode multiValueMode, String methodName, - ToIntFunction function + ToIntFunction function ) { super(indexFieldData, multiValueMode); @@ -69,13 +69,11 @@ class DateObjectValueSource extends FieldDataValueSource { @Override public DoubleValues getValues(LeafReaderContext leaf, DoubleValues scores) { LeafNumericFieldData leafData = (LeafNumericFieldData) fieldData.load(leaf); - MutableDateTime joda = new MutableDateTime(0, DateTimeZone.UTC); NumericDoubleValues docValues = multiValueMode.select(leafData.getDoubleValues()); return new DoubleValues() { @Override public double doubleValue() throws IOException { - joda.setMillis((long) docValues.doubleValue()); - return function.applyAsInt(joda); + return function.applyAsInt(ZonedDateTime.ofInstant(Instant.ofEpochMilli((long) docValues.doubleValue()), ZoneOffset.UTC)); } @Override diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index 560d12d14395d..3b087eb296f6a 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -79,7 +79,6 @@ dependencies { api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" api "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:${versions.jackson}" - api "joda-time:joda-time:${versions.joda}" api "org.slf4j:slf4j-api:${versions.slf4j}" // network stack @@ -129,6 +128,7 @@ test { // this is tested explicitly in separate test tasks exclude '**/RepositoryCredentialsTests.class' exclude '**/S3RepositoryThirdPartyTests.class' + systemProperty 'aws.region', 'us-east-2' } boolean useFixture = false diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_camel_case_dateformat.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_camel_case_dateformat.yml new file mode 100644 index 0000000000000..528f4afa5fdd1 --- /dev/null +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_camel_case_dateformat.yml @@ -0,0 +1,19 @@ +--- +"Verify that we can still use index with camel case date field": + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "camel_case_date_format"}}' + - '{"date_field": "2023-02-01T00:00+01:00"}' + + - do: + search: + rest_total_hits_as_int: true + index: camel_case_date_format + body: + query: + range: + date_field: + gte: "2023-01-01T00:00+01:00" + lte: "2023-03-01T00:00+01:00" diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml index 6427a45e19f58..ed4619e060d96 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml @@ -1,83 +1,3 @@ ---- -"Create index with joda style index that is incompatible with java.time. (6.0)": - - skip: - features: "allowed_warnings" - version: "6.8.1 -" - reason: change of warning message - - do: - allowed_warnings: - - "Use of 'Y' (year-of-era) will change to 'y' in the next major version of OpenSearch. Prefix your date format with '8' to use the new specifier." - indices.create: - index: joda_for_range - body: - settings: - index: - number_of_replicas: 2 - mappings: - "properties": - "time_frame": - "type": "date_range" - "format": "YYYY-MM-dd'T'HH:mmZZ" - - - do: - bulk: - refresh: true - body: - - '{"index": {"_index": "joda_for_range"}}' - - '{"time_frame": {"gte": "2019-01-01T00:00+01:00", "lte" : "2019-03-01T00:00+01:00"}}' - - - do: - search: - rest_total_hits_as_int: true - index: joda_for_range - body: - query: - range: - time_frame: - gte: "2019-02-01T00:00+01:00" - lte: "2019-02-01T00:00+01:00" - - match: { hits.total: 1 } - ---- -"Create index with joda style index that is incompatible with java.time (>6.1)": - - skip: - features: "allowed_warnings" - version: " - 6.8.0, 7.0.0 -" - reason: change of warning message, we skip 7 becase this format will be considered java - - do: - allowed_warnings: - - "'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.; 'Z' time zone offset/id fails when parsing 'Z' for Zulu timezone. Consider using 'X'. Prefix your date format with '8' to use the new specifier." - indices.create: - index: joda_for_range - body: - settings: - index: - number_of_replicas: 2 - mappings: - "properties": - "time_frame": - "type": "date_range" - "format": "YYYY-MM-dd'T'HH:mmZZ" - - - do: - bulk: - refresh: true - body: - - '{"index": {"_index": "joda_for_range"}}' - - '{"time_frame": {"gte": "2019-01-01T00:00+01:00", "lte" : "2019-03-01T00:00+01:00"}}' - - - do: - search: - rest_total_hits_as_int: true - index: joda_for_range - body: - query: - range: - time_frame: - gte: "2019-02-01T00:00+01:00" - lte: "2019-02-01T00:00+01:00" - - match: { hits.total: 1 } - --- "Create index with java style index in 6": - do: diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_camel_case_dateformat.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_camel_case_dateformat.yml new file mode 100644 index 0000000000000..66c0f400ebb5d --- /dev/null +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_camel_case_dateformat.yml @@ -0,0 +1,79 @@ +--- +"Create index with camel case date format (allowed with warning prior to 2.12.0)": + - skip: + version: "2.12.0 - " + reason: "at version 2.12.0, camel case date format is not allowed" + features: "warnings" + - do: + warnings: + - "Camel case format name strictDateOptionalTime is deprecated and will be removed in a future version. Use snake case name strict_date_optional_time instead." + indices.create: + index: camel_case_date_format + body: + settings: + index: + number_of_replicas: 2 + mappings: + "properties": + "date_field": + "type": "date" + "format": "strictDateOptionalTime" + + + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "camel_case_date_format"}}' + - '{"date_field": "2023-02-01T00:00+01:00"}' + + - do: + search: + rest_total_hits_as_int: true + index: camel_case_date_format + body: + query: + range: + date_field: + gte: "2023-01-01T00:00+01:00" + lte: "2023-03-01T00:00+01:00" + - match: { hits.total: 1 } + +--- +"Create index with camel case date format (when version is >= 2.12.0)": + - skip: + version: " - 2.11.99" + reason: "at version 2.12.0, camel case date format is not allowed" + features: "warnings" + - do: + indices.create: + index: camel_case_date_format + body: + settings: + index: + number_of_replicas: 2 + mappings: + "properties": + "date_field": + "type": "date" + "format": "strict_date_optional_time" + + + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "camel_case_date_format"}}' + - '{"date_field": "2023-02-01T00:00+01:00"}' + + - do: + search: + rest_total_hits_as_int: true + index: camel_case_date_format + body: + query: + range: + date_field: + gte: "2023-01-01T00:00+01:00" + lte: "2023-03-01T00:00+01:00" + - match: { hits.total: 1 } diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_camel_case_dateformat.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_camel_case_dateformat.yml new file mode 100644 index 0000000000000..aaf9110b4bb01 --- /dev/null +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_camel_case_dateformat.yml @@ -0,0 +1,20 @@ +--- +"Verify that we can use index with camel case date field in upgraded cluster": + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "camel_case_date_format"}}' + - '{"date_field": "2023-02-01T00:00+01:00"}' + + - do: + search: + rest_total_hits_as_int: true + index: camel_case_date_format + body: + query: + range: + date_field: + gte: "2023-01-01T00:00+01:00" + lte: "2023-03-01T00:00+01:00" + - match: { hits.total: 4 } diff --git a/server/build.gradle b/server/build.gradle index fa8a44ef6fc94..c975efd650192 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -130,9 +130,6 @@ dependencies { // utilities api project(":libs:opensearch-cli") - // time handling, remove with java 8 time - api "joda-time:joda-time:${versions.joda}" - // percentiles aggregation api 'com.tdunning:t-digest:3.3' // precentil ranks aggregation diff --git a/server/licenses/joda-time-2.12.2.jar.sha1 b/server/licenses/joda-time-2.12.2.jar.sha1 deleted file mode 100644 index 6e9b28eb35597..0000000000000 --- a/server/licenses/joda-time-2.12.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -78e18a7b4180e911dafba0a412adfa82c1e3d14b \ No newline at end of file diff --git a/server/licenses/joda-time-LICENSE.txt b/server/licenses/joda-time-LICENSE.txt deleted file mode 100644 index 75b52484ea471..0000000000000 --- a/server/licenses/joda-time-LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed 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. diff --git a/server/licenses/joda-time-NOTICE.txt b/server/licenses/joda-time-NOTICE.txt deleted file mode 100644 index dffbcf31cacf6..0000000000000 --- a/server/licenses/joda-time-NOTICE.txt +++ /dev/null @@ -1,5 +0,0 @@ -============================================================================= -= NOTICE file corresponding to section 4d of the Apache License Version 2.0 = -============================================================================= -This product includes software developed by -Joda.org (http://www.joda.org/). diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java index 2f348e5d6218d..3c4fc59cddbc0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java @@ -32,6 +32,8 @@ package org.opensearch.indices; +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestBuilder; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; @@ -41,11 +43,16 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.core.action.ActionResponse; import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.IndexNotFoundException; import org.opensearch.test.OpenSearchIntegTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; +import org.junit.Before; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchHits; @@ -54,15 +61,44 @@ import static org.hamcrest.Matchers.notNullValue; public class DateMathIndexExpressionsIntegrationIT extends OpenSearchIntegTestCase { + private ZonedDateTime now; + + @Before + public void setNow() { + now = ZonedDateTime.now(ZoneOffset.UTC); + } + + /** + * the internal cluster calls System.nanoTime() and System.currentTimeMillis() during evaluations of requests + * that need date-math index resolution. These are not mockable in these tests. As is, executing requests as-is + * in these test cases can potentially result in invalid responses when day-boundaries are hit mid test run. Instead + * of failing when index resolution with `now` is one day off, this method wraps calls with the assumption that + * the day did not change during the test run. + */ + public R dateSensitiveGet(ActionRequestBuilder builder) { + Runnable dayChangeAssumption = () -> assumeTrue( + "day changed between requests", + ZonedDateTime.now(ZoneOffset.UTC).getDayOfYear() == now.getDayOfYear() + ); + R response; + try { + response = builder.get(); + } catch (IndexNotFoundException e) { + // index resolver throws this if it does not find the exact index due to day changes + dayChangeAssumption.run(); + throw e; + } + dayChangeAssumption.run(); + return response; + } public void testIndexNameDateMathExpressions() { - DateTime now = new DateTime(DateTimeZone.UTC); - String index1 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now); - String index2 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(1)); - String index3 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(2)); + String index1 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now); + String index2 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(1)); + String index3 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(2)); createIndex(index1, index2, index3); - GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings(index1, index2, index3).get(); + GetSettingsResponse getSettingsResponse = dateSensitiveGet(client().admin().indices().prepareGetSettings(index1, index2, index3)); assertEquals(index1, getSettingsResponse.getSetting(index1, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); assertEquals(index2, getSettingsResponse.getSetting(index2, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); assertEquals(index3, getSettingsResponse.getSetting(index3, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); @@ -75,27 +111,25 @@ public void testIndexNameDateMathExpressions() { client().prepareIndex(dateMathExp3).setId("3").setSource("{}", MediaTypeRegistry.JSON).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3).get(); + SearchResponse searchResponse = dateSensitiveGet(client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3)); assertHitCount(searchResponse, 3); assertSearchHits(searchResponse, "1", "2", "3"); - GetResponse getResponse = client().prepareGet(dateMathExp1, "1").get(); + GetResponse getResponse = dateSensitiveGet(client().prepareGet(dateMathExp1, "1")); assertThat(getResponse.isExists(), is(true)); assertThat(getResponse.getId(), equalTo("1")); - getResponse = client().prepareGet(dateMathExp2, "2").get(); + getResponse = dateSensitiveGet(client().prepareGet(dateMathExp2, "2")); assertThat(getResponse.isExists(), is(true)); assertThat(getResponse.getId(), equalTo("2")); - getResponse = client().prepareGet(dateMathExp3, "3").get(); + getResponse = dateSensitiveGet(client().prepareGet(dateMathExp3, "3")); assertThat(getResponse.isExists(), is(true)); assertThat(getResponse.getId(), equalTo("3")); - MultiGetResponse mgetResponse = client().prepareMultiGet() - .add(dateMathExp1, "1") - .add(dateMathExp2, "2") - .add(dateMathExp3, "3") - .get(); + MultiGetResponse mgetResponse = dateSensitiveGet( + client().prepareMultiGet().add(dateMathExp1, "1").add(dateMathExp2, "2").add(dateMathExp3, "3") + ); assertThat(mgetResponse.getResponses()[0].getResponse().isExists(), is(true)); assertThat(mgetResponse.getResponses()[0].getResponse().getId(), equalTo("1")); assertThat(mgetResponse.getResponses()[1].getResponse().isExists(), is(true)); @@ -103,29 +137,30 @@ public void testIndexNameDateMathExpressions() { assertThat(mgetResponse.getResponses()[2].getResponse().isExists(), is(true)); assertThat(mgetResponse.getResponses()[2].getResponse().getId(), equalTo("3")); - IndicesStatsResponse indicesStatsResponse = client().admin().indices().prepareStats(dateMathExp1, dateMathExp2, dateMathExp3).get(); + IndicesStatsResponse indicesStatsResponse = dateSensitiveGet( + client().admin().indices().prepareStats(dateMathExp1, dateMathExp2, dateMathExp3) + ); assertThat(indicesStatsResponse.getIndex(index1), notNullValue()); assertThat(indicesStatsResponse.getIndex(index2), notNullValue()); assertThat(indicesStatsResponse.getIndex(index3), notNullValue()); - DeleteResponse deleteResponse = client().prepareDelete(dateMathExp1, "1").get(); + DeleteResponse deleteResponse = dateSensitiveGet(client().prepareDelete(dateMathExp1, "1")); assertEquals(DocWriteResponse.Result.DELETED, deleteResponse.getResult()); assertThat(deleteResponse.getId(), equalTo("1")); - deleteResponse = client().prepareDelete(dateMathExp2, "2").get(); + deleteResponse = dateSensitiveGet(client().prepareDelete(dateMathExp2, "2")); assertEquals(DocWriteResponse.Result.DELETED, deleteResponse.getResult()); assertThat(deleteResponse.getId(), equalTo("2")); - deleteResponse = client().prepareDelete(dateMathExp3, "3").get(); + deleteResponse = dateSensitiveGet(client().prepareDelete(dateMathExp3, "3")); assertEquals(DocWriteResponse.Result.DELETED, deleteResponse.getResult()); assertThat(deleteResponse.getId(), equalTo("3")); } - public void testAutoCreateIndexWithDateMathExpression() throws Exception { - DateTime now = new DateTime(DateTimeZone.UTC); - String index1 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now); - String index2 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(1)); - String index3 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(2)); + public void testAutoCreateIndexWithDateMathExpression() { + String index1 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now); + String index2 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(1)); + String index3 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(2)); String dateMathExp1 = "<.marvel-{now/d}>"; String dateMathExp2 = "<.marvel-{now/d-1d}>"; @@ -135,28 +170,29 @@ public void testAutoCreateIndexWithDateMathExpression() throws Exception { client().prepareIndex(dateMathExp3).setId("3").setSource("{}", MediaTypeRegistry.JSON).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3).get(); + SearchResponse searchResponse = dateSensitiveGet(client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3)); assertHitCount(searchResponse, 3); assertSearchHits(searchResponse, "1", "2", "3"); - IndicesStatsResponse indicesStatsResponse = client().admin().indices().prepareStats(dateMathExp1, dateMathExp2, dateMathExp3).get(); + IndicesStatsResponse indicesStatsResponse = dateSensitiveGet( + client().admin().indices().prepareStats(dateMathExp1, dateMathExp2, dateMathExp3) + ); assertThat(indicesStatsResponse.getIndex(index1), notNullValue()); assertThat(indicesStatsResponse.getIndex(index2), notNullValue()); assertThat(indicesStatsResponse.getIndex(index3), notNullValue()); } - public void testCreateIndexWithDateMathExpression() throws Exception { - DateTime now = new DateTime(DateTimeZone.UTC); - String index1 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now); - String index2 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(1)); - String index3 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(2)); + public void testCreateIndexWithDateMathExpression() { + String index1 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now); + String index2 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(1)); + String index3 = ".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.minusDays(2)); String dateMathExp1 = "<.marvel-{now/d}>"; String dateMathExp2 = "<.marvel-{now/d-1d}>"; String dateMathExp3 = "<.marvel-{now/d-2d}>"; createIndex(dateMathExp1, dateMathExp2, dateMathExp3); - GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings(index1, index2, index3).get(); + GetSettingsResponse getSettingsResponse = dateSensitiveGet(client().admin().indices().prepareGetSettings(index1, index2, index3)); assertEquals(dateMathExp1, getSettingsResponse.getSetting(index1, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); assertEquals(dateMathExp2, getSettingsResponse.getSetting(index2, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); assertEquals(dateMathExp3, getSettingsResponse.getSetting(index3, IndexMetadata.SETTING_INDEX_PROVIDED_NAME)); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java index 799bbf91a567d..f9717e3bfe937 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java @@ -64,14 +64,12 @@ import org.opensearch.search.sort.SortOrder; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; import java.math.BigInteger; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -80,6 +78,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -1191,7 +1190,7 @@ public void testDocValueFields() throws Exception { 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(DateFormatter.forPattern("dateOptionalTime").format(date)) + equalTo(DateFormatter.forPattern("date_optional_time").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")); @@ -1321,10 +1320,10 @@ public void testDocValueFieldsWithFieldAlias() throws Exception { assertAcked(prepareCreate("test").setMapping(mapping)); ensureGreen("test"); - DateTime date = new DateTime(1990, 12, 29, 0, 0, DateTimeZone.UTC); - org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + ZonedDateTime date = ZonedDateTime.of(1990, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.ROOT); - index("test", MapperService.SINGLE_MAPPING_NAME, "1", "text_field", "foo", "date_field", formatter.print(date)); + index("test", MapperService.SINGLE_MAPPING_NAME, "1", "text_field", "foo", "date_field", formatter.format(date)); refresh("test"); SearchRequestBuilder builder = client().prepareSearch() @@ -1382,10 +1381,10 @@ public void testWildcardDocValueFieldsWithFieldAlias() throws Exception { assertAcked(prepareCreate("test").setMapping(mapping)); ensureGreen("test"); - DateTime date = new DateTime(1990, 12, 29, 0, 0, DateTimeZone.UTC); - org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + ZonedDateTime date = ZonedDateTime.of(1990, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.ROOT); - index("test", MapperService.SINGLE_MAPPING_NAME, "1", "text_field", "foo", "date_field", formatter.print(date)); + index("test", MapperService.SINGLE_MAPPING_NAME, "1", "text_field", "foo", "date_field", formatter.format(date)); refresh("test"); SearchRequestBuilder builder = client().prepareSearch() diff --git a/server/src/main/java/org/joda/time/format/StrictISODateTimeFormat.java b/server/src/main/java/org/joda/time/format/StrictISODateTimeFormat.java deleted file mode 100644 index 0bc54b24d7d1a..0000000000000 --- a/server/src/main/java/org/joda/time/format/StrictISODateTimeFormat.java +++ /dev/null @@ -1,1906 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* @notice - * Copyright 2001-2009 Stephen Colebourne - * - * Licensed 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.joda.time.format; - -import org.joda.time.DateTimeFieldType; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/* - * Elasticsearch Note: This class has been copied almost identically from joda, where the - * class is named ISODatetimeFormat - * - * However there has been done one huge modification in several methods, which forces the date - * year to be exactly n digits, so that a year like "5" is invalid and must be "0005" - * - * All methods have been marked with an "// ES change" commentary - * - * In case you compare this with the original ISODateTimeFormat, make sure you use a diff - * call, that ignores whitespaces/tabs/indentations like 'diff -b' - */ - -/** - * Factory that creates instances of DateTimeFormatter based on the ISO8601 standard. - *

- * Date-time formatting is performed by the {@link DateTimeFormatter} class. - * Three classes provide factory methods to create formatters, and this is one. - * The others are {@link DateTimeFormat} and {@link DateTimeFormatterBuilder}. - *

- * ISO8601 is the international standard for data interchange. It defines a - * framework, rather than an absolute standard. As a result this provider has a - * number of methods that represent common uses of the framework. The most common - * formats are {@link #date() date}, {@link #time() time}, and {@link #dateTime() dateTime}. - *

- * For example, to format a date time in ISO format: - *

- * DateTime dt = new DateTime();
- * DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
- * String str = fmt.print(dt);
- * 
- *

- * Note that these formatters mostly follow the ISO8601 standard for printing. - * For parsing, the formatters are more lenient and allow formats that are not - * in strict compliance with the standard. - *

- * It is important to understand that these formatters are not linked to - * the ISOChronology. These formatters may be used with any - * chronology, however there may be certain side effects with more unusual - * chronologies. For example, the ISO formatters rely on dayOfWeek being - * single digit, dayOfMonth being two digit and dayOfYear being three digit. - * A chronology with a ten day week would thus cause issues. However, in - * general, it is safe to use these formatters with other chronologies. - *

- * ISODateTimeFormat is thread-safe and immutable, and the formatters it - * returns are as well. - * - * @author Brian S O'Neill - * @since 1.0 - * @see DateTimeFormat - * @see DateTimeFormatterBuilder - */ -public class StrictISODateTimeFormat { - - /** - * Constructor. - * - * @since 1.1 (previously private) - */ - protected StrictISODateTimeFormat() { - super(); - } - - // ----------------------------------------------------------------------- - /** - * Returns a formatter that outputs only those fields specified. - *

- * This method examines the fields provided and returns an ISO-style - * formatter that best fits. This can be useful for outputting - * less-common ISO styles, such as YearMonth (YYYY-MM) or MonthDay (--MM-DD). - *

- * The list provided may have overlapping fields, such as dayOfWeek and - * dayOfMonth. In this case, the style is chosen based on the following - * list, thus in the example, the calendar style is chosen as dayOfMonth - * is higher in priority than dayOfWeek: - *

    - *
  • monthOfYear - calendar date style - *
  • dayOfYear - ordinal date style - *
  • weekOfWeekYear - week date style - *
  • dayOfMonth - calendar date style - *
  • dayOfWeek - week date style - *
  • year - *
  • weekyear - *
- * The supported formats are: - *
-     * Extended      Basic       Fields
-     * 2005-03-25    20050325    year/monthOfYear/dayOfMonth
-     * 2005-03       2005-03     year/monthOfYear
-     * 2005--25      2005--25    year/dayOfMonth *
-     * 2005          2005        year
-     * --03-25       --0325      monthOfYear/dayOfMonth
-     * --03          --03        monthOfYear
-     * ---03         ---03       dayOfMonth
-     * 2005-084      2005084     year/dayOfYear
-     * -084          -084        dayOfYear
-     * 2005-W12-5    2005W125    weekyear/weekOfWeekyear/dayOfWeek
-     * 2005-W-5      2005W-5     weekyear/dayOfWeek *
-     * 2005-W12      2005W12     weekyear/weekOfWeekyear
-     * -W12-5        -W125       weekOfWeekyear/dayOfWeek
-     * -W12          -W12        weekOfWeekyear
-     * -W-5          -W-5        dayOfWeek
-     * 10:20:30.040  102030.040  hour/minute/second/milli
-     * 10:20:30      102030      hour/minute/second
-     * 10:20         1020        hour/minute
-     * 10            10          hour
-     * -20:30.040    -2030.040   minute/second/milli
-     * -20:30        -2030       minute/second
-     * -20           -20         minute
-     * --30.040      --30.040    second/milli
-     * --30          --30        second
-     * ---.040       ---.040     milli *
-     * 10-30.040     10-30.040   hour/second/milli *
-     * 10:20-.040    1020-.040   hour/minute/milli *
-     * 10-30         10-30       hour/second *
-     * 10--.040      10--.040    hour/milli *
-     * -20-.040      -20-.040    minute/milli *
-     *   plus datetime formats like {date}T{time}
-     * 
- * * indicates that this is not an official ISO format and can be excluded - * by passing in strictISO as true. - *

- * This method can side effect the input collection of fields. - * If the input collection is modifiable, then each field that was added to - * the formatter will be removed from the collection, including any duplicates. - * If the input collection is unmodifiable then no side effect occurs. - *

- * This side effect processing is useful if you need to know whether all - * the fields were converted into the formatter or not. To achieve this, - * pass in a modifiable list, and check that it is empty on exit. - * - * @param fields the fields to get a formatter for, not null, - * updated by the method call unless unmodifiable, - * removing those fields built in the formatter - * @param extended true to use the extended format (with separators) - * @param strictISO true to stick exactly to ISO8601, false to include additional formats - * @return a suitable formatter - * @throws IllegalArgumentException if there is no format for the fields - * @since 1.1 - */ - public static DateTimeFormatter forFields(Collection fields, boolean extended, boolean strictISO) { - - if (fields == null || fields.size() == 0) { - throw new IllegalArgumentException("The fields must not be null or empty"); - } - Set workingFields = new HashSet<>(fields); - int inputSize = workingFields.size(); - boolean reducedPrec = false; - DateTimeFormatterBuilder bld = new DateTimeFormatterBuilder(); - // date - if (workingFields.contains(DateTimeFieldType.monthOfYear())) { - reducedPrec = dateByMonth(bld, workingFields, extended, strictISO); - } else if (workingFields.contains(DateTimeFieldType.dayOfYear())) { - reducedPrec = dateByOrdinal(bld, workingFields, extended); - } else if (workingFields.contains(DateTimeFieldType.weekOfWeekyear())) { - reducedPrec = dateByWeek(bld, workingFields, extended, strictISO); - } else if (workingFields.contains(DateTimeFieldType.dayOfMonth())) { - reducedPrec = dateByMonth(bld, workingFields, extended, strictISO); - } else if (workingFields.contains(DateTimeFieldType.dayOfWeek())) { - reducedPrec = dateByWeek(bld, workingFields, extended, strictISO); - } else if (workingFields.remove(DateTimeFieldType.year())) { - bld.append(Constants.ye); - reducedPrec = true; - } else if (workingFields.remove(DateTimeFieldType.weekyear())) { - bld.append(Constants.we); - reducedPrec = true; - } - boolean datePresent = (workingFields.size() < inputSize); - - // time - time(bld, workingFields, extended, strictISO, reducedPrec, datePresent); - - // result - if (bld.canBuildFormatter() == false) { - throw new IllegalArgumentException("No valid format for fields: " + fields); - } - - // side effect the input collection to indicate the processed fields - // handling unmodifiable collections with no side effect - try { - fields.retainAll(workingFields); - } catch (UnsupportedOperationException ex) { - // ignore, so we can handle unmodifiable collections - } - return bld.toFormatter(); - } - - // ----------------------------------------------------------------------- - /** - * Creates a date using the calendar date format. - * Specification reference: 5.2.1. - * - * @param bld the builder - * @param fields the fields - * @param extended true to use extended format - * @param strictISO true to only allow ISO formats - * @return true if reduced precision - * @since 1.1 - */ - private static boolean dateByMonth( - DateTimeFormatterBuilder bld, - Collection fields, - boolean extended, - boolean strictISO - ) { - - boolean reducedPrec = false; - if (fields.remove(DateTimeFieldType.year())) { - bld.append(Constants.ye); - if (fields.remove(DateTimeFieldType.monthOfYear())) { - if (fields.remove(DateTimeFieldType.dayOfMonth())) { - // YYYY-MM-DD/YYYYMMDD - appendSeparator(bld, extended); - bld.appendMonthOfYear(2); - appendSeparator(bld, extended); - bld.appendDayOfMonth(2); - } else { - // YYYY-MM/YYYY-MM - bld.appendLiteral('-'); - bld.appendMonthOfYear(2); - reducedPrec = true; - } - } else { - if (fields.remove(DateTimeFieldType.dayOfMonth())) { - // YYYY--DD/YYYY--DD (non-iso) - checkNotStrictISO(fields, strictISO); - bld.appendLiteral('-'); - bld.appendLiteral('-'); - bld.appendDayOfMonth(2); - } else { - // YYYY/YYYY - reducedPrec = true; - } - } - - } else if (fields.remove(DateTimeFieldType.monthOfYear())) { - bld.appendLiteral('-'); - bld.appendLiteral('-'); - bld.appendMonthOfYear(2); - if (fields.remove(DateTimeFieldType.dayOfMonth())) { - // --MM-DD/--MMDD - appendSeparator(bld, extended); - bld.appendDayOfMonth(2); - } else { - // --MM/--MM - reducedPrec = true; - } - } else if (fields.remove(DateTimeFieldType.dayOfMonth())) { - // ---DD/---DD - bld.appendLiteral('-'); - bld.appendLiteral('-'); - bld.appendLiteral('-'); - bld.appendDayOfMonth(2); - } - return reducedPrec; - } - - // ----------------------------------------------------------------------- - /** - * Creates a date using the ordinal date format. - * Specification reference: 5.2.2. - * - * @param bld the builder - * @param fields the fields - * @param extended true to use extended format - * @since 1.1 - */ - private static boolean dateByOrdinal(DateTimeFormatterBuilder bld, Collection fields, boolean extended) { - - boolean reducedPrec = false; - if (fields.remove(DateTimeFieldType.year())) { - bld.append(Constants.ye); - if (fields.remove(DateTimeFieldType.dayOfYear())) { - // YYYY-DDD/YYYYDDD - appendSeparator(bld, extended); - bld.appendDayOfYear(3); - } else { - // YYYY/YYYY - reducedPrec = true; - } - - } else if (fields.remove(DateTimeFieldType.dayOfYear())) { - // -DDD/-DDD - bld.appendLiteral('-'); - bld.appendDayOfYear(3); - } - return reducedPrec; - } - - // ----------------------------------------------------------------------- - /** - * Creates a date using the calendar date format. - * Specification reference: 5.2.3. - * - * @param bld the builder - * @param fields the fields - * @param extended true to use extended format - * @param strictISO true to only allow ISO formats - * @since 1.1 - */ - private static boolean dateByWeek( - DateTimeFormatterBuilder bld, - Collection fields, - boolean extended, - boolean strictISO - ) { - - boolean reducedPrec = false; - if (fields.remove(DateTimeFieldType.weekyear())) { - bld.append(Constants.we); - if (fields.remove(DateTimeFieldType.weekOfWeekyear())) { - appendSeparator(bld, extended); - bld.appendLiteral('W'); - bld.appendWeekOfWeekyear(2); - if (fields.remove(DateTimeFieldType.dayOfWeek())) { - // YYYY-WWW-D/YYYYWWWD - appendSeparator(bld, extended); - bld.appendDayOfWeek(1); - } else { - // YYYY-WWW/YYYY-WWW - reducedPrec = true; - } - } else { - if (fields.remove(DateTimeFieldType.dayOfWeek())) { - // YYYY-W-D/YYYYW-D (non-iso) - checkNotStrictISO(fields, strictISO); - appendSeparator(bld, extended); - bld.appendLiteral('W'); - bld.appendLiteral('-'); - bld.appendDayOfWeek(1); - } else { - // YYYY/YYYY - reducedPrec = true; - } - } - - } else if (fields.remove(DateTimeFieldType.weekOfWeekyear())) { - bld.appendLiteral('-'); - bld.appendLiteral('W'); - bld.appendWeekOfWeekyear(2); - if (fields.remove(DateTimeFieldType.dayOfWeek())) { - // -WWW-D/-WWWD - appendSeparator(bld, extended); - bld.appendDayOfWeek(1); - } else { - // -WWW/-WWW - reducedPrec = true; - } - } else if (fields.remove(DateTimeFieldType.dayOfWeek())) { - // -W-D/-W-D - bld.appendLiteral('-'); - bld.appendLiteral('W'); - bld.appendLiteral('-'); - bld.appendDayOfWeek(1); - } - return reducedPrec; - } - - // ----------------------------------------------------------------------- - /** - * Adds the time fields to the builder. - * Specification reference: 5.3.1. - * - * @param bld the builder - * @param fields the fields - * @param extended whether to use the extended format - * @param strictISO whether to be strict - * @param reducedPrec whether the date was reduced precision - * @param datePresent whether there was a date - * @since 1.1 - */ - private static void time( - DateTimeFormatterBuilder bld, - Collection fields, - boolean extended, - boolean strictISO, - boolean reducedPrec, - boolean datePresent - ) { - - boolean hour = fields.remove(DateTimeFieldType.hourOfDay()); - boolean minute = fields.remove(DateTimeFieldType.minuteOfHour()); - boolean second = fields.remove(DateTimeFieldType.secondOfMinute()); - boolean milli = fields.remove(DateTimeFieldType.millisOfSecond()); - if (!hour && !minute && !second && !milli) { - return; - } - if (hour || minute || second || milli) { - if (strictISO && reducedPrec) { - throw new IllegalArgumentException("No valid ISO8601 format for fields because Date was reduced precision: " + fields); - } - if (datePresent) { - bld.appendLiteral('T'); - } - } - if (hour && minute && second || (hour && !second && !milli)) { - // OK - HMSm/HMS/HM/H - valid in combination with date - } else { - if (strictISO && datePresent) { - throw new IllegalArgumentException("No valid ISO8601 format for fields because Time was truncated: " + fields); - } - if (!hour && (minute && second || (minute && !milli) || second)) { - // OK - MSm/MS/M/Sm/S - valid ISO formats - } else { - if (strictISO) { - throw new IllegalArgumentException("No valid ISO8601 format for fields: " + fields); - } - } - } - if (hour) { - bld.appendHourOfDay(2); - } else if (minute || second || milli) { - bld.appendLiteral('-'); - } - if (extended && hour && minute) { - bld.appendLiteral(':'); - } - if (minute) { - bld.appendMinuteOfHour(2); - } else if (second || milli) { - bld.appendLiteral('-'); - } - if (extended && minute && second) { - bld.appendLiteral(':'); - } - if (second) { - bld.appendSecondOfMinute(2); - } else if (milli) { - bld.appendLiteral('-'); - } - if (milli) { - bld.appendLiteral('.'); - bld.appendMillisOfSecond(3); - } - } - - // ----------------------------------------------------------------------- - /** - * Checks that the iso only flag is not set, throwing an exception if it is. - * - * @param fields the fields - * @param strictISO true if only ISO formats allowed - * @since 1.1 - */ - private static void checkNotStrictISO(Collection fields, boolean strictISO) { - if (strictISO) { - throw new IllegalArgumentException("No valid ISO8601 format for fields: " + fields); - } - } - - /** - * Appends the separator if necessary. - * - * @param bld the builder - * @param extended whether to append the separator - * @since 1.1 - */ - private static void appendSeparator(DateTimeFormatterBuilder bld, boolean extended) { - if (extended) { - bld.appendLiteral('-'); - } - } - - // ----------------------------------------------------------------------- - /** - * Returns a generic ISO date parser for parsing dates with a possible zone. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * It accepts formats described by the following syntax: - *

-     * date              = date-element ['T' offset]
-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * offset            = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]])
-     * 
- */ - public static DateTimeFormatter dateParser() { - return Constants.dp; - } - - /** - * Returns a generic ISO date parser for parsing local dates. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * This parser is initialised with the local (UTC) time zone. - *

- * It accepts formats described by the following syntax: - *

-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * 
- * @since 1.3 - */ - public static DateTimeFormatter localDateParser() { - return Constants.ldp; - } - - /** - * Returns a generic ISO date parser for parsing dates. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * It accepts formats described by the following syntax: - *

-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * 
- */ - public static DateTimeFormatter dateElementParser() { - return Constants.dpe; - } - - /** - * Returns a generic ISO time parser for parsing times with a possible zone. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * time           = ['T'] time-element [offset]
-     * time-element   = HH [minute-element] | [fraction]
-     * minute-element = ':' mm [second-element] | [fraction]
-     * second-element = ':' ss [fraction]
-     * fraction       = ('.' | ',') digit+
-     * offset         = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]])
-     * 
- */ - public static DateTimeFormatter timeParser() { - return Constants.tp; - } - - /** - * Returns a generic ISO time parser for parsing local times. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * This parser is initialised with the local (UTC) time zone. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * time           = ['T'] time-element
-     * time-element   = HH [minute-element] | [fraction]
-     * minute-element = ':' mm [second-element] | [fraction]
-     * second-element = ':' ss [fraction]
-     * fraction       = ('.' | ',') digit+
-     * 
- * @since 1.3 - */ - public static DateTimeFormatter localTimeParser() { - return Constants.ltp; - } - - /** - * Returns a generic ISO time parser. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * time-element   = HH [minute-element] | [fraction]
-     * minute-element = ':' mm [second-element] | [fraction]
-     * second-element = ':' ss [fraction]
-     * fraction       = ('.' | ',') digit+
-     * 
- */ - public static DateTimeFormatter timeElementParser() { - return Constants.tpe; - } - - /** - * Returns a generic ISO datetime parser which parses either a date or a time or both. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * datetime          = time | date-opt-time
-     * time              = 'T' time-element [offset]
-     * date-opt-time     = date-element ['T' [time-element] [offset]]
-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * time-element      = HH [minute-element] | [fraction]
-     * minute-element    = ':' mm [second-element] | [fraction]
-     * second-element    = ':' ss [fraction]
-     * fraction          = ('.' | ',') digit+
-     * offset            = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]])
-     * 
- */ - public static DateTimeFormatter dateTimeParser() { - return Constants.dtp; - } - - /** - * Returns a generic ISO datetime parser where the date is mandatory and the time is optional. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * This parser can parse zoned datetimes. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * date-opt-time     = date-element ['T' [time-element] [offset]]
-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * time-element      = HH [minute-element] | [fraction]
-     * minute-element    = ':' mm [second-element] | [fraction]
-     * second-element    = ':' ss [fraction]
-     * fraction          = ('.' | ',') digit+
-     * 
- * @since 1.3 - */ - public static DateTimeFormatter dateOptionalTimeParser() { - return Constants.dotp; - } - - /** - * Returns a generic ISO datetime parser where the date is mandatory and the time is optional. - *

- * The returned formatter can only be used for parsing, printing is unsupported. - *

- * This parser only parses local datetimes. - * This parser is initialised with the local (UTC) time zone. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * It accepts formats described by the following syntax: - *

-     * datetime          = date-element ['T' time-element]
-     * date-element      = std-date-element | ord-date-element | week-date-element
-     * std-date-element  = yyyy ['-' MM ['-' dd]]
-     * ord-date-element  = yyyy ['-' DDD]
-     * week-date-element = xxxx '-W' ww ['-' e]
-     * time-element      = HH [minute-element] | [fraction]
-     * minute-element    = ':' mm [second-element] | [fraction]
-     * second-element    = ':' ss [fraction]
-     * fraction          = ('.' | ',') digit+
-     * 
- * @since 1.3 - */ - public static DateTimeFormatter localDateOptionalTimeParser() { - return Constants.ldotp; - } - - // ----------------------------------------------------------------------- - /** - * Returns a formatter for a full date as four digit year, two digit month - * of year, and two digit day of month (yyyy-MM-dd). - *

- * The returned formatter prints and parses only this format. - * See {@link #dateParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-MM-dd - */ - public static DateTimeFormatter date() { - return yearMonthDay(); - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, three digit fraction of second, and - * time zone offset (HH:mm:ss.SSSZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * See {@link #timeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for HH:mm:ss.SSSZZ - */ - public static DateTimeFormatter time() { - return Constants.t; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, and time zone offset (HH:mm:ssZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * See {@link #timeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for HH:mm:ssZZ - */ - public static DateTimeFormatter timeNoMillis() { - return Constants.tx; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, three digit fraction of second, and - * time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * See {@link #timeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for 'T'HH:mm:ss.SSSZZ - */ - public static DateTimeFormatter tTime() { - return Constants.tt; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, and time zone offset prefixed - * by 'T' ('T'HH:mm:ssZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * See {@link #timeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for 'T'HH:mm:ssZZ - */ - public static DateTimeFormatter tTimeNoMillis() { - return Constants.ttx; - } - - /** - * Returns a formatter that combines a full date and time, separated by a 'T' - * (yyyy-MM-dd'T'HH:mm:ss.SSSZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSSZZ - */ - public static DateTimeFormatter dateTime() { - return Constants.dt; - } - - /** - * Returns a formatter that combines a full date and time without millis, - * separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-MM-dd'T'HH:mm:ssZZ - */ - public static DateTimeFormatter dateTimeNoMillis() { - return Constants.dtx; - } - - /** - * Returns a formatter for a full ordinal date, using a four - * digit year and three digit dayOfYear (yyyy-DDD). - *

- * The returned formatter prints and parses only this format. - * See {@link #dateParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-DDD - * @since 1.1 - */ - public static DateTimeFormatter ordinalDate() { - return Constants.od; - } - - /** - * Returns a formatter for a full ordinal date and time, using a four - * digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-DDD'T'HH:mm:ss.SSSZZ - * @since 1.1 - */ - public static DateTimeFormatter ordinalDateTime() { - return Constants.odt; - } - - /** - * Returns a formatter for a full ordinal date and time without millis, - * using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for yyyy-DDD'T'HH:mm:ssZZ - * @since 1.1 - */ - public static DateTimeFormatter ordinalDateTimeNoMillis() { - return Constants.odtx; - } - - /** - * Returns a formatter for a full date as four digit weekyear, two digit - * week of weekyear, and one digit day of week (xxxx-'W'ww-e). - *

- * The returned formatter prints and parses only this format. - * See {@link #dateParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for xxxx-'W'ww-e - */ - public static DateTimeFormatter weekDate() { - return Constants.wwd; - } - - /** - * Returns a formatter that combines a full weekyear date and time, - * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ - */ - public static DateTimeFormatter weekDateTime() { - return Constants.wdt; - } - - /** - * Returns a formatter that combines a full weekyear date and time without millis, - * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * See {@link #dateTimeParser()} for a more flexible parser that accepts different formats. - * - * @return a formatter for xxxx-'W'ww-e'T'HH:mm:ssZZ - */ - public static DateTimeFormatter weekDateTimeNoMillis() { - return Constants.wdtx; - } - - // ----------------------------------------------------------------------- - /** - * Returns a basic formatter for a full date as four digit year, two digit - * month of year, and two digit day of month (yyyyMMdd). - *

- * The returned formatter prints and parses only this format. - * - * @return a formatter for yyyyMMdd - */ - public static DateTimeFormatter basicDate() { - return Constants.bd; - } - - /** - * Returns a basic formatter for a two digit hour of day, two digit minute - * of hour, two digit second of minute, three digit millis, and time zone - * offset (HHmmss.SSSZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * - * @return a formatter for HHmmss.SSSZ - */ - public static DateTimeFormatter basicTime() { - return Constants.bt; - } - - /** - * Returns a basic formatter for a two digit hour of day, two digit minute - * of hour, two digit second of minute, and time zone offset (HHmmssZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * - * @return a formatter for HHmmssZ - */ - public static DateTimeFormatter basicTimeNoMillis() { - return Constants.btx; - } - - /** - * Returns a basic formatter for a two digit hour of day, two digit minute - * of hour, two digit second of minute, three digit millis, and time zone - * offset prefixed by 'T' ('T'HHmmss.SSSZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * - * @return a formatter for 'T'HHmmss.SSSZ - */ - public static DateTimeFormatter basicTTime() { - return Constants.btt; - } - - /** - * Returns a basic formatter for a two digit hour of day, two digit minute - * of hour, two digit second of minute, and time zone offset prefixed by 'T' - * ('T'HHmmssZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * - * @return a formatter for 'T'HHmmssZ - */ - public static DateTimeFormatter basicTTimeNoMillis() { - return Constants.bttx; - } - - /** - * Returns a basic formatter that combines a basic date and time, separated - * by a 'T' (yyyyMMdd'T'HHmmss.SSSZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * - * @return a formatter for yyyyMMdd'T'HHmmss.SSSZ - */ - public static DateTimeFormatter basicDateTime() { - return Constants.bdt; - } - - /** - * Returns a basic formatter that combines a basic date and time without millis, - * separated by a 'T' (yyyyMMdd'T'HHmmssZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * - * @return a formatter for yyyyMMdd'T'HHmmssZ - */ - public static DateTimeFormatter basicDateTimeNoMillis() { - return Constants.bdtx; - } - - /** - * Returns a formatter for a full ordinal date, using a four - * digit year and three digit dayOfYear (yyyyDDD). - *

- * The returned formatter prints and parses only this format. - * - * @return a formatter for yyyyDDD - * @since 1.1 - */ - public static DateTimeFormatter basicOrdinalDate() { - return Constants.bod; - } - - /** - * Returns a formatter for a full ordinal date and time, using a four - * digit year and three digit dayOfYear (yyyyDDD'T'HHmmss.SSSZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * - * @return a formatter for yyyyDDD'T'HHmmss.SSSZ - * @since 1.1 - */ - public static DateTimeFormatter basicOrdinalDateTime() { - return Constants.bodt; - } - - /** - * Returns a formatter for a full ordinal date and time without millis, - * using a four digit year and three digit dayOfYear (yyyyDDD'T'HHmmssZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * - * @return a formatter for yyyyDDD'T'HHmmssZ - * @since 1.1 - */ - public static DateTimeFormatter basicOrdinalDateTimeNoMillis() { - return Constants.bodtx; - } - - /** - * Returns a basic formatter for a full date as four digit weekyear, two - * digit week of weekyear, and one digit day of week (xxxx'W'wwe). - *

- * The returned formatter prints and parses only this format. - * - * @return a formatter for xxxx'W'wwe - */ - public static DateTimeFormatter basicWeekDate() { - return Constants.bwd; - } - - /** - * Returns a basic formatter that combines a basic weekyear date and time, - * separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which includes milliseconds. - * - * @return a formatter for xxxx'W'wwe'T'HHmmss.SSSZ - */ - public static DateTimeFormatter basicWeekDateTime() { - return Constants.bwdt; - } - - /** - * Returns a basic formatter that combines a basic weekyear date and time - * without millis, separated by a 'T' (xxxx'W'wwe'T'HHmmssZ). - *

- * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. - * The parser is strict by default, thus time string {@code 24:00} cannot be parsed. - *

- * The returned formatter prints and parses only this format, which excludes milliseconds. - * - * @return a formatter for xxxx'W'wwe'T'HHmmssZ - */ - public static DateTimeFormatter basicWeekDateTimeNoMillis() { - return Constants.bwdtx; - } - - // ----------------------------------------------------------------------- - /** - * Returns a formatter for a four digit year. (yyyy) - * - * @return a formatter for yyyy - */ - public static DateTimeFormatter year() { - return Constants.ye; - } - - /** - * Returns a formatter for a four digit year and two digit month of - * year. (yyyy-MM) - * - * @return a formatter for yyyy-MM - */ - public static DateTimeFormatter yearMonth() { - return Constants.ym; - } - - /** - * Returns a formatter for a four digit year, two digit month of year, and - * two digit day of month. (yyyy-MM-dd) - * - * @return a formatter for yyyy-MM-dd - */ - public static DateTimeFormatter yearMonthDay() { - return Constants.ymd; - } - - /** - * Returns a formatter for a four digit weekyear. (xxxx) - * - * @return a formatter for xxxx - */ - public static DateTimeFormatter weekyear() { - return Constants.we; - } - - /** - * Returns a formatter for a four digit weekyear and two digit week of - * weekyear. (xxxx-'W'ww) - * - * @return a formatter for xxxx-'W'ww - */ - public static DateTimeFormatter weekyearWeek() { - return Constants.ww; - } - - /** - * Returns a formatter for a four digit weekyear, two digit week of - * weekyear, and one digit day of week. (xxxx-'W'ww-e) - * - * @return a formatter for xxxx-'W'ww-e - */ - public static DateTimeFormatter weekyearWeekDay() { - return Constants.wwd; - } - - /** - * Returns a formatter for a two digit hour of day. (HH) - * - * @return a formatter for HH - */ - public static DateTimeFormatter hour() { - return Constants.hde; - } - - /** - * Returns a formatter for a two digit hour of day and two digit minute of - * hour. (HH:mm) - * - * @return a formatter for HH:mm - */ - public static DateTimeFormatter hourMinute() { - return Constants.hm; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, and two digit second of minute. (HH:mm:ss) - * - * @return a formatter for HH:mm:ss - */ - public static DateTimeFormatter hourMinuteSecond() { - return Constants.hms; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, and three digit fraction of - * second (HH:mm:ss.SSS). Parsing will parse up to 3 fractional second - * digits. - * - * @return a formatter for HH:mm:ss.SSS - */ - public static DateTimeFormatter hourMinuteSecondMillis() { - return Constants.hmsl; - } - - /** - * Returns a formatter for a two digit hour of day, two digit minute of - * hour, two digit second of minute, and three digit fraction of - * second (HH:mm:ss.SSS). Parsing will parse up to 9 fractional second - * digits, throwing away all except the first three. - * - * @return a formatter for HH:mm:ss.SSS - */ - public static DateTimeFormatter hourMinuteSecondFraction() { - return Constants.hmsf; - } - - /** - * Returns a formatter that combines a full date and two digit hour of - * day. (yyyy-MM-dd'T'HH) - * - * @return a formatter for yyyy-MM-dd'T'HH - */ - public static DateTimeFormatter dateHour() { - return Constants.dh; - } - - /** - * Returns a formatter that combines a full date, two digit hour of day, - * and two digit minute of hour. (yyyy-MM-dd'T'HH:mm) - * - * @return a formatter for yyyy-MM-dd'T'HH:mm - */ - public static DateTimeFormatter dateHourMinute() { - return Constants.dhm; - } - - /** - * Returns a formatter that combines a full date, two digit hour of day, - * two digit minute of hour, and two digit second of - * minute. (yyyy-MM-dd'T'HH:mm:ss) - * - * @return a formatter for yyyy-MM-dd'T'HH:mm:ss - */ - public static DateTimeFormatter dateHourMinuteSecond() { - return Constants.dhms; - } - - /** - * Returns a formatter that combines a full date, two digit hour of day, - * two digit minute of hour, two digit second of minute, and three digit - * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up - * to 3 fractional second digits. - * - * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSS - */ - public static DateTimeFormatter dateHourMinuteSecondMillis() { - return Constants.dhmsl; - } - - /** - * Returns a formatter that combines a full date, two digit hour of day, - * two digit minute of hour, two digit second of minute, and three digit - * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up - * to 9 fractional second digits, throwing away all except the first three. - * - * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSS - */ - public static DateTimeFormatter dateHourMinuteSecondFraction() { - return Constants.dhmsf; - } - - // ----------------------------------------------------------------------- - static final class Constants { - private static final DateTimeFormatter ye = yearElement(), // year element (yyyy) - mye = monthElement(), // monthOfYear element (-MM) - dme = dayOfMonthElement(), // dayOfMonth element (-dd) - we = weekyearElement(), // weekyear element (xxxx) - wwe = weekElement(), // weekOfWeekyear element (-ww) - dwe = dayOfWeekElement(), // dayOfWeek element (-ee) - dye = dayOfYearElement(), // dayOfYear element (-DDD) - hde = hourElement(), // hourOfDay element (HH) - mhe = minuteElement(), // minuteOfHour element (:mm) - sme = secondElement(), // secondOfMinute element (:ss) - fse = fractionElement(), // fractionOfSecond element (.SSSSSSSSS) - ze = offsetElement(), // zone offset element - lte = literalTElement(), // literal 'T' element - - // y, // year (same as year element) - ym = yearMonth(), // year month - ymd = yearMonthDay(), // year month day - - // w, // weekyear (same as weekyear element) - ww = weekyearWeek(), // weekyear week - wwd = weekyearWeekDay(), // weekyear week day - - // h, // hour (same as hour element) - hm = hourMinute(), // hour minute - hms = hourMinuteSecond(), // hour minute second - hmsl = hourMinuteSecondMillis(), // hour minute second millis - hmsf = hourMinuteSecondFraction(), // hour minute second fraction - - dh = dateHour(), // date hour - dhm = dateHourMinute(), // date hour minute - dhms = dateHourMinuteSecond(), // date hour minute second - dhmsl = dateHourMinuteSecondMillis(), // date hour minute second millis - dhmsf = dateHourMinuteSecondFraction(), // date hour minute second fraction - - // d, // date (same as ymd) - t = time(), // time - tx = timeNoMillis(), // time no millis - tt = tTime(), // Ttime - ttx = tTimeNoMillis(), // Ttime no millis - dt = dateTime(), // date time - dtx = dateTimeNoMillis(), // date time no millis - - // wd, // week date (same as wwd) - wdt = weekDateTime(), // week date time - wdtx = weekDateTimeNoMillis(), // week date time no millis - - od = ordinalDate(), // ordinal date (same as yd) - odt = ordinalDateTime(), // ordinal date time - odtx = ordinalDateTimeNoMillis(), // ordinal date time no millis - - bd = basicDate(), // basic date - bt = basicTime(), // basic time - btx = basicTimeNoMillis(), // basic time no millis - btt = basicTTime(), // basic Ttime - bttx = basicTTimeNoMillis(), // basic Ttime no millis - bdt = basicDateTime(), // basic date time - bdtx = basicDateTimeNoMillis(), // basic date time no millis - - bod = basicOrdinalDate(), // basic ordinal date - bodt = basicOrdinalDateTime(), // basic ordinal date time - bodtx = basicOrdinalDateTimeNoMillis(), // basic ordinal date time no millis - - bwd = basicWeekDate(), // basic week date - bwdt = basicWeekDateTime(), // basic week date time - bwdtx = basicWeekDateTimeNoMillis(), // basic week date time no millis - - dpe = dateElementParser(), // date parser element - tpe = timeElementParser(), // time parser element - dp = dateParser(), // date parser - ldp = localDateParser(), // local date parser - tp = timeParser(), // time parser - ltp = localTimeParser(), // local time parser - dtp = dateTimeParser(), // date time parser - dotp = dateOptionalTimeParser(), // date optional time parser - ldotp = localDateOptionalTimeParser(); // local date optional time parser - - // ----------------------------------------------------------------------- - private static DateTimeFormatter dateParser() { - if (dp == null) { - DateTimeParser tOffset = new DateTimeFormatterBuilder().appendLiteral('T').append(offsetElement()).toParser(); - return new DateTimeFormatterBuilder().append(dateElementParser()).appendOptional(tOffset).toFormatter(); - } - return dp; - } - - private static DateTimeFormatter localDateParser() { - if (ldp == null) { - return dateElementParser().withZoneUTC(); - } - return ldp; - } - - private static DateTimeFormatter dateElementParser() { - if (dpe == null) { - return new DateTimeFormatterBuilder().append( - null, - new DateTimeParser[] { - new DateTimeFormatterBuilder().append(yearElement()) - .appendOptional( - new DateTimeFormatterBuilder().append(monthElement()) - .appendOptional(dayOfMonthElement().getParser()) - .toParser() - ) - .toParser(), - new DateTimeFormatterBuilder().append(weekyearElement()) - .append(weekElement()) - .appendOptional(dayOfWeekElement().getParser()) - .toParser(), - new DateTimeFormatterBuilder().append(yearElement()).append(dayOfYearElement()).toParser() } - ).toFormatter(); - } - return dpe; - } - - private static DateTimeFormatter timeParser() { - if (tp == null) { - return new DateTimeFormatterBuilder().appendOptional(literalTElement().getParser()) - .append(timeElementParser()) - .appendOptional(offsetElement().getParser()) - .toFormatter(); - } - return tp; - } - - private static DateTimeFormatter localTimeParser() { - if (ltp == null) { - return new DateTimeFormatterBuilder().appendOptional(literalTElement().getParser()) - .append(timeElementParser()) - .toFormatter() - .withZoneUTC(); - } - return ltp; - } - - private static DateTimeFormatter timeElementParser() { - if (tpe == null) { - // Decimal point can be either '.' or ',' - DateTimeParser decimalPoint = new DateTimeFormatterBuilder().append( - null, - new DateTimeParser[] { - new DateTimeFormatterBuilder().appendLiteral('.').toParser(), - new DateTimeFormatterBuilder().appendLiteral(',').toParser() } - ).toParser(); - - return new DateTimeFormatterBuilder() - // time-element - .append(hourElement()) - .append( - null, - new DateTimeParser[] { - new DateTimeFormatterBuilder() - // minute-element - .append(minuteElement()) - .append( - null, - new DateTimeParser[] { - new DateTimeFormatterBuilder() - // second-element - .append(secondElement()) - // second fraction - .appendOptional( - new DateTimeFormatterBuilder().append(decimalPoint).appendFractionOfSecond(1, 9).toParser() - ) - .toParser(), - // minute fraction - new DateTimeFormatterBuilder().append(decimalPoint).appendFractionOfMinute(1, 9).toParser(), - null } - ) - .toParser(), - // hour fraction - new DateTimeFormatterBuilder().append(decimalPoint).appendFractionOfHour(1, 9).toParser(), - null } - ) - .toFormatter(); - } - return tpe; - } - - private static DateTimeFormatter dateTimeParser() { - if (dtp == null) { - // This is different from the general time parser in that the 'T' - // is required. - DateTimeParser time = new DateTimeFormatterBuilder().appendLiteral('T') - .append(timeElementParser()) - .appendOptional(offsetElement().getParser()) - .toParser(); - return new DateTimeFormatterBuilder().append(null, new DateTimeParser[] { time, dateOptionalTimeParser().getParser() }) - .toFormatter(); - } - return dtp; - } - - private static DateTimeFormatter dateOptionalTimeParser() { - if (dotp == null) { - DateTimeParser timeOrOffset = new DateTimeFormatterBuilder().appendLiteral('T') - .appendOptional(timeElementParser().getParser()) - .appendOptional(offsetElement().getParser()) - .toParser(); - return new DateTimeFormatterBuilder().append(dateElementParser()).appendOptional(timeOrOffset).toFormatter(); - } - return dotp; - } - - private static DateTimeFormatter localDateOptionalTimeParser() { - if (ldotp == null) { - DateTimeParser time = new DateTimeFormatterBuilder().appendLiteral('T').append(timeElementParser()).toParser(); - return new DateTimeFormatterBuilder().append(dateElementParser()).appendOptional(time).toFormatter().withZoneUTC(); - } - return ldotp; - } - - // ----------------------------------------------------------------------- - private static DateTimeFormatter time() { - if (t == null) { - return new DateTimeFormatterBuilder().append(hourMinuteSecondFraction()).append(offsetElement()).toFormatter(); - } - return t; - } - - private static DateTimeFormatter timeNoMillis() { - if (tx == null) { - return new DateTimeFormatterBuilder().append(hourMinuteSecond()).append(offsetElement()).toFormatter(); - } - return tx; - } - - private static DateTimeFormatter tTime() { - if (tt == null) { - return new DateTimeFormatterBuilder().append(literalTElement()).append(time()).toFormatter(); - } - return tt; - } - - private static DateTimeFormatter tTimeNoMillis() { - if (ttx == null) { - return new DateTimeFormatterBuilder().append(literalTElement()).append(timeNoMillis()).toFormatter(); - } - return ttx; - } - - private static DateTimeFormatter dateTime() { - if (dt == null) { - return new DateTimeFormatterBuilder().append(date()).append(tTime()).toFormatter(); - } - return dt; - } - - private static DateTimeFormatter dateTimeNoMillis() { - if (dtx == null) { - return new DateTimeFormatterBuilder().append(date()).append(tTimeNoMillis()).toFormatter(); - } - return dtx; - } - - private static DateTimeFormatter ordinalDate() { - if (od == null) { - return new DateTimeFormatterBuilder().append(yearElement()).append(dayOfYearElement()).toFormatter(); - } - return od; - } - - private static DateTimeFormatter ordinalDateTime() { - if (odt == null) { - return new DateTimeFormatterBuilder().append(ordinalDate()).append(tTime()).toFormatter(); - } - return odt; - } - - private static DateTimeFormatter ordinalDateTimeNoMillis() { - if (odtx == null) { - return new DateTimeFormatterBuilder().append(ordinalDate()).append(tTimeNoMillis()).toFormatter(); - } - return odtx; - } - - private static DateTimeFormatter weekDateTime() { - if (wdt == null) { - return new DateTimeFormatterBuilder().append(weekDate()).append(tTime()).toFormatter(); - } - return wdt; - } - - private static DateTimeFormatter weekDateTimeNoMillis() { - if (wdtx == null) { - return new DateTimeFormatterBuilder().append(weekDate()).append(tTimeNoMillis()).toFormatter(); - } - return wdtx; - } - - // ----------------------------------------------------------------------- - private static DateTimeFormatter basicDate() { - if (bd == null) { - return new DateTimeFormatterBuilder().appendYear(4, 4) - .appendFixedDecimal(DateTimeFieldType.monthOfYear(), 2) - .appendFixedDecimal(DateTimeFieldType.dayOfMonth(), 2) - .toFormatter(); - } - return bd; - } - - private static DateTimeFormatter basicTime() { - if (bt == null) { - return new DateTimeFormatterBuilder().appendFixedDecimal(DateTimeFieldType.hourOfDay(), 2) - .appendFixedDecimal(DateTimeFieldType.minuteOfHour(), 2) - .appendFixedDecimal(DateTimeFieldType.secondOfMinute(), 2) - .appendLiteral('.') - .appendFractionOfSecond(3, 9) - .appendTimeZoneOffset("Z", false, 2, 2) - .toFormatter(); - } - return bt; - } - - private static DateTimeFormatter basicTimeNoMillis() { - if (btx == null) { - return new DateTimeFormatterBuilder().appendFixedDecimal(DateTimeFieldType.hourOfDay(), 2) - .appendFixedDecimal(DateTimeFieldType.minuteOfHour(), 2) - .appendFixedDecimal(DateTimeFieldType.secondOfMinute(), 2) - .appendTimeZoneOffset("Z", false, 2, 2) - .toFormatter(); - } - return btx; - } - - private static DateTimeFormatter basicTTime() { - if (btt == null) { - return new DateTimeFormatterBuilder().append(literalTElement()).append(basicTime()).toFormatter(); - } - return btt; - } - - private static DateTimeFormatter basicTTimeNoMillis() { - if (bttx == null) { - return new DateTimeFormatterBuilder().append(literalTElement()).append(basicTimeNoMillis()).toFormatter(); - } - return bttx; - } - - private static DateTimeFormatter basicDateTime() { - if (bdt == null) { - return new DateTimeFormatterBuilder().append(basicDate()).append(basicTTime()).toFormatter(); - } - return bdt; - } - - private static DateTimeFormatter basicDateTimeNoMillis() { - if (bdtx == null) { - return new DateTimeFormatterBuilder().append(basicDate()).append(basicTTimeNoMillis()).toFormatter(); - } - return bdtx; - } - - private static DateTimeFormatter basicOrdinalDate() { - if (bod == null) { - return new DateTimeFormatterBuilder().appendYear(4, 4).appendFixedDecimal(DateTimeFieldType.dayOfYear(), 3).toFormatter(); - } - return bod; - } - - private static DateTimeFormatter basicOrdinalDateTime() { - if (bodt == null) { - return new DateTimeFormatterBuilder().append(basicOrdinalDate()).append(basicTTime()).toFormatter(); - } - return bodt; - } - - private static DateTimeFormatter basicOrdinalDateTimeNoMillis() { - if (bodtx == null) { - return new DateTimeFormatterBuilder().append(basicOrdinalDate()).append(basicTTimeNoMillis()).toFormatter(); - } - return bodtx; - } - - private static DateTimeFormatter basicWeekDate() { - if (bwd == null) { - return new DateTimeFormatterBuilder() - // ES change, was .appendWeekyear(4, 4) - .appendFixedSignedDecimal(DateTimeFieldType.weekyear(), 4) - .appendLiteral('W') - .appendFixedDecimal(DateTimeFieldType.weekOfWeekyear(), 2) - .appendFixedDecimal(DateTimeFieldType.dayOfWeek(), 1) - .toFormatter(); - } - return bwd; - } - - private static DateTimeFormatter basicWeekDateTime() { - if (bwdt == null) { - return new DateTimeFormatterBuilder().append(basicWeekDate()).append(basicTTime()).toFormatter(); - } - return bwdt; - } - - private static DateTimeFormatter basicWeekDateTimeNoMillis() { - if (bwdtx == null) { - return new DateTimeFormatterBuilder().append(basicWeekDate()).append(basicTTimeNoMillis()).toFormatter(); - } - return bwdtx; - } - - // ----------------------------------------------------------------------- - private static DateTimeFormatter yearMonth() { - if (ym == null) { - return new DateTimeFormatterBuilder().append(yearElement()).append(monthElement()).toFormatter(); - } - return ym; - } - - private static DateTimeFormatter yearMonthDay() { - if (ymd == null) { - return new DateTimeFormatterBuilder().append(yearElement()) - .append(monthElement()) - .append(dayOfMonthElement()) - .toFormatter(); - } - return ymd; - } - - private static DateTimeFormatter weekyearWeek() { - if (ww == null) { - return new DateTimeFormatterBuilder().append(weekyearElement()).append(weekElement()).toFormatter(); - } - return ww; - } - - private static DateTimeFormatter weekyearWeekDay() { - if (wwd == null) { - return new DateTimeFormatterBuilder().append(weekyearElement()) - .append(weekElement()) - .append(dayOfWeekElement()) - .toFormatter(); - } - return wwd; - } - - private static DateTimeFormatter hourMinute() { - if (hm == null) { - return new DateTimeFormatterBuilder().append(hourElement()).append(minuteElement()).toFormatter(); - } - return hm; - } - - private static DateTimeFormatter hourMinuteSecond() { - if (hms == null) { - return new DateTimeFormatterBuilder().append(hourElement()).append(minuteElement()).append(secondElement()).toFormatter(); - } - return hms; - } - - private static DateTimeFormatter hourMinuteSecondMillis() { - if (hmsl == null) { - return new DateTimeFormatterBuilder().append(hourElement()) - .append(minuteElement()) - .append(secondElement()) - .appendLiteral('.') - .appendFractionOfSecond(3, 3) - .toFormatter(); - } - return hmsl; - } - - private static DateTimeFormatter hourMinuteSecondFraction() { - if (hmsf == null) { - return new DateTimeFormatterBuilder().append(hourElement()) - .append(minuteElement()) - .append(secondElement()) - .append(fractionElement()) - .toFormatter(); - } - return hmsf; - } - - private static DateTimeFormatter dateHour() { - if (dh == null) { - return new DateTimeFormatterBuilder().append(date()).append(literalTElement()).append(hour()).toFormatter(); - } - return dh; - } - - private static DateTimeFormatter dateHourMinute() { - if (dhm == null) { - return new DateTimeFormatterBuilder().append(date()).append(literalTElement()).append(hourMinute()).toFormatter(); - } - return dhm; - } - - private static DateTimeFormatter dateHourMinuteSecond() { - if (dhms == null) { - return new DateTimeFormatterBuilder().append(date()).append(literalTElement()).append(hourMinuteSecond()).toFormatter(); - } - return dhms; - } - - private static DateTimeFormatter dateHourMinuteSecondMillis() { - if (dhmsl == null) { - return new DateTimeFormatterBuilder().append(date()) - .append(literalTElement()) - .append(hourMinuteSecondMillis()) - .toFormatter(); - } - return dhmsl; - } - - private static DateTimeFormatter dateHourMinuteSecondFraction() { - if (dhmsf == null) { - return new DateTimeFormatterBuilder().append(date()) - .append(literalTElement()) - .append(hourMinuteSecondFraction()) - .toFormatter(); - } - return dhmsf; - } - - // ----------------------------------------------------------------------- - private static DateTimeFormatter yearElement() { - if (ye == null) { - return new DateTimeFormatterBuilder() - // ES change, was .appendYear(4, 9) - .appendFixedSignedDecimal(DateTimeFieldType.year(), 4) - .toFormatter(); - } - return ye; - } - - private static DateTimeFormatter monthElement() { - if (mye == null) { - return new DateTimeFormatterBuilder().appendLiteral('-') - // ES change, was .appendMonthOfYear(2) - .appendFixedSignedDecimal(DateTimeFieldType.monthOfYear(), 2) - .toFormatter(); - } - return mye; - } - - private static DateTimeFormatter dayOfMonthElement() { - if (dme == null) { - return new DateTimeFormatterBuilder().appendLiteral('-') - // ES change, was .appendDayOfMonth(2) - .appendFixedSignedDecimal(DateTimeFieldType.dayOfMonth(), 2) - .toFormatter(); - } - return dme; - } - - private static DateTimeFormatter weekyearElement() { - if (we == null) { - return new DateTimeFormatterBuilder() - // ES change, was .appendWeekyear(4, 9) - .appendFixedSignedDecimal(DateTimeFieldType.weekyear(), 4) - .toFormatter(); - } - return we; - } - - private static DateTimeFormatter weekElement() { - if (wwe == null) { - return new DateTimeFormatterBuilder().appendLiteral("-W") - // ES change, was .appendWeekOfWeekyear(2) - .appendFixedSignedDecimal(DateTimeFieldType.weekOfWeekyear(), 2) - .toFormatter(); - } - return wwe; - } - - private static DateTimeFormatter dayOfWeekElement() { - if (dwe == null) { - return new DateTimeFormatterBuilder().appendLiteral('-').appendDayOfWeek(1).toFormatter(); - } - return dwe; - } - - private static DateTimeFormatter dayOfYearElement() { - if (dye == null) { - return new DateTimeFormatterBuilder().appendLiteral('-') - // ES change, was .appendDayOfYear(3) - .appendFixedSignedDecimal(DateTimeFieldType.dayOfYear(), 3) - .toFormatter(); - } - return dye; - } - - private static DateTimeFormatter literalTElement() { - if (lte == null) { - return new DateTimeFormatterBuilder().appendLiteral('T').toFormatter(); - } - return lte; - } - - private static DateTimeFormatter hourElement() { - if (hde == null) { - return new DateTimeFormatterBuilder() - // ES change, was .appendHourOfDay(2) - .appendFixedSignedDecimal(DateTimeFieldType.hourOfDay(), 2) - .toFormatter(); - } - return hde; - } - - private static DateTimeFormatter minuteElement() { - if (mhe == null) { - return new DateTimeFormatterBuilder().appendLiteral(':') - // ES change, was .appendMinuteOfHour(2) - .appendFixedSignedDecimal(DateTimeFieldType.minuteOfHour(), 2) - .toFormatter(); - } - return mhe; - } - - private static DateTimeFormatter secondElement() { - if (sme == null) { - return new DateTimeFormatterBuilder().appendLiteral(':') - // ES change, was .appendSecondOfMinute(2) - .appendFixedSignedDecimal(DateTimeFieldType.secondOfMinute(), 2) - .toFormatter(); - } - return sme; - } - - private static DateTimeFormatter fractionElement() { - if (fse == null) { - return new DateTimeFormatterBuilder().appendLiteral('.') - // Support parsing up to nanosecond precision even though - // those extra digits will be dropped. - .appendFractionOfSecond(3, 9) - .toFormatter(); - } - return fse; - } - - private static DateTimeFormatter offsetElement() { - if (ze == null) { - return new DateTimeFormatterBuilder().appendTimeZoneOffset("Z", true, 2, 4).toFormatter(); - } - return ze; - } - - } - -} diff --git a/server/src/main/java/org/opensearch/bootstrap/Security.java b/server/src/main/java/org/opensearch/bootstrap/Security.java index 749c146de4f16..1a17ffcc449c8 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Security.java +++ b/server/src/main/java/org/opensearch/bootstrap/Security.java @@ -222,7 +222,7 @@ static Map getPluginPermissions(Environment environment) throws * Reads and returns the specified {@code policyFile}. *

* Jar files listed in {@code codebases} location will be provided to the policy file via - * a system property of the short name: e.g. ${codebase.joda-convert-1.2.jar} + * a system property of the short name: e.g. ${codebase.some-dependency-1.2.jar} * would map to full URL. */ @SuppressForbidden(reason = "accesses fully qualified URLs to configure security") diff --git a/server/src/main/java/org/opensearch/common/joda/Joda.java b/server/src/main/java/org/opensearch/common/joda/Joda.java deleted file mode 100644 index 8b466e01b15c7..0000000000000 --- a/server/src/main/java/org/opensearch/common/joda/Joda.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.common.logging.DeprecationLogger; -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.FormatNames; -import org.opensearch.common.util.LazyInitializable; -import org.opensearch.core.common.Strings; -import org.opensearch.core.common.io.stream.StreamInput; -import org.opensearch.core.common.io.stream.StreamOutput; -import org.joda.time.Chronology; -import org.joda.time.DateTime; -import org.joda.time.DateTimeField; -import org.joda.time.DateTimeFieldType; -import org.joda.time.DateTimeZone; -import org.joda.time.DurationField; -import org.joda.time.DurationFieldType; -import org.joda.time.ReadablePartial; -import org.joda.time.field.DividedDateTimeField; -import org.joda.time.field.OffsetDateTimeField; -import org.joda.time.field.ScaledDurationField; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.DateTimeFormatterBuilder; -import org.joda.time.format.DateTimeParser; -import org.joda.time.format.DateTimeParserBucket; -import org.joda.time.format.DateTimePrinter; -import org.joda.time.format.ISODateTimeFormat; -import org.joda.time.format.StrictISODateTimeFormat; - -import java.io.IOException; -import java.io.Writer; -import java.math.BigDecimal; -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * Joda class. - * - * @deprecated - * - * @opensearch.internal - */ -@Deprecated -public class Joda { - // Joda.forPattern could be used even before the logging is initialized. - // If LogManager.getLogger is called before logging config is loaded - // it results in errors sent to status logger and startup to fail. - // Hence a lazy initialization. - private static final LazyInitializable deprecationLogger = new LazyInitializable( - () -> DeprecationLogger.getLogger(Joda.class) - ); - - /** - * Parses a joda based pattern, including some named ones (similar to the built in Joda ISO ones). - */ - public static JodaDateFormatter forPattern(String input) { - if (Strings.hasLength(input)) { - input = input.trim(); - } - if (input == null || input.length() == 0) { - throw new IllegalArgumentException("No date pattern provided"); - } - - FormatNames formatName = FormatNames.forName(input); - if (formatName != null && formatName.isCamelCase(input)) { - String msg = "Camel case format name {} is deprecated and will be removed in a future version. " - + "Use snake case name {} instead."; - getDeprecationLogger().deprecate( - "camelCaseDateFormat_" + formatName.getCamelCaseName(), - msg, - formatName.getCamelCaseName(), - formatName.getSnakeCaseName() - ); - } - - DateTimeFormatter formatter; - if (FormatNames.BASIC_DATE.matches(input)) { - formatter = ISODateTimeFormat.basicDate(); - } else if (FormatNames.BASIC_DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.basicDateTime(); - } else if (FormatNames.BASIC_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.basicDateTimeNoMillis(); - } else if (FormatNames.BASIC_ORDINAL_DATE.matches(input)) { - formatter = ISODateTimeFormat.basicOrdinalDate(); - } else if (FormatNames.BASIC_ORDINAL_DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.basicOrdinalDateTime(); - } else if (FormatNames.BASIC_ORDINAL_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.basicOrdinalDateTimeNoMillis(); - } else if (FormatNames.BASIC_TIME.matches(input)) { - formatter = ISODateTimeFormat.basicTime(); - } else if (FormatNames.BASIC_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.basicTimeNoMillis(); - } else if (FormatNames.BASIC_T_TIME.matches(input)) { - formatter = ISODateTimeFormat.basicTTime(); - } else if (FormatNames.BASIC_T_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.basicTTimeNoMillis(); - } else if (FormatNames.BASIC_WEEK_DATE.matches(input)) { - formatter = ISODateTimeFormat.basicWeekDate(); - } else if (FormatNames.BASIC_WEEK_DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.basicWeekDateTime(); - } else if (FormatNames.BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.basicWeekDateTimeNoMillis(); - } else if (FormatNames.DATE.matches(input)) { - formatter = ISODateTimeFormat.date(); - } else if (FormatNames.DATE_HOUR.matches(input)) { - formatter = ISODateTimeFormat.dateHour(); - } else if (FormatNames.DATE_HOUR_MINUTE.matches(input)) { - formatter = ISODateTimeFormat.dateHourMinute(); - } else if (FormatNames.DATE_HOUR_MINUTE_SECOND.matches(input)) { - formatter = ISODateTimeFormat.dateHourMinuteSecond(); - } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { - formatter = ISODateTimeFormat.dateHourMinuteSecondFraction(); - } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.dateHourMinuteSecondMillis(); - } else if (FormatNames.DATE_OPTIONAL_TIME.matches(input)) { - // in this case, we have a separate parser and printer since the dataOptionalTimeParser can't print - // this sucks we should use the root local by default and not be dependent on the node - return new JodaDateFormatter( - input, - ISODateTimeFormat.dateOptionalTimeParser().withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970), - ISODateTimeFormat.dateTime().withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970) - ); - } else if (FormatNames.DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.dateTime(); - } else if (FormatNames.DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.dateTimeNoMillis(); - } else if (FormatNames.HOUR.matches(input)) { - formatter = ISODateTimeFormat.hour(); - } else if (FormatNames.HOUR_MINUTE.matches(input)) { - formatter = ISODateTimeFormat.hourMinute(); - } else if (FormatNames.HOUR_MINUTE_SECOND.matches(input)) { - formatter = ISODateTimeFormat.hourMinuteSecond(); - } else if (FormatNames.HOUR_MINUTE_SECOND_FRACTION.matches(input)) { - formatter = ISODateTimeFormat.hourMinuteSecondFraction(); - } else if (FormatNames.HOUR_MINUTE_SECOND_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.hourMinuteSecondMillis(); - } else if (FormatNames.ORDINAL_DATE.matches(input)) { - formatter = ISODateTimeFormat.ordinalDate(); - } else if (FormatNames.ORDINAL_DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.ordinalDateTime(); - } else if (FormatNames.ORDINAL_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.ordinalDateTimeNoMillis(); - } else if (FormatNames.TIME.matches(input)) { - formatter = ISODateTimeFormat.time(); - } else if (FormatNames.TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.timeNoMillis(); - } else if (FormatNames.T_TIME.matches(input)) { - formatter = ISODateTimeFormat.tTime(); - } else if (FormatNames.T_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.tTimeNoMillis(); - } else if (FormatNames.WEEK_DATE.matches(input)) { - formatter = ISODateTimeFormat.weekDate(); - } else if (FormatNames.WEEK_DATE_TIME.matches(input)) { - formatter = ISODateTimeFormat.weekDateTime(); - } else if (FormatNames.WEEK_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = ISODateTimeFormat.weekDateTimeNoMillis(); - } else if (FormatNames.WEEKYEAR.matches(input)) { - getDeprecationLogger().deprecate( - "week_year_format_name", - "Format name \"week_year\" is deprecated and will be removed in a future version. " + "Use \"weekyear\" format instead" - ); - formatter = ISODateTimeFormat.weekyear(); - } else if (FormatNames.WEEK_YEAR.matches(input)) { - formatter = ISODateTimeFormat.weekyear(); - } else if (FormatNames.WEEK_YEAR_WEEK.matches(input)) { - formatter = ISODateTimeFormat.weekyearWeek(); - } else if (FormatNames.WEEKYEAR_WEEK_DAY.matches(input)) { - formatter = ISODateTimeFormat.weekyearWeekDay(); - } else if (FormatNames.YEAR.matches(input)) { - formatter = ISODateTimeFormat.year(); - } else if (FormatNames.YEAR_MONTH.matches(input)) { - formatter = ISODateTimeFormat.yearMonth(); - } else if (FormatNames.YEAR_MONTH_DAY.matches(input)) { - formatter = ISODateTimeFormat.yearMonthDay(); - } else if (FormatNames.EPOCH_SECOND.matches(input)) { - formatter = new DateTimeFormatterBuilder().append(new EpochTimePrinter(false), new EpochTimeParser(false)).toFormatter(); - } else if (FormatNames.EPOCH_MILLIS.matches(input)) { - formatter = new DateTimeFormatterBuilder().append(new EpochTimePrinter(true), new EpochTimeParser(true)).toFormatter(); - // strict date formats here, must be at least 4 digits for year and two for months and two for day - } else if (FormatNames.STRICT_BASIC_WEEK_DATE.matches(input)) { - formatter = StrictISODateTimeFormat.basicWeekDate(); - } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.basicWeekDateTime(); - } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.basicWeekDateTimeNoMillis(); - } else if (FormatNames.STRICT_DATE.matches(input)) { - formatter = StrictISODateTimeFormat.date(); - } else if (FormatNames.STRICT_DATE_HOUR.matches(input)) { - formatter = StrictISODateTimeFormat.dateHour(); - } else if (FormatNames.STRICT_DATE_HOUR_MINUTE.matches(input)) { - formatter = StrictISODateTimeFormat.dateHourMinute(); - } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND.matches(input)) { - formatter = StrictISODateTimeFormat.dateHourMinuteSecond(); - } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { - formatter = StrictISODateTimeFormat.dateHourMinuteSecondFraction(); - } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.dateHourMinuteSecondMillis(); - } else if (FormatNames.STRICT_DATE_OPTIONAL_TIME.matches(input)) { - // in this case, we have a separate parser and printer since the dataOptionalTimeParser can't print - // this sucks we should use the root local by default and not be dependent on the node - return new JodaDateFormatter( - input, - StrictISODateTimeFormat.dateOptionalTimeParser().withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970), - StrictISODateTimeFormat.dateTime().withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970) - ); - } else if (FormatNames.STRICT_DATE_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.dateTime(); - } else if (FormatNames.STRICT_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.dateTimeNoMillis(); - } else if (FormatNames.STRICT_HOUR.matches(input)) { - formatter = StrictISODateTimeFormat.hour(); - } else if (FormatNames.STRICT_HOUR_MINUTE.matches(input)) { - formatter = StrictISODateTimeFormat.hourMinute(); - } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND.matches(input)) { - formatter = StrictISODateTimeFormat.hourMinuteSecond(); - } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { - formatter = StrictISODateTimeFormat.hourMinuteSecondFraction(); - } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.hourMinuteSecondMillis(); - } else if (FormatNames.STRICT_ORDINAL_DATE.matches(input)) { - formatter = StrictISODateTimeFormat.ordinalDate(); - } else if (FormatNames.STRICT_ORDINAL_DATE_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.ordinalDateTime(); - } else if (FormatNames.STRICT_ORDINAL_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.ordinalDateTimeNoMillis(); - } else if (FormatNames.STRICT_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.time(); - } else if (FormatNames.STRICT_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.timeNoMillis(); - } else if (FormatNames.STRICT_T_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.tTime(); - } else if (FormatNames.STRICT_T_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.tTimeNoMillis(); - } else if (FormatNames.STRICT_WEEK_DATE.matches(input)) { - formatter = StrictISODateTimeFormat.weekDate(); - } else if (FormatNames.STRICT_WEEK_DATE_TIME.matches(input)) { - formatter = StrictISODateTimeFormat.weekDateTime(); - } else if (FormatNames.STRICT_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { - formatter = StrictISODateTimeFormat.weekDateTimeNoMillis(); - } else if (FormatNames.STRICT_WEEKYEAR.matches(input)) { - formatter = StrictISODateTimeFormat.weekyear(); - } else if (FormatNames.STRICT_WEEKYEAR_WEEK.matches(input)) { - formatter = StrictISODateTimeFormat.weekyearWeek(); - } else if (FormatNames.STRICT_WEEKYEAR_WEEK_DAY.matches(input)) { - formatter = StrictISODateTimeFormat.weekyearWeekDay(); - } else if (FormatNames.STRICT_YEAR.matches(input)) { - formatter = StrictISODateTimeFormat.year(); - } else if (FormatNames.STRICT_YEAR_MONTH.matches(input)) { - formatter = StrictISODateTimeFormat.yearMonth(); - } else if (FormatNames.STRICT_YEAR_MONTH_DAY.matches(input)) { - formatter = StrictISODateTimeFormat.yearMonthDay(); - } else if (Strings.hasLength(input) && input.contains("||")) { - String[] formats = Strings.delimitedListToStringArray(input, "||"); - DateTimeParser[] parsers = new DateTimeParser[formats.length]; - - if (formats.length == 1) { - formatter = forPattern(input).parser; - } else { - DateTimeFormatter dateTimeFormatter = null; - for (int i = 0; i < formats.length; i++) { - JodaDateFormatter currentFormatter = forPattern(formats[i]); - DateTimeFormatter currentParser = currentFormatter.parser; - if (dateTimeFormatter == null) { - dateTimeFormatter = currentFormatter.printer; - } - parsers[i] = currentParser.getParser(); - } - - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append( - dateTimeFormatter.withZone(DateTimeZone.UTC).getPrinter(), - parsers - ); - formatter = builder.toFormatter(); - } - } else { - try { - maybeLogJodaDeprecation(input); - formatter = DateTimeFormat.forPattern(input); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid format: [" + input + "]: " + e.getMessage(), e); - } - } - - formatter = formatter.withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970); - return new JodaDateFormatter(input, formatter, formatter); - } - - public static void writeTimeZone(final StreamOutput out, final DateTimeZone timeZone) throws IOException { - out.writeString(timeZone.getID()); - } - - public static void writeOptionalTimeZone(final StreamOutput out, final DateTimeZone timeZone) throws IOException { - if (timeZone == null) { - out.writeBoolean(false); - } else { - out.writeBoolean(true); - writeTimeZone(out, timeZone); - } - } - - /** - * Read a {@linkplain DateTimeZone} from a {@linkplain StreamInput}. - */ - public static DateTimeZone readTimeZone(final StreamInput in) throws IOException { - return DateTimeZone.forID(in.readString()); - } - - /** - * Read an optional {@linkplain DateTimeZone}. - */ - public static DateTimeZone readOptionalTimeZone(final StreamInput in) throws IOException { - if (in.readBoolean()) { - return DateTimeZone.forID(in.readString()); - } - return null; - } - - private static void maybeLogJodaDeprecation(String format) { - if (JodaDeprecationPatterns.isDeprecatedPattern(format)) { - String suggestion = JodaDeprecationPatterns.formatSuggestion(format); - getDeprecationLogger().deprecate( - "joda-pattern-deprecation", - suggestion + " " + JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS - ); - } - } - - public static DateFormatter getStrictStandardDateFormatter() { - // 2014/10/10 - DateTimeFormatter shortFormatter = new DateTimeFormatterBuilder().appendFixedDecimal(DateTimeFieldType.year(), 4) - .appendLiteral('/') - .appendFixedDecimal(DateTimeFieldType.monthOfYear(), 2) - .appendLiteral('/') - .appendFixedDecimal(DateTimeFieldType.dayOfMonth(), 2) - .toFormatter() - .withZoneUTC(); - - // 2014/10/10 12:12:12 - DateTimeFormatter longFormatter = new DateTimeFormatterBuilder().appendFixedDecimal(DateTimeFieldType.year(), 4) - .appendLiteral('/') - .appendFixedDecimal(DateTimeFieldType.monthOfYear(), 2) - .appendLiteral('/') - .appendFixedDecimal(DateTimeFieldType.dayOfMonth(), 2) - .appendLiteral(' ') - .appendFixedSignedDecimal(DateTimeFieldType.hourOfDay(), 2) - .appendLiteral(':') - .appendFixedSignedDecimal(DateTimeFieldType.minuteOfHour(), 2) - .appendLiteral(':') - .appendFixedSignedDecimal(DateTimeFieldType.secondOfMinute(), 2) - .toFormatter() - .withZoneUTC(); - - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().append( - longFormatter.withZone(DateTimeZone.UTC).getPrinter(), - new DateTimeParser[] { longFormatter.getParser(), shortFormatter.getParser(), new EpochTimeParser(true) } - ); - - DateTimeFormatter formatter = builder.toFormatter().withLocale(Locale.ROOT).withZone(DateTimeZone.UTC).withDefaultYear(1970); - return new JodaDateFormatter("yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis", formatter, formatter); - } - - public static final DurationFieldType Quarters = new DurationFieldType("quarters") { - @Override - public DurationField getField(Chronology chronology) { - return new ScaledDurationField(chronology.months(), Quarters, 3); - } - }; - - public static final DateTimeFieldType QuarterOfYear = new DateTimeFieldType("quarterOfYear") { - @Override - public DurationFieldType getDurationType() { - return Quarters; - } - - @Override - public DurationFieldType getRangeDurationType() { - return DurationFieldType.years(); - } - - @Override - public DateTimeField getField(Chronology chronology) { - return new OffsetDateTimeField( - new DividedDateTimeField(new OffsetDateTimeField(chronology.monthOfYear(), -1), QuarterOfYear, 3), - 1 - ); - } - }; - - /** - * parses epcoch timers - * - * @opensearch.internal - */ - public static class EpochTimeParser implements DateTimeParser { - - private static final Pattern scientificNotation = Pattern.compile("[Ee]"); - - private final boolean hasMilliSecondPrecision; - - public EpochTimeParser(boolean hasMilliSecondPrecision) { - this.hasMilliSecondPrecision = hasMilliSecondPrecision; - } - - @Override - public int estimateParsedLength() { - return hasMilliSecondPrecision ? 19 : 16; - } - - @Override - public int parseInto(DateTimeParserBucket bucket, String text, int position) { - boolean isPositive = text.startsWith("-") == false; - int firstDotIndex = text.indexOf('.'); - boolean isTooLong = (firstDotIndex == -1 ? text.length() : firstDotIndex) > estimateParsedLength(); - - if (bucket.getZone() != DateTimeZone.UTC) { - String format = hasMilliSecondPrecision ? "epoch_millis" : "epoch_second"; - throw new IllegalArgumentException("time_zone must be UTC for format [" + format + "]"); - } else if (isPositive && isTooLong) { - return -1; - } - - int factor = hasMilliSecondPrecision ? 1 : 1000; - try { - long millis = new BigDecimal(text).longValue() * factor; - // check for deprecations, but after it has parsed correctly so invalid values aren't counted as deprecated - if (millis < 0) { - getDeprecationLogger().deprecate( - "epoch-negative", - "Use of negative values" - + " in epoch time formats is deprecated and will not be supported in the next major version of OpenSearch." - ); - } - if (scientificNotation.matcher(text).find()) { - getDeprecationLogger().deprecate( - "epoch-scientific-notation", - "Use of scientific notation" - + " in epoch time formats is deprecated and will not be supported in the next major version of OpenSearch." - ); - } - DateTime dt = new DateTime(millis, DateTimeZone.UTC); - bucket.saveField(DateTimeFieldType.year(), dt.getYear()); - bucket.saveField(DateTimeFieldType.monthOfYear(), dt.getMonthOfYear()); - bucket.saveField(DateTimeFieldType.dayOfMonth(), dt.getDayOfMonth()); - bucket.saveField(DateTimeFieldType.hourOfDay(), dt.getHourOfDay()); - bucket.saveField(DateTimeFieldType.minuteOfHour(), dt.getMinuteOfHour()); - bucket.saveField(DateTimeFieldType.secondOfMinute(), dt.getSecondOfMinute()); - bucket.saveField(DateTimeFieldType.millisOfSecond(), dt.getMillisOfSecond()); - bucket.setZone(DateTimeZone.UTC); - } catch (Exception e) { - return -1; - } - return text.length(); - } - } - - private static DeprecationLogger getDeprecationLogger() { - return deprecationLogger.getOrCompute(); - } - - /** - * Epoch timer printer - * - * @opensearch.internal - */ - public static class EpochTimePrinter implements DateTimePrinter { - - private boolean hasMilliSecondPrecision; - - public EpochTimePrinter(boolean hasMilliSecondPrecision) { - this.hasMilliSecondPrecision = hasMilliSecondPrecision; - } - - @Override - public int estimatePrintedLength() { - return hasMilliSecondPrecision ? 19 : 16; - } - - /** - * We adjust the instant by displayOffset to adjust for the offset that might have been added in - * {@link DateTimeFormatter#printTo(Appendable, long, Chronology)} when using a time zone. - */ - @Override - public void printTo(StringBuffer buf, long instant, Chronology chrono, int displayOffset, DateTimeZone displayZone, Locale locale) { - if (hasMilliSecondPrecision) { - buf.append(instant - displayOffset); - } else { - buf.append((instant - displayOffset) / 1000); - } - } - - /** - * We adjust the instant by displayOffset to adjust for the offset that might have been added in - * {@link DateTimeFormatter#printTo(Appendable, long, Chronology)} when using a time zone. - */ - @Override - public void printTo(Writer out, long instant, Chronology chrono, int displayOffset, DateTimeZone displayZone, Locale locale) - throws IOException { - if (hasMilliSecondPrecision) { - out.write(String.valueOf(instant - displayOffset)); - } else { - out.append(String.valueOf((instant - displayOffset) / 1000)); - } - } - - @Override - public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) { - if (hasMilliSecondPrecision) { - buf.append(String.valueOf(getDateTimeMillis(partial))); - } else { - buf.append(String.valueOf(getDateTimeMillis(partial) / 1000)); - } - } - - @Override - public void printTo(Writer out, ReadablePartial partial, Locale locale) throws IOException { - if (hasMilliSecondPrecision) { - out.append(String.valueOf(getDateTimeMillis(partial))); - } else { - out.append(String.valueOf(getDateTimeMillis(partial) / 1000)); - } - } - - private long getDateTimeMillis(ReadablePartial partial) { - int year = partial.get(DateTimeFieldType.year()); - int monthOfYear = partial.get(DateTimeFieldType.monthOfYear()); - int dayOfMonth = partial.get(DateTimeFieldType.dayOfMonth()); - int hourOfDay = partial.get(DateTimeFieldType.hourOfDay()); - int minuteOfHour = partial.get(DateTimeFieldType.minuteOfHour()); - int secondOfMinute = partial.get(DateTimeFieldType.secondOfMinute()); - int millisOfSecond = partial.get(DateTimeFieldType.millisOfSecond()); - return partial.getChronology() - .getDateTimeMillis(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond); - } - } -} diff --git a/server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java b/server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java deleted file mode 100644 index bf25e5b1b3923..0000000000000 --- a/server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.DateMathParser; -import org.opensearch.common.time.DateUtils; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormatter; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.TemporalAccessor; -import java.util.Locale; -import java.util.Objects; - -/** - * Joda date formatter. - * - * @opensearch.internal - */ -public class JodaDateFormatter implements DateFormatter { - - final String pattern; - final DateTimeFormatter parser; - final DateTimeFormatter printer; - - JodaDateFormatter(String pattern, DateTimeFormatter parser, DateTimeFormatter printer) { - this.pattern = pattern; - this.printer = printer; - this.parser = parser; - } - - @Override - public TemporalAccessor parse(String input) { - final DateTime dt = parser.parseDateTime(input); - return ZonedDateTime.ofInstant(Instant.ofEpochMilli(dt.getMillis()), DateUtils.dateTimeZoneToZoneId(dt.getZone())); - } - - public long parseMillis(String input) { - return parser.parseMillis(input); - } - - public DateTime parseJoda(String input) { - return parser.parseDateTime(input); - } - - @Override - public DateFormatter withZone(ZoneId zoneId) { - DateTimeZone timeZone = DateUtils.zoneIdToDateTimeZone(zoneId); - if (parser.getZone().equals(timeZone)) { - return this; - } - DateTimeFormatter parser = this.parser.withZone(timeZone); - DateTimeFormatter printer = this.printer.withZone(timeZone); - return new JodaDateFormatter(pattern, parser, printer); - } - - @Override - public DateFormatter withLocale(Locale locale) { - if (parser.getLocale().equals(locale)) { - return this; - } - DateTimeFormatter parser = this.parser.withLocale(locale); - DateTimeFormatter printer = this.printer.withLocale(locale); - return new JodaDateFormatter(pattern, parser, printer); - } - - @Override - public String format(TemporalAccessor accessor) { - DateTimeZone timeZone = DateUtils.zoneIdToDateTimeZone(ZoneId.from(accessor)); - DateTime dateTime = new DateTime(Instant.from(accessor).toEpochMilli(), timeZone); - return printer.print(dateTime); - } - - public String formatJoda(DateTime dateTime) { - return printer.print(dateTime); - } - - public String formatMillis(long millis) { - return printer.print(millis); - } - - public JodaDateFormatter withYear(int year) { - if (parser.getDefaultYear() == year) { - return this; - } - return new JodaDateFormatter(pattern, parser.withDefaultYear(year), printer.withDefaultYear(year)); - } - - @Override - public String pattern() { - return pattern; - } - - @Override - public String printPattern() { - throw new UnsupportedOperationException("JodaDateFormatter does not have a print pattern"); - } - - @Override - public Locale locale() { - return printer.getLocale(); - } - - @Override - public ZoneId zone() { - return DateUtils.dateTimeZoneToZoneId(printer.getZone()); - } - - @Override - public DateMathParser toDateMathParser() { - return new JodaDateMathParser(this); - } - - @Override - public int hashCode() { - return Objects.hash(locale(), zone(), pattern()); - } - - @Override - public boolean equals(Object obj) { - if (obj.getClass().equals(this.getClass()) == false) { - return false; - } - JodaDateFormatter other = (JodaDateFormatter) obj; - - return Objects.equals(pattern(), other.pattern()) - && Objects.equals(locale(), other.locale()) - && Objects.equals(zone(), other.zone()); - } -} diff --git a/server/src/main/java/org/opensearch/common/joda/JodaDateMathParser.java b/server/src/main/java/org/opensearch/common/joda/JodaDateMathParser.java deleted file mode 100644 index ae38e9a6a8073..0000000000000 --- a/server/src/main/java/org/opensearch/common/joda/JodaDateMathParser.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.OpenSearchParseException; -import org.opensearch.common.time.DateMathParser; -import org.opensearch.common.time.DateUtils; -import org.joda.time.DateTimeZone; -import org.joda.time.MutableDateTime; -import org.joda.time.format.DateTimeFormatter; - -import java.time.Instant; -import java.time.ZoneId; -import java.util.Objects; -import java.util.function.LongSupplier; - -/** - * A parser for date/time formatted text with optional date math. - *

- * The format of the datetime is configurable, and unix timestamps can also be used. Datemath - * is appended to a datetime with the following syntax: - * ||[+-/](\d+)?[yMwdhHms]. - * - * @opensearch.internal - */ -public class JodaDateMathParser implements DateMathParser { - - private final JodaDateFormatter dateTimeFormatter; - - public JodaDateMathParser(JodaDateFormatter dateTimeFormatter) { - Objects.requireNonNull(dateTimeFormatter); - this.dateTimeFormatter = dateTimeFormatter; - } - - // Note: we take a callable here for the timestamp in order to be able to figure out - // if it has been used. For instance, the request cache does not cache requests that make - // use of `now`. - @Override - public Instant parse(String text, LongSupplier now, boolean roundUp, ZoneId tz) { - final DateTimeZone timeZone = tz == null ? null : DateUtils.zoneIdToDateTimeZone(tz); - long time; - String mathString; - if (text.startsWith("now")) { - try { - time = now.getAsLong(); - } catch (Exception e) { - throw new OpenSearchParseException("could not read the current timestamp", e); - } - mathString = text.substring("now".length()); - } else { - int index = text.indexOf("||"); - if (index == -1) { - return Instant.ofEpochMilli(parseDateTime(text, timeZone, roundUp)); - } - time = parseDateTime(text.substring(0, index), timeZone, false); - mathString = text.substring(index + 2); - } - - return Instant.ofEpochMilli(parseMath(mathString, time, roundUp, timeZone)); - } - - private long parseMath(String mathString, long time, boolean roundUp, DateTimeZone timeZone) throws OpenSearchParseException { - if (timeZone == null) { - timeZone = DateTimeZone.UTC; - } - MutableDateTime dateTime = new MutableDateTime(time, timeZone); - for (int i = 0; i < mathString.length();) { - char c = mathString.charAt(i++); - final boolean round; - final int sign; - if (c == '/') { - round = true; - sign = 1; - } else { - round = false; - if (c == '+') { - sign = 1; - } else if (c == '-') { - sign = -1; - } else { - throw new OpenSearchParseException("operator not supported for date math [{}]", mathString); - } - } - - if (i >= mathString.length()) { - throw new OpenSearchParseException("truncated date math [{}]", mathString); - } - - final int num; - if (!Character.isDigit(mathString.charAt(i))) { - num = 1; - } else { - int numFrom = i; - while (i < mathString.length() && Character.isDigit(mathString.charAt(i))) { - i++; - } - if (i >= mathString.length()) { - throw new OpenSearchParseException("truncated date math [{}]", mathString); - } - num = Integer.parseInt(mathString.substring(numFrom, i)); - } - if (round) { - if (num != 1) { - throw new OpenSearchParseException("rounding `/` can only be used on single unit types [{}]", mathString); - } - } - char unit = mathString.charAt(i++); - MutableDateTime.Property propertyToRound = null; - switch (unit) { - case 'y': - if (round) { - propertyToRound = dateTime.yearOfCentury(); - } else { - dateTime.addYears(sign * num); - } - break; - case 'M': - if (round) { - propertyToRound = dateTime.monthOfYear(); - } else { - dateTime.addMonths(sign * num); - } - break; - case 'w': - if (round) { - propertyToRound = dateTime.weekOfWeekyear(); - } else { - dateTime.addWeeks(sign * num); - } - break; - case 'd': - if (round) { - propertyToRound = dateTime.dayOfMonth(); - } else { - dateTime.addDays(sign * num); - } - break; - case 'h': - case 'H': - if (round) { - propertyToRound = dateTime.hourOfDay(); - } else { - dateTime.addHours(sign * num); - } - break; - case 'm': - if (round) { - propertyToRound = dateTime.minuteOfHour(); - } else { - dateTime.addMinutes(sign * num); - } - break; - case 's': - if (round) { - propertyToRound = dateTime.secondOfMinute(); - } else { - dateTime.addSeconds(sign * num); - } - break; - default: - throw new OpenSearchParseException("unit [{}] not supported for date math [{}]", unit, mathString); - } - if (propertyToRound != null) { - if (roundUp) { - // we want to go up to the next whole value, even if we are already on a rounded value - propertyToRound.add(1); - propertyToRound.roundFloor(); - dateTime.addMillis(-1); // subtract 1 millisecond to get the largest inclusive value - } else { - propertyToRound.roundFloor(); - } - } - } - return dateTime.getMillis(); - } - - private long parseDateTime(String value, DateTimeZone timeZone, boolean roundUpIfNoTime) { - DateTimeFormatter parser = dateTimeFormatter.parser; - if (timeZone != null) { - parser = parser.withZone(timeZone); - } - try { - MutableDateTime date; - // We use 01/01/1970 as a base date so that things keep working with date - // fields that are filled with times without dates - if (roundUpIfNoTime) { - date = new MutableDateTime(1970, 1, 1, 23, 59, 59, 999, DateTimeZone.UTC); - } else { - date = new MutableDateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeZone.UTC); - } - final int end = parser.parseInto(date, value, 0); - if (end < 0) { - int position = ~end; - throw new IllegalArgumentException("Parse failure at index [" + position + "] of [" + value + "]"); - } else if (end != value.length()) { - throw new IllegalArgumentException("Unrecognized chars at the end of [" + value + "]: [" + value.substring(end) + "]"); - } - return date.getMillis(); - } catch (IllegalArgumentException e) { - throw new OpenSearchParseException("failed to parse date field [{}] with format [{}]", e, value, dateTimeFormatter.pattern()); - } - } - -} diff --git a/server/src/main/java/org/opensearch/common/joda/JodaDeprecationPatterns.java b/server/src/main/java/org/opensearch/common/joda/JodaDeprecationPatterns.java deleted file mode 100644 index efb6362ab63aa..0000000000000 --- a/server/src/main/java/org/opensearch/common/joda/JodaDeprecationPatterns.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.FormatNames; - -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Deprecated patters for joda date/time - * - * @opensearch.internal - */ -public class JodaDeprecationPatterns { - public static final String USE_NEW_FORMAT_SPECIFIERS = "Use new java.time date format specifiers."; - private static Map JODA_PATTERNS_DEPRECATIONS = new LinkedHashMap<>(); - - static { - JODA_PATTERNS_DEPRECATIONS.put("Y", "'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year."); - JODA_PATTERNS_DEPRECATIONS.put("y", "'y' year should be replaced with 'u'. Use 'y' for year-of-era."); - JODA_PATTERNS_DEPRECATIONS.put("C", "'C' century of era is no longer supported."); - JODA_PATTERNS_DEPRECATIONS.put("x", "'x' weak-year should be replaced with 'Y'. Use 'x' for zone-offset."); - JODA_PATTERNS_DEPRECATIONS.put("Z", "'Z' time zone offset/id fails when parsing 'Z' for Zulu timezone. Consider using 'X'."); - JODA_PATTERNS_DEPRECATIONS.put("z", "'z' time zone text. Will print 'Z' for Zulu given UTC timezone."); - } - - /** - * Checks if date parsing pattern is deprecated. - * Deprecated here means: when it was not already prefixed with 8 (meaning already upgraded) - * and it is not a predefined pattern from FormatNames like basic_date_time_no_millis - * and it uses pattern characters which changed meaning from joda to java like Y becomes y. - * @param pattern - a format to be checked - * @return true if format is deprecated, otherwise false - */ - public static boolean isDeprecatedPattern(String pattern) { - List patterns = DateFormatter.splitCombinedPatterns(pattern); - - for (String subPattern : patterns) { - boolean isDeprecated = subPattern.startsWith("8") == false - && FormatNames.exist(subPattern) == false - && JODA_PATTERNS_DEPRECATIONS.keySet().stream().filter(s -> subPattern.contains(s)).findAny().isPresent(); - if (isDeprecated) { - return true; - } - } - return false; - } - - /** - * Formats deprecation message for suggestion field in a warning header. - * Joins all warnings in a one message. - * @param pattern - a pattern to be formatted - * @return a formatted deprecation message - */ - public static String formatSuggestion(String pattern) { - List patterns = DateFormatter.splitCombinedPatterns(pattern); - - Set warnings = new LinkedHashSet<>(); - for (String subPattern : patterns) { - if (isDeprecatedPattern(subPattern)) { - String suggestion = JODA_PATTERNS_DEPRECATIONS.entrySet() - .stream() - .filter(s -> subPattern.contains(s.getKey())) - .map(s -> s.getValue()) - .collect(Collectors.joining("; ")); - warnings.add(suggestion); - } - } - String combinedWarning = warnings.stream().collect(Collectors.joining("; ")); - return combinedWarning; - } -} diff --git a/server/src/main/java/org/opensearch/common/joda/package-info.java b/server/src/main/java/org/opensearch/common/joda/package-info.java deleted file mode 100644 index 55ed8d9592a6d..0000000000000 --- a/server/src/main/java/org/opensearch/common/joda/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/** Base Joda Time package. */ -package org.opensearch.common.joda; diff --git a/server/src/main/java/org/opensearch/common/time/DateFormatter.java b/server/src/main/java/org/opensearch/common/time/DateFormatter.java index c98bd853dfced..66ebca4010e6c 100644 --- a/server/src/main/java/org/opensearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/opensearch/common/time/DateFormatter.java @@ -32,13 +32,12 @@ package org.opensearch.common.time; +import org.opensearch.Version; import org.opensearch.core.common.Strings; -import org.joda.time.DateTime; import java.time.Instant; import java.time.ZoneId; import java.time.ZoneOffset; -import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.util.ArrayList; @@ -69,14 +68,6 @@ default long parseMillis(String input) { return DateFormatters.from(parse(input)).toInstant().toEpochMilli(); } - /** - * Parse the given input into a Joda {@link DateTime}. - */ - default DateTime parseJoda(String input) { - ZonedDateTime dateTime = ZonedDateTime.from(parse(input)); - return new DateTime(dateTime.toInstant().toEpochMilli(), DateUtils.zoneIdToDateTimeZone(dateTime.getZone())); - } - /** * Create a copy of this formatter that is configured to parse dates in the specified time zone * @@ -109,15 +100,6 @@ default String formatMillis(long millis) { return format(Instant.ofEpochMilli(millis).atZone(zone)); } - /** - * Return the given Joda {@link DateTime} formatted with this format. - */ - default String formatJoda(DateTime dateTime) { - return format( - ZonedDateTime.ofInstant(Instant.ofEpochMilli(dateTime.getMillis()), DateUtils.dateTimeZoneToZoneId(dateTime.getZone())) - ); - } - /** * A name based format for this formatter. Can be one of the registered formatters like epoch_millis or * a configured format like HH:mm:ss @@ -156,6 +138,10 @@ default String formatJoda(DateTime dateTime) { DateMathParser toDateMathParser(); static DateFormatter forPattern(String input, String printPattern, Boolean canCacheFormatter) { + return forPattern(input, printPattern, canCacheFormatter, Version.CURRENT); + } + + static DateFormatter forPattern(String input, String printPattern, Boolean canCacheFormatter, final Version supportedVersion) { if (Strings.hasLength(input) == false) { throw new IllegalArgumentException("No date pattern provided"); @@ -164,7 +150,12 @@ static DateFormatter forPattern(String input, String printPattern, Boolean canCa // support the 6.x BWC compatible way of parsing java 8 dates String format = strip8Prefix(input); List patterns = splitCombinedPatterns(format); - List formatters = patterns.stream().map(DateFormatters::forPattern).collect(Collectors.toList()); + List formatters = patterns.stream().map(p -> { + if (supportedVersion.before(Version.V_2_12_0)) { + return LegacyFormatNames.camelCaseToSnakeCase(p); + } + return p; + }).map(DateFormatters::forPattern).collect(Collectors.toList()); DateFormatter printFormatter = formatters.get(0); if (Strings.hasLength(printPattern)) { @@ -179,15 +170,19 @@ static DateFormatter forPattern(String input, String printPattern, Boolean canCa } static DateFormatter forPattern(String input) { - return forPattern(input, null, false); + return forPattern(input, null, false, Version.CURRENT); + } + + static DateFormatter forPattern(final String input, final Version version) { + return forPattern(input, null, false, version); } static DateFormatter forPattern(String input, String printPattern) { - return forPattern(input, printPattern, false); + return forPattern(input, printPattern, false, Version.CURRENT); } static DateFormatter forPattern(String input, Boolean canCacheFormatter) { - return forPattern(input, null, canCacheFormatter); + return forPattern(input, null, canCacheFormatter, Version.CURRENT); } static String strip8Prefix(String input) { diff --git a/server/src/main/java/org/opensearch/common/time/DateFormatters.java b/server/src/main/java/org/opensearch/common/time/DateFormatters.java index e74ab687b903b..e0373a133450e 100644 --- a/server/src/main/java/org/opensearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/opensearch/common/time/DateFormatters.java @@ -242,7 +242,7 @@ public class DateFormatters { /** * Returns a ISO 8601 compatible date time formatter and parser. * This is not fully compatible to the existing spec, which would require far more edge cases, but merely compatible with the - * existing joda time ISO date formatter + * legacy joda time ISO date formatter */ private static final DateFormatter ISO_8601 = new JavaDateFormatter( "iso8601", @@ -1971,19 +1971,6 @@ static DateFormatter forPattern(String input) { throw new IllegalArgumentException("No date pattern provided"); } - FormatNames formatName = FormatNames.forName(input); - if (formatName != null && formatName.isCamelCase(input)) { - String msg = "Camel case format name {} is deprecated and will be removed in a future version. " - + "Use snake case name {} instead."; - deprecationLogger.getOrCompute() - .deprecate( - "camelCaseDateFormat_" + formatName.getCamelCaseName(), - msg, - formatName.getCamelCaseName(), - formatName.getSnakeCaseName() - ); - } - if (FormatNames.ISO8601.matches(input)) { return ISO_8601; } else if (FormatNames.BASIC_DATE.matches(input)) { @@ -2185,7 +2172,7 @@ static DateFormatter forPattern(String input) { * - If no time is given, the start of the day is used * - If no month of the year is found, the first day of the year is used * - If an iso based weekyear is found, but not week is specified, the first monday - * of the new year is chosen (reataining BWC to joda time) + * of the new year is chosen (retaing BWC w/ joda time) * - If an iso based weekyear is found and an iso based weekyear week, the start * of the day is used * diff --git a/server/src/main/java/org/opensearch/common/time/DateMathParser.java b/server/src/main/java/org/opensearch/common/time/DateMathParser.java index 7088d6cb7a498..bf2f8b5210b28 100644 --- a/server/src/main/java/org/opensearch/common/time/DateMathParser.java +++ b/server/src/main/java/org/opensearch/common/time/DateMathParser.java @@ -32,14 +32,14 @@ package org.opensearch.common.time; -import org.joda.time.DateTimeZone; - import java.time.Instant; import java.time.ZoneId; import java.util.function.LongSupplier; /** - * An abstraction over date math parsing to allow different implementation for joda and java time. + * An abstraction over date math parsing. + * + * todo: merge {@link JavaDateMathParser} into this class * * @opensearch.internal */ @@ -49,17 +49,7 @@ public interface DateMathParser { * Parse a date math expression without timezone info and rounding down. */ default Instant parse(String text, LongSupplier now) { - return parse(text, now, false, (ZoneId) null); - } - - // Note: we take a callable here for the timestamp in order to be able to figure out - // if it has been used. For instance, the request cache does not cache requests that make - // use of `now`. - - // exists for backcompat, do not use! - @Deprecated - default Instant parse(String text, LongSupplier now, boolean roundUpProperty, DateTimeZone tz) { - return parse(text, now, roundUpProperty, tz == null ? null : ZoneId.of(tz.getID())); + return parse(text, now, false, null); } /** diff --git a/server/src/main/java/org/opensearch/common/time/DateUtils.java b/server/src/main/java/org/opensearch/common/time/DateUtils.java index 7ab395a1117e7..bb469bcd73f1a 100644 --- a/server/src/main/java/org/opensearch/common/time/DateUtils.java +++ b/server/src/main/java/org/opensearch/common/time/DateUtils.java @@ -33,13 +33,11 @@ package org.opensearch.common.time; import org.opensearch.common.logging.DeprecationLogger; -import org.joda.time.DateTimeZone; import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.time.ZoneId; -import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Collections; import java.util.HashMap; @@ -57,16 +55,6 @@ * @opensearch.internal */ public class DateUtils { - public static DateTimeZone zoneIdToDateTimeZone(ZoneId zoneId) { - if (zoneId == null) { - return null; - } - if (zoneId instanceof ZoneOffset) { - // the id for zoneoffset is not ISO compatible, so cannot be read by ZoneId.of - return DateTimeZone.forOffsetMillis(((ZoneOffset) zoneId).getTotalSeconds() * 1000); - } - return DateTimeZone.forID(zoneId.getId()); - } private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DateUtils.class); // pkg private for tests @@ -204,17 +192,6 @@ public static DateTimeZone zoneIdToDateTimeZone(ZoneId zoneId) { DEPRECATED_LONG_TIMEZONES = Collections.unmodifiableMap(tzs); } - public static ZoneId dateTimeZoneToZoneId(DateTimeZone timeZone) { - if (timeZone == null) { - return null; - } - if (DateTimeZone.UTC.equals(timeZone)) { - return ZoneOffset.UTC; - } - - return of(timeZone.getID()); - } - public static ZoneId of(String zoneId) { String deprecatedId = DEPRECATED_SHORT_TIMEZONES.get(zoneId); if (deprecatedId != null) { diff --git a/server/src/main/java/org/opensearch/common/time/FormatNames.java b/server/src/main/java/org/opensearch/common/time/FormatNames.java index ba0a8fcf4a17a..90730c587112a 100644 --- a/server/src/main/java/org/opensearch/common/time/FormatNames.java +++ b/server/src/main/java/org/opensearch/common/time/FormatNames.java @@ -32,137 +32,106 @@ package org.opensearch.common.time; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - /** * Date format names. * * @opensearch.internal */ public enum FormatNames { - ISO8601(null, "iso8601"), - BASIC_DATE("basicDate", "basic_date"), - BASIC_DATE_TIME("basicDateTime", "basic_date_time"), - BASIC_DATE_TIME_NO_MILLIS("basicDateTimeNoMillis", "basic_date_time_no_millis"), - BASIC_ORDINAL_DATE("basicOrdinalDate", "basic_ordinal_date"), - BASIC_ORDINAL_DATE_TIME("basicOrdinalDateTime", "basic_ordinal_date_time"), - BASIC_ORDINAL_DATE_TIME_NO_MILLIS("basicOrdinalDateTimeNoMillis", "basic_ordinal_date_time_no_millis"), - BASIC_TIME("basicTime", "basic_time"), - BASIC_TIME_NO_MILLIS("basicTimeNoMillis", "basic_time_no_millis"), - BASIC_T_TIME("basicTTime", "basic_t_time"), - BASIC_T_TIME_NO_MILLIS("basicTTimeNoMillis", "basic_t_time_no_millis"), - BASIC_WEEK_DATE("basicWeekDate", "basic_week_date"), - BASIC_WEEK_DATE_TIME("basicWeekDateTime", "basic_week_date_time"), - BASIC_WEEK_DATE_TIME_NO_MILLIS("basicWeekDateTimeNoMillis", "basic_week_date_time_no_millis"), - DATE(null, "date"), - DATE_HOUR("dateHour", "date_hour"), - DATE_HOUR_MINUTE("dateHourMinute", "date_hour_minute"), - DATE_HOUR_MINUTE_SECOND("dateHourMinuteSecond", "date_hour_minute_second"), - DATE_HOUR_MINUTE_SECOND_FRACTION("dateHourMinuteSecondFraction", "date_hour_minute_second_fraction"), - DATE_HOUR_MINUTE_SECOND_MILLIS("dateHourMinuteSecondMillis", "date_hour_minute_second_millis"), - DATE_OPTIONAL_TIME("dateOptionalTime", "date_optional_time"), - DATE_TIME("dateTime", "date_time"), - DATE_TIME_NO_MILLIS("dateTimeNoMillis", "date_time_no_millis"), - HOUR(null, "hour"), - HOUR_MINUTE("hourMinute", "hour_minute"), - HOUR_MINUTE_SECOND("hourMinuteSecond", "hour_minute_second"), - HOUR_MINUTE_SECOND_FRACTION("hourMinuteSecondFraction", "hour_minute_second_fraction"), - HOUR_MINUTE_SECOND_MILLIS("hourMinuteSecondMillis", "hour_minute_second_millis"), - ORDINAL_DATE("ordinalDate", "ordinal_date"), - ORDINAL_DATE_TIME("ordinalDateTime", "ordinal_date_time"), - ORDINAL_DATE_TIME_NO_MILLIS("ordinalDateTimeNoMillis", "ordinal_date_time_no_millis"), - TIME(null, "time"), - TIME_NO_MILLIS("timeNoMillis", "time_no_millis"), - T_TIME("tTime", "t_time"), - T_TIME_NO_MILLIS("tTimeNoMillis", "t_time_no_millis"), - WEEK_DATE("weekDate", "week_date"), - WEEK_DATE_TIME("weekDateTime", "week_date_time"), - WEEK_DATE_TIME_NO_MILLIS("weekDateTimeNoMillis", "week_date_time_no_millis"), - WEEK_YEAR(null, "week_year"), - WEEKYEAR(null, "weekyear"), - WEEK_YEAR_WEEK("weekyearWeek", "weekyear_week"), - WEEKYEAR_WEEK_DAY("weekyearWeekDay", "weekyear_week_day"), - YEAR(null, "year"), - YEAR_MONTH("yearMonth", "year_month"), - YEAR_MONTH_DAY("yearMonthDay", "year_month_day"), - EPOCH_SECOND(null, "epoch_second"), - EPOCH_MILLIS(null, "epoch_millis"), + ISO8601("iso8601"), + BASIC_DATE("basic_date"), + BASIC_DATE_TIME("basic_date_time"), + BASIC_DATE_TIME_NO_MILLIS("basic_date_time_no_millis"), + BASIC_ORDINAL_DATE("basic_ordinal_date"), + BASIC_ORDINAL_DATE_TIME("basic_ordinal_date_time"), + BASIC_ORDINAL_DATE_TIME_NO_MILLIS("basic_ordinal_date_time_no_millis"), + BASIC_TIME("basic_time"), + BASIC_TIME_NO_MILLIS("basic_time_no_millis"), + BASIC_T_TIME("basic_t_time"), + BASIC_T_TIME_NO_MILLIS("basic_t_time_no_millis"), + BASIC_WEEK_DATE("basic_week_date"), + BASIC_WEEK_DATE_TIME("basic_week_date_time"), + BASIC_WEEK_DATE_TIME_NO_MILLIS("basic_week_date_time_no_millis"), + DATE("date"), + DATE_HOUR("date_hour"), + DATE_HOUR_MINUTE("date_hour_minute"), + DATE_HOUR_MINUTE_SECOND("date_hour_minute_second"), + DATE_HOUR_MINUTE_SECOND_FRACTION("date_hour_minute_second_fraction"), + DATE_HOUR_MINUTE_SECOND_MILLIS("date_hour_minute_second_millis"), + DATE_OPTIONAL_TIME("date_optional_time"), + DATE_TIME("date_time"), + DATE_TIME_NO_MILLIS("date_time_no_millis"), + HOUR("hour"), + HOUR_MINUTE("hour_minute"), + HOUR_MINUTE_SECOND("hour_minute_second"), + HOUR_MINUTE_SECOND_FRACTION("hour_minute_second_fraction"), + HOUR_MINUTE_SECOND_MILLIS("hour_minute_second_millis"), + ORDINAL_DATE("ordinal_date"), + ORDINAL_DATE_TIME("ordinal_date_time"), + ORDINAL_DATE_TIME_NO_MILLIS("ordinal_date_time_no_millis"), + TIME("time"), + TIME_NO_MILLIS("time_no_millis"), + T_TIME("t_time"), + T_TIME_NO_MILLIS("t_time_no_millis"), + WEEK_DATE("week_date"), + WEEK_DATE_TIME("week_date_time"), + WEEK_DATE_TIME_NO_MILLIS("week_date_time_no_millis"), + WEEK_YEAR("week_year"), + WEEKYEAR("weekyear"), + WEEK_YEAR_WEEK("weekyear_week"), + WEEKYEAR_WEEK_DAY("weekyear_week_day"), + YEAR("year"), + YEAR_MONTH("year_month"), + YEAR_MONTH_DAY("year_month_day"), + EPOCH_SECOND("epoch_second"), + EPOCH_MILLIS("epoch_millis"), // strict date formats here, must be at least 4 digits for year and two for months and two for day" - STRICT_BASIC_WEEK_DATE("strictBasicWeekDate", "strict_basic_week_date"), - STRICT_BASIC_WEEK_DATE_TIME("strictBasicWeekDateTime", "strict_basic_week_date_time"), - STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS("strictBasicWeekDateTimeNoMillis", "strict_basic_week_date_time_no_millis"), - STRICT_DATE("strictDate", "strict_date"), - STRICT_DATE_HOUR("strictDateHour", "strict_date_hour"), - STRICT_DATE_HOUR_MINUTE("strictDateHourMinute", "strict_date_hour_minute"), - STRICT_DATE_HOUR_MINUTE_SECOND("strictDateHourMinuteSecond", "strict_date_hour_minute_second"), - STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION("strictDateHourMinuteSecondFraction", "strict_date_hour_minute_second_fraction"), - STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS("strictDateHourMinuteSecondMillis", "strict_date_hour_minute_second_millis"), - STRICT_DATE_OPTIONAL_TIME("strictDateOptionalTime", "strict_date_optional_time"), - STRICT_DATE_OPTIONAL_TIME_NANOS("strictDateOptionalTimeNanos", "strict_date_optional_time_nanos"), - STRICT_DATE_TIME("strictDateTime", "strict_date_time"), - STRICT_DATE_TIME_NO_MILLIS("strictDateTimeNoMillis", "strict_date_time_no_millis"), - STRICT_HOUR("strictHour", "strict_hour"), - STRICT_HOUR_MINUTE("strictHourMinute", "strict_hour_minute"), - STRICT_HOUR_MINUTE_SECOND("strictHourMinuteSecond", "strict_hour_minute_second"), - STRICT_HOUR_MINUTE_SECOND_FRACTION("strictHourMinuteSecondFraction", "strict_hour_minute_second_fraction"), - STRICT_HOUR_MINUTE_SECOND_MILLIS("strictHourMinuteSecondMillis", "strict_hour_minute_second_millis"), - STRICT_ORDINAL_DATE("strictOrdinalDate", "strict_ordinal_date"), - STRICT_ORDINAL_DATE_TIME("strictOrdinalDateTime", "strict_ordinal_date_time"), - STRICT_ORDINAL_DATE_TIME_NO_MILLIS("strictOrdinalDateTimeNoMillis", "strict_ordinal_date_time_no_millis"), - STRICT_TIME("strictTime", "strict_time"), - STRICT_TIME_NO_MILLIS("strictTimeNoMillis", "strict_time_no_millis"), - STRICT_T_TIME("strictTTime", "strict_t_time"), - STRICT_T_TIME_NO_MILLIS("strictTTimeNoMillis", "strict_t_time_no_millis"), - STRICT_WEEK_DATE("strictWeekDate", "strict_week_date"), - STRICT_WEEK_DATE_TIME("strictWeekDateTime", "strict_week_date_time"), - STRICT_WEEK_DATE_TIME_NO_MILLIS("strictWeekDateTimeNoMillis", "strict_week_date_time_no_millis"), - STRICT_WEEKYEAR("strictWeekyear", "strict_weekyear"), - STRICT_WEEKYEAR_WEEK("strictWeekyearWeek", "strict_weekyear_week"), - STRICT_WEEKYEAR_WEEK_DAY("strictWeekyearWeekDay", "strict_weekyear_week_day"), - STRICT_YEAR("strictYear", "strict_year"), - STRICT_YEAR_MONTH("strictYearMonth", "strict_year_month"), - STRICT_YEAR_MONTH_DAY("strictYearMonthDay", "strict_year_month_day"); + STRICT_BASIC_WEEK_DATE("strict_basic_week_date"), + STRICT_BASIC_WEEK_DATE_TIME("strict_basic_week_date_time"), + STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS("strict_basic_week_date_time_no_millis"), + STRICT_DATE("strict_date"), + STRICT_DATE_HOUR("strict_date_hour"), + STRICT_DATE_HOUR_MINUTE("strict_date_hour_minute"), + STRICT_DATE_HOUR_MINUTE_SECOND("strict_date_hour_minute_second"), + STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION("strict_date_hour_minute_second_fraction"), + STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS("strict_date_hour_minute_second_millis"), + STRICT_DATE_OPTIONAL_TIME("strict_date_optional_time"), + STRICT_DATE_OPTIONAL_TIME_NANOS("strict_date_optional_time_nanos"), + STRICT_DATE_TIME("strict_date_time"), + STRICT_DATE_TIME_NO_MILLIS("strict_date_time_no_millis"), + STRICT_HOUR("strict_hour"), + STRICT_HOUR_MINUTE("strict_hour_minute"), + STRICT_HOUR_MINUTE_SECOND("strict_hour_minute_second"), + STRICT_HOUR_MINUTE_SECOND_FRACTION("strict_hour_minute_second_fraction"), + STRICT_HOUR_MINUTE_SECOND_MILLIS("strict_hour_minute_second_millis"), + STRICT_ORDINAL_DATE("strict_ordinal_date"), + STRICT_ORDINAL_DATE_TIME("strict_ordinal_date_time"), + STRICT_ORDINAL_DATE_TIME_NO_MILLIS("strict_ordinal_date_time_no_millis"), + STRICT_TIME("strict_time"), + STRICT_TIME_NO_MILLIS("strict_time_no_millis"), + STRICT_T_TIME("strict_t_time"), + STRICT_T_TIME_NO_MILLIS("strict_t_time_no_millis"), + STRICT_WEEK_DATE("strict_week_date"), + STRICT_WEEK_DATE_TIME("strict_week_date_time"), + STRICT_WEEK_DATE_TIME_NO_MILLIS("strict_week_date_time_no_millis"), + STRICT_WEEKYEAR("strict_weekyear"), + STRICT_WEEKYEAR_WEEK("strict_weekyear_week"), + STRICT_WEEKYEAR_WEEK_DAY("strict_weekyear_week_day"), + STRICT_YEAR("strict_year"), + STRICT_YEAR_MONTH("strict_year_month"), + STRICT_YEAR_MONTH_DAY("strict_year_month_day"); - private static final Set ALL_NAMES = Arrays.stream(values()) - .flatMap(n -> Stream.of(n.snakeCaseName, n.camelCaseName)) - .collect(Collectors.toSet()); - private final String camelCaseName; private final String snakeCaseName; - FormatNames(String camelCaseName, String snakeCaseName) { - this.camelCaseName = camelCaseName; + FormatNames(String snakeCaseName) { this.snakeCaseName = snakeCaseName; } - public static boolean exist(String format) { - return ALL_NAMES.contains(format); - } - - public static FormatNames forName(String format) { - for (FormatNames name : values()) { - if (name.matches(format)) { - return name; - } - } - return null; - } - public boolean matches(String format) { - return format.equals(camelCaseName) || format.equals(snakeCaseName); - } - - public boolean isCamelCase(String format) { - return format.equals(camelCaseName); + return format.equals(snakeCaseName); } public String getSnakeCaseName() { return snakeCaseName; } - - public String getCamelCaseName() { - return camelCaseName; - } } diff --git a/server/src/main/java/org/opensearch/common/time/LegacyFormatNames.java b/server/src/main/java/org/opensearch/common/time/LegacyFormatNames.java new file mode 100644 index 0000000000000..86a318fe8ba05 --- /dev/null +++ b/server/src/main/java/org/opensearch/common/time/LegacyFormatNames.java @@ -0,0 +1,122 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.time; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Legacy Date format names. + * + * These format names are to ensure BWC support for camel case DateFormat names on indexes + * created prior to 2.12. + * + * @opensearch.internal + */ +enum LegacyFormatNames { + ISO8601(null, "iso8601"), + BASIC_DATE("basicDate", "basic_date"), + BASIC_DATE_TIME("basicDateTime", "basic_date_time"), + BASIC_DATE_TIME_NO_MILLIS("basicDateTimeNoMillis", "basic_date_time_no_millis"), + BASIC_ORDINAL_DATE("basicOrdinalDate", "basic_ordinal_date"), + BASIC_ORDINAL_DATE_TIME("basicOrdinalDateTime", "basic_ordinal_date_time"), + BASIC_ORDINAL_DATE_TIME_NO_MILLIS("basicOrdinalDateTimeNoMillis", "basic_ordinal_date_time_no_millis"), + BASIC_TIME("basicTime", "basic_time"), + BASIC_TIME_NO_MILLIS("basicTimeNoMillis", "basic_time_no_millis"), + BASIC_T_TIME("basicTTime", "basic_t_time"), + BASIC_T_TIME_NO_MILLIS("basicTTimeNoMillis", "basic_t_time_no_millis"), + BASIC_WEEK_DATE("basicWeekDate", "basic_week_date"), + BASIC_WEEK_DATE_TIME("basicWeekDateTime", "basic_week_date_time"), + BASIC_WEEK_DATE_TIME_NO_MILLIS("basicWeekDateTimeNoMillis", "basic_week_date_time_no_millis"), + DATE(null, "date"), + DATE_HOUR("dateHour", "date_hour"), + DATE_HOUR_MINUTE("dateHourMinute", "date_hour_minute"), + DATE_HOUR_MINUTE_SECOND("dateHourMinuteSecond", "date_hour_minute_second"), + DATE_HOUR_MINUTE_SECOND_FRACTION("dateHourMinuteSecondFraction", "date_hour_minute_second_fraction"), + DATE_HOUR_MINUTE_SECOND_MILLIS("dateHourMinuteSecondMillis", "date_hour_minute_second_millis"), + DATE_OPTIONAL_TIME("dateOptionalTime", "date_optional_time"), + DATE_TIME("dateTime", "date_time"), + DATE_TIME_NO_MILLIS("dateTimeNoMillis", "date_time_no_millis"), + HOUR(null, "hour"), + HOUR_MINUTE("hourMinute", "hour_minute"), + HOUR_MINUTE_SECOND("hourMinuteSecond", "hour_minute_second"), + HOUR_MINUTE_SECOND_FRACTION("hourMinuteSecondFraction", "hour_minute_second_fraction"), + HOUR_MINUTE_SECOND_MILLIS("hourMinuteSecondMillis", "hour_minute_second_millis"), + ORDINAL_DATE("ordinalDate", "ordinal_date"), + ORDINAL_DATE_TIME("ordinalDateTime", "ordinal_date_time"), + ORDINAL_DATE_TIME_NO_MILLIS("ordinalDateTimeNoMillis", "ordinal_date_time_no_millis"), + TIME(null, "time"), + TIME_NO_MILLIS("timeNoMillis", "time_no_millis"), + T_TIME("tTime", "t_time"), + T_TIME_NO_MILLIS("tTimeNoMillis", "t_time_no_millis"), + WEEK_DATE("weekDate", "week_date"), + WEEK_DATE_TIME("weekDateTime", "week_date_time"), + WEEK_DATE_TIME_NO_MILLIS("weekDateTimeNoMillis", "week_date_time_no_millis"), + WEEK_YEAR(null, "week_year"), + WEEKYEAR(null, "weekyear"), + WEEK_YEAR_WEEK("weekyearWeek", "weekyear_week"), + WEEKYEAR_WEEK_DAY("weekyearWeekDay", "weekyear_week_day"), + YEAR(null, "year"), + YEAR_MONTH("yearMonth", "year_month"), + YEAR_MONTH_DAY("yearMonthDay", "year_month_day"), + EPOCH_SECOND(null, "epoch_second"), + EPOCH_MILLIS(null, "epoch_millis"), + // strict date formats here, must be at least 4 digits for year and two for months and two for day + STRICT_BASIC_WEEK_DATE("strictBasicWeekDate", "strict_basic_week_date"), + STRICT_BASIC_WEEK_DATE_TIME("strictBasicWeekDateTime", "strict_basic_week_date_time"), + STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS("strictBasicWeekDateTimeNoMillis", "strict_basic_week_date_time_no_millis"), + STRICT_DATE("strictDate", "strict_date"), + STRICT_DATE_HOUR("strictDateHour", "strict_date_hour"), + STRICT_DATE_HOUR_MINUTE("strictDateHourMinute", "strict_date_hour_minute"), + STRICT_DATE_HOUR_MINUTE_SECOND("strictDateHourMinuteSecond", "strict_date_hour_minute_second"), + STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION("strictDateHourMinuteSecondFraction", "strict_date_hour_minute_second_fraction"), + STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS("strictDateHourMinuteSecondMillis", "strict_date_hour_minute_second_millis"), + STRICT_DATE_OPTIONAL_TIME("strictDateOptionalTime", "strict_date_optional_time"), + STRICT_DATE_OPTIONAL_TIME_NANOS("strictDateOptionalTimeNanos", "strict_date_optional_time_nanos"), + STRICT_DATE_TIME("strictDateTime", "strict_date_time"), + STRICT_DATE_TIME_NO_MILLIS("strictDateTimeNoMillis", "strict_date_time_no_millis"), + STRICT_HOUR("strictHour", "strict_hour"), + STRICT_HOUR_MINUTE("strictHourMinute", "strict_hour_minute"), + STRICT_HOUR_MINUTE_SECOND("strictHourMinuteSecond", "strict_hour_minute_second"), + STRICT_HOUR_MINUTE_SECOND_FRACTION("strictHourMinuteSecondFraction", "strict_hour_minute_second_fraction"), + STRICT_HOUR_MINUTE_SECOND_MILLIS("strictHourMinuteSecondMillis", "strict_hour_minute_second_millis"), + STRICT_ORDINAL_DATE("strictOrdinalDate", "strict_ordinal_date"), + STRICT_ORDINAL_DATE_TIME("strictOrdinalDateTime", "strict_ordinal_date_time"), + STRICT_ORDINAL_DATE_TIME_NO_MILLIS("strictOrdinalDateTimeNoMillis", "strict_ordinal_date_time_no_millis"), + STRICT_TIME("strictTime", "strict_time"), + STRICT_TIME_NO_MILLIS("strictTimeNoMillis", "strict_time_no_millis"), + STRICT_T_TIME("strictTTime", "strict_t_time"), + STRICT_T_TIME_NO_MILLIS("strictTTimeNoMillis", "strict_t_time_no_millis"), + STRICT_WEEK_DATE("strictWeekDate", "strict_week_date"), + STRICT_WEEK_DATE_TIME("strictWeekDateTime", "strict_week_date_time"), + STRICT_WEEK_DATE_TIME_NO_MILLIS("strictWeekDateTimeNoMillis", "strict_week_date_time_no_millis"), + STRICT_WEEKYEAR("strictWeekyear", "strict_weekyear"), + STRICT_WEEKYEAR_WEEK("strictWeekyearWeek", "strict_weekyear_week"), + STRICT_WEEKYEAR_WEEK_DAY("strictWeekyearWeekDay", "strict_weekyear_week_day"), + STRICT_YEAR("strictYear", "strict_year"), + STRICT_YEAR_MONTH("strictYearMonth", "strict_year_month"), + STRICT_YEAR_MONTH_DAY("strictYearMonthDay", "strict_year_month_day"); + + private static final Map ALL_NAMES = Arrays.stream(values()) + .filter(n -> n.camelCaseName != null) + .collect(Collectors.toMap(n -> n.camelCaseName, n -> n.snakeCaseName)); + + private final String camelCaseName; + private final String snakeCaseName; + + LegacyFormatNames(String camelCaseName, String snakeCaseName) { + this.camelCaseName = camelCaseName; + this.snakeCaseName = snakeCaseName; + } + + public static String camelCaseToSnakeCase(String format) { + return ALL_NAMES.getOrDefault(format, format); + } +} diff --git a/server/src/main/java/org/opensearch/index/mapper/DateFieldMapper.java b/server/src/main/java/org/opensearch/index/mapper/DateFieldMapper.java index 3b832628695fe..a541ae00fc746 100644 --- a/server/src/main/java/org/opensearch/index/mapper/DateFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/DateFieldMapper.java @@ -282,10 +282,15 @@ public Builder( private DateFormatter buildFormatter() { try { if (format.isConfigured() && !printFormat.isConfigured()) { - return DateFormatter.forPattern(format.getValue(), null, !format.isConfigured()).withLocale(locale.getValue()); + return DateFormatter.forPattern(format.getValue(), null, format.isConfigured() == false, indexCreatedVersion) + .withLocale(locale.getValue()); } - return DateFormatter.forPattern(format.getValue(), printFormat.getValue(), !format.isConfigured()) - .withLocale(locale.getValue()); + return DateFormatter.forPattern( + format.getValue(), + printFormat.getValue(), + format.isConfigured() == false, + indexCreatedVersion + ).withLocale(locale.getValue()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Error parsing [format] on field [" + name() + "]: " + e.getMessage(), e); } diff --git a/server/src/main/java/org/opensearch/index/mapper/ParametrizedFieldMapper.java b/server/src/main/java/org/opensearch/index/mapper/ParametrizedFieldMapper.java index 93b929a82f095..b85b18a20e621 100644 --- a/server/src/main/java/org/opensearch/index/mapper/ParametrizedFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/ParametrizedFieldMapper.java @@ -325,7 +325,8 @@ private void init(FieldMapper toInit) { setValue(initializer.apply(toInit)); } - private void parse(String field, ParserContext context, Object in) { + // public for testing only + public void parse(String field, ParserContext context, Object in) { setValue(parser.apply(field, context, in)); } diff --git a/server/src/main/java/org/opensearch/script/ClassPermission.java b/server/src/main/java/org/opensearch/script/ClassPermission.java index 9be8ecbc7c33e..a6e97de1a8387 100644 --- a/server/src/main/java/org/opensearch/script/ClassPermission.java +++ b/server/src/main/java/org/opensearch/script/ClassPermission.java @@ -78,12 +78,6 @@ *

  • {@link java.util.Map}
  • *
  • {@link java.util.Set}
  • *
  • {@link java.util.UUID}
  • - *
  • {@link org.joda.time.DateTime}
  • - *
  • {@link org.joda.time.DateTimeUtils}
  • - *
  • {@link org.joda.time.DateTimeZone}
  • - *
  • {@link org.joda.time.Instant}
  • - *
  • {@link org.joda.time.ReadableDateTime}
  • - *
  • {@link org.joda.time.ReadableInstant}
  • * * * @opensearch.internal @@ -116,14 +110,7 @@ public final class ClassPermission extends BasicPermission { java.util.List.class.getName(), java.util.Map.class.getName(), java.util.Set.class.getName(), - java.util.UUID.class.getName(), - // joda-time - org.joda.time.DateTime.class.getName(), - org.joda.time.DateTimeUtils.class.getName(), - org.joda.time.DateTimeZone.class.getName(), - org.joda.time.Instant.class.getName(), - org.joda.time.ReadableDateTime.class.getName(), - org.joda.time.ReadableInstant.class.getName() + java.util.UUID.class.getName() ) ) ); diff --git a/server/src/main/java/org/opensearch/search/DocValueFormat.java b/server/src/main/java/org/opensearch/search/DocValueFormat.java index 7be51643eeb7d..00476e62bc9ae 100644 --- a/server/src/main/java/org/opensearch/search/DocValueFormat.java +++ b/server/src/main/java/org/opensearch/search/DocValueFormat.java @@ -243,14 +243,15 @@ public DateTime(DateFormatter formatter, ZoneId timeZone, DateFieldMapper.Resolu } public DateTime(StreamInput in) throws IOException { - if (in.getVersion().onOrAfter(Version.V_2_12_0)) { + final Version inVersion = in.getVersion(); + if (inVersion.onOrAfter(Version.V_2_12_0)) { this.formatter = DateFormatter.forPattern(in.readString(), in.readOptionalString()); } else { - this.formatter = DateFormatter.forPattern(in.readString()); + this.formatter = DateFormatter.forPattern(in.readString(), inVersion); } this.parser = formatter.toDateMathParser(); - String zoneId = in.readString(); + final String zoneId = in.readString(); this.timeZone = ZoneId.of(zoneId); this.resolution = DateFieldMapper.Resolution.ofOrdinal(in.readVInt()); if (in.getVersion().before(Version.V_3_0_0)) { diff --git a/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptDoubleValues.java b/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptDoubleValues.java index 9b73f1b2155fb..09a614175dbdd 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptDoubleValues.java +++ b/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptDoubleValues.java @@ -36,7 +36,6 @@ import org.opensearch.index.fielddata.SortingNumericDoubleValues; import org.opensearch.script.AggregationScript; import org.opensearch.search.aggregations.AggregationExecutionException; -import org.joda.time.ReadableInstant; import java.io.IOException; import java.lang.reflect.Array; @@ -67,9 +66,6 @@ public boolean advanceExact(int target) throws IOException { } else if (value instanceof Number) { resize(1); values[0] = ((Number) value).doubleValue(); - } else if (value instanceof ReadableInstant) { - resize(1); - values[0] = ((ReadableInstant) value).getMillis(); } else if (value instanceof ZonedDateTime) { resize(1); values[0] = ((ZonedDateTime) value).toInstant().toEpochMilli(); @@ -105,9 +101,6 @@ public boolean advanceExact(int target) throws IOException { private static double toDoubleValue(Object o) { if (o instanceof Number) { return ((Number) o).doubleValue(); - } else if (o instanceof ReadableInstant) { - // Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric - return ((ReadableInstant) o).getMillis(); } else if (o instanceof ZonedDateTime) { return ((ZonedDateTime) o).toInstant().toEpochMilli(); } else if (o instanceof Boolean) { diff --git a/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptLongValues.java b/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptLongValues.java index b62178f85273a..c9bb21de50468 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptLongValues.java +++ b/server/src/main/java/org/opensearch/search/aggregations/support/values/ScriptLongValues.java @@ -37,7 +37,6 @@ import org.opensearch.index.fielddata.AbstractSortingNumericDocValues; import org.opensearch.script.AggregationScript; import org.opensearch.search.aggregations.AggregationExecutionException; -import org.joda.time.ReadableInstant; import java.io.IOException; import java.lang.reflect.Array; @@ -104,9 +103,6 @@ else if (value instanceof Collection) { private static long toLongValue(Object o) { if (o instanceof Number) { return ((Number) o).longValue(); - } else if (o instanceof ReadableInstant) { - // Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric - return ((ReadableInstant) o).getMillis(); } else if (o instanceof ZonedDateTime) { return ((ZonedDateTime) o).toInstant().toEpochMilli(); } else if (o instanceof Boolean) { diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java index a152b4473ef1e..20137c7316333 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java @@ -39,18 +39,20 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver.Context; import org.opensearch.cluster.metadata.IndexNameExpressionResolver.DateMathExpressionResolver; import org.opensearch.test.OpenSearchTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.joda.time.DateTimeZone.UTC; public class DateMathExpressionResolverTests extends OpenSearchTestCase { @@ -80,20 +82,32 @@ public void testExpression() throws Exception { assertThat(result.size(), equalTo(3)); assertThat( result.get(0), - equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".marvel-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); assertThat( result.get(1), - equalTo(".watch_history-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".watch_history-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); assertThat( result.get(2), - equalTo("logstash-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + "logstash-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); } public void testEmpty() throws Exception { - List result = expressionResolver.resolve(context, Collections.emptyList()); + List result = expressionResolver.resolve(context, Collections.emptyList()); assertThat(result.size(), equalTo(0)); } @@ -110,9 +124,11 @@ public void testExpression_MultiParts() throws Exception { result.get(0), equalTo( ".text1-" - + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC)) + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + "-text2-" - + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC).withDayOfMonth(1)) + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC).withDayOfMonth(1)) ) ); } @@ -122,7 +138,11 @@ public void testExpression_CustomFormat() throws Exception { assertThat(results.size(), equalTo(1)); assertThat( results.get(0), - equalTo(".marvel-" + DateTimeFormat.forPattern("yyyy.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".marvel-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); } @@ -131,7 +151,11 @@ public void testExpression_EscapeStatic() throws Exception { assertThat(result.size(), equalTo(1)); assertThat( result.get(0), - equalTo(".mar{v}el-" + DateTimeFormat.forPattern("yyyy.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".mar{v}el-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); } @@ -140,7 +164,11 @@ public void testExpression_EscapeDateFormat() throws Exception { assertThat(result.size(), equalTo(1)); assertThat( result.get(0), - equalTo(".marvel-" + DateTimeFormat.forPattern("'{year}'yyyy").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".marvel-" + + DateTimeFormatter.ofPattern("'{year}'yyyy", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); } @@ -153,45 +181,59 @@ public void testExpression_MixedArray() throws Exception { assertThat(result.get(0), equalTo("name1")); assertThat( result.get(1), - equalTo(".marvel-" + DateTimeFormat.forPattern("yyyy.MM.dd").print(new DateTime(context.getStartTime(), UTC))) + equalTo( + ".marvel-" + + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC)) + ) ); assertThat(result.get(2), equalTo("name2")); assertThat( result.get(3), - equalTo(".logstash-" + DateTimeFormat.forPattern("yyyy.MM").print(new DateTime(context.getStartTime(), UTC).withDayOfMonth(1))) + equalTo( + ".logstash-" + + DateTimeFormatter.ofPattern("uuuu.MM", Locale.ROOT) + .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(context.getStartTime()), ZoneOffset.UTC).withDayOfMonth(1)) + ) ); } public void testExpression_CustomTimeZoneInIndexName() throws Exception { - DateTimeZone timeZone; + ZoneId timeZone; int hoursOffset; int minutesOffset = 0; if (randomBoolean()) { hoursOffset = randomIntBetween(-12, 14); - timeZone = DateTimeZone.forOffsetHours(hoursOffset); + timeZone = ZoneOffset.ofHours(hoursOffset); } else { hoursOffset = randomIntBetween(-11, 13); minutesOffset = randomIntBetween(0, 59); - timeZone = DateTimeZone.forOffsetHoursMinutes(hoursOffset, minutesOffset); + if (hoursOffset < 0) { + minutesOffset = -minutesOffset; + } + timeZone = ZoneOffset.ofHoursMinutes(hoursOffset, minutesOffset); } - DateTime now; + ZonedDateTime now; if (hoursOffset >= 0) { // rounding to next day 00:00 - now = DateTime.now(UTC) + now = ZonedDateTime.now(ZoneOffset.UTC) .plusHours(hoursOffset) .plusMinutes(minutesOffset) - .withHourOfDay(0) - .withMinuteOfHour(0) - .withSecondOfMinute(0); + .withHour(0) + .withMinute(0) + .withSecond(0); } else { // rounding to today 00:00 - now = DateTime.now(UTC).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0); + now = ZonedDateTime.now(ZoneOffset.UTC).withHour(0).withMinute(0).withSecond(0); } - Context context = new Context(this.context.getState(), this.context.getOptions(), now.getMillis(), false); - List results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{yyyy.MM.dd|" + timeZone.getID() + "}}>")); + Context context = new Context(this.context.getState(), this.context.getOptions(), now.toInstant().toEpochMilli(), false); + List results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{yyyy.MM.dd|" + timeZone.getId() + "}}>")); assertThat(results.size(), equalTo(1)); logger.info("timezone: [{}], now [{}], name: [{}]", timeZone, now, results.get(0)); - assertThat(results.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("yyyy.MM.dd").print(now.withZone(timeZone)))); + assertThat( + results.get(0), + equalTo(".marvel-" + DateTimeFormatter.ofPattern("uuuu.MM.dd", Locale.ROOT).format(now.withZoneSameInstant(timeZone))) + ); } public void testExpressionInvalidUnescaped() throws Exception { diff --git a/server/src/test/java/org/opensearch/common/RoundingTests.java b/server/src/test/java/org/opensearch/common/RoundingTests.java index cc71ee08abcca..6873037070db8 100644 --- a/server/src/test/java/org/opensearch/common/RoundingTests.java +++ b/server/src/test/java/org/opensearch/common/RoundingTests.java @@ -235,7 +235,7 @@ public void testOffsetRounding() { /** * Randomized test on TimeUnitRounding. Test uses random - * {@link org.opensearch.common.Rounding.DateTimeUnit} and {@link ZoneId} and often (50% of the time) + * {@link Rounding.DateTimeUnit} and {@link ZoneId} and often (50% of the time) * chooses test dates that are exactly on or close to offset changes (e.g. * DST) in the chosen time zone. *

    diff --git a/server/src/test/java/org/opensearch/common/joda/JavaJodaTimeDuellingTests.java b/server/src/test/java/org/opensearch/common/joda/JavaJodaTimeDuellingTests.java deleted file mode 100644 index e8ddfde11f4cc..0000000000000 --- a/server/src/test/java/org/opensearch/common/joda/JavaJodaTimeDuellingTests.java +++ /dev/null @@ -1,952 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.OpenSearchParseException; -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.DateFormatters; -import org.opensearch.common.time.DateMathParser; -import org.opensearch.common.time.FormatNames; -import org.opensearch.test.OpenSearchTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.ISODateTimeFormat; - -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; -import java.util.Locale; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; - -public class JavaJodaTimeDuellingTests extends OpenSearchTestCase { - @Override - protected boolean enableWarningsCheck() { - return false; - } - - public void testTimezoneParsing() { - /* this testcase won't work in joda. See comment in {@link #testPartialTimeParsing()} - assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); - */ - assertSameDateAs("2016-11-30T00+01", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T00+0100", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T00+01:00", "strict_date_optional_time", "strict_date_optional_time"); - } - - public void testPartialTimeParsing() { - /* - This does not work in Joda as it reports 2016-11-30T01:00:00Z - because StrictDateOptionalTime confuses +01 with an hour (which is a signed fixed length digit) - assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); - ES java.time implementation does not suffer from this, - but we intentionally not allow parsing timezone without an time part as it is not allowed in iso8601 - */ - assertJavaTimeParseException("2016-11-30T+01", "strict_date_optional_time"); - - assertSameDateAs("2016-11-30T12+01", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00+01", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00:00+01", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00:00.000+01", "strict_date_optional_time", "strict_date_optional_time"); - - // without timezone - assertSameDateAs("2016-11-30T", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00:00", "strict_date_optional_time", "strict_date_optional_time"); - assertSameDateAs("2016-11-30T12:00:00.000", "strict_date_optional_time", "strict_date_optional_time"); - } - - // date_optional part of a parser names "strict_date_optional_time" or "date_optional"time - // means that date part can be partially parsed. - public void testPartialDateParsing() { - assertSameDateAs("2001", "strict_date_optional_time_nanos", "strict_date_optional_time"); - assertSameDateAs("2001-01", "strict_date_optional_time_nanos", "strict_date_optional_time"); - assertSameDateAs("2001-01-01", "strict_date_optional_time_nanos", "strict_date_optional_time"); - - assertSameDate("2001", "strict_date_optional_time"); - assertSameDate("2001-01", "strict_date_optional_time"); - assertSameDate("2001-01-01", "strict_date_optional_time"); - - assertSameDate("2001", "date_optional_time"); - assertSameDate("2001-01", "date_optional_time"); - assertSameDate("2001-01-01", "date_optional_time"); - - assertSameDateAs("2001", "iso8601", "strict_date_optional_time"); - assertSameDateAs("2001-01", "iso8601", "strict_date_optional_time"); - assertSameDateAs("2001-01-01", "iso8601", "strict_date_optional_time"); - - assertSameDate("9999", "date_optional_time||epoch_second"); - } - - public void testCompositeDateMathParsing() { - // in all these examples the second pattern will be used - assertDateMathEquals("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); - assertDateMathEquals("2014-06-06T12:01:02.123", "strictDateTimeNoMillis||yyyy-MM-dd'T'HH:mm:ss.SSS"); - assertDateMathEquals("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS"); - } - - public void testExceptionWhenCompositeParsingFailsDateMath() { - // both parsing failures should contain pattern and input text in exception - // both patterns fail parsing the input text due to only 2 digits of millis. Hence full text was not parsed. - String pattern = "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"; - String text = "2014-06-06T12:01:02.123"; - OpenSearchParseException e1 = expectThrows( - OpenSearchParseException.class, - () -> dateMathToMillis(text, DateFormatter.forPattern(pattern)) - ); - assertThat(e1.getMessage(), containsString(pattern)); - assertThat(e1.getMessage(), containsString(text)); - - OpenSearchParseException e2 = expectThrows(OpenSearchParseException.class, () -> dateMathToMillis(text, Joda.forPattern(pattern))); - assertThat(e2.getMessage(), containsString(pattern)); - assertThat(e2.getMessage(), containsString(text)); - } - - private long dateMathToMillis(String text, DateFormatter dateFormatter) { - DateFormatter javaFormatter = dateFormatter.withLocale(randomLocale(random())); - DateMathParser javaDateMath = javaFormatter.toDateMathParser(); - return javaDateMath.parse(text, () -> 0, true, (ZoneId) null).toEpochMilli(); - } - - // these parsers should allow both ',' and '.' as a decimal point - public void testDecimalPointParsing() { - assertSameDate("2001-01-01T00:00:00.123Z", "strict_date_optional_time"); - assertSameDate("2001-01-01T00:00:00,123Z", "strict_date_optional_time"); - - assertSameDate("2001-01-01T00:00:00.123Z", "date_optional_time"); - assertSameDate("2001-01-01T00:00:00,123Z", "date_optional_time"); - - // only java.time has nanos parsing, but the results for 3digits should be the same - DateFormatter jodaFormatter = Joda.forPattern("strict_date_optional_time"); - DateFormatter javaFormatter = DateFormatter.forPattern("strict_date_optional_time_nanos"); - assertSameDate("2001-01-01T00:00:00.123Z", "strict_date_optional_time_nanos", jodaFormatter, javaFormatter); - assertSameDate("2001-01-01T00:00:00,123Z", "strict_date_optional_time_nanos", jodaFormatter, javaFormatter); - - assertParseException("2001-01-01T00:00:00.123,456Z", "strict_date_optional_time"); - assertParseException("2001-01-01T00:00:00.123,456Z", "date_optional_time"); - // This should fail, but java is ok with this because the field has the same value - // assertJavaTimeParseException("2001-01-01T00:00:00.123,123Z", "strict_date_optional_time_nanos"); - } - - public void testIncompatiblePatterns() { - // in joda 'y' means year, this is changed to 'u' in java.time. difference is in before era yeaers - assertSameMillis("-0001-01-01", "yyyy-MM-dd", "8uuuu-MM-dd"); - assertSameMillis("-1", "y", "8u"); - - // year-of-era in joda becomes 'y' in java.time - assertSameMillis("2019-01-01", "YYYY-MM-dd", "8yyyy-MM-dd"); - - // in joda 'Z' was able to parse 'Z' zulu but in java it fails. You have to use 'X' to do that. - assertSameMillis("2019-01-01T01:01:01.001Z", "YYYY-MM-dd'T'HH:mm:ss.SSSZ", "8yyyy-MM-dd'T'HH:mm:ss.SSSX"); - assertSameMillis("2019-01-01T01:01:01.001+0000", "YYYY-MM-dd'T'HH:mm:ss.SSSZ", "8yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - - // 'z' zoneId in joda prints UTC whereas joda prints 'Z' for zulu - TemporalAccessor parse = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSz", Locale.getDefault()) - .parse("2019-01-01T01:01:01.001+00:00"); - String javaZoneId = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSz", Locale.getDefault()).format(parse); - - DateTime dateTime = DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ss.SSSZ") - .withOffsetParsed() - .parseDateTime("2019-01-01T01:01:01.001+0000"); - String jodaZoneId = DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ss.SSSz").print(dateTime); - assertThat(javaZoneId, equalTo("2019-01-01T01:01:01.001Z")); - assertThat(jodaZoneId, equalTo("2019-01-01T01:01:01.001UTC")); - } - - private void assertSameMillis(String input, String jodaFormat, String javaFormat) { - DateFormatter jodaFormatter = Joda.forPattern(jodaFormat); - DateFormatter javaFormatter = DateFormatter.forPattern(javaFormat); - - DateTime jodaDateTime = jodaFormatter.parseJoda(input); - - TemporalAccessor javaTimeAccessor = javaFormatter.parse(input); - ZonedDateTime zonedDateTime = DateFormatters.from(javaTimeAccessor); - - String msg = String.format( - Locale.ROOT, - "Input [%s] JodaFormat [%s] JavaFormat [%s] Joda [%s], Java [%s]", - input, - jodaFormat, - javaFormat, - jodaDateTime, - DateTimeFormatter.ISO_INSTANT.format(zonedDateTime.toInstant()) - ); - - assertThat(msg, jodaDateTime.getMillis(), is(zonedDateTime.toInstant().toEpochMilli())); - } - - 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"); - } - - // this test requires tests to run with -Djava.locale.providers=COMPAT in order to work - // public void testCustomLocales() { - // - // // also ensure that locale based dates are the same - // assertSameDate("Di., 05 Dez. 2000 02:55:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); - // assertSameDate("Mi., 06 Dez. 2000 02:55:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); - // assertSameDate("Do., 07 Dez. 2000 00:00:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); - // assertSameDate("Fr., 08 Dez. 2000 00:00:00 -0800", "E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de")); - // - // DateTime dateTimeNow = DateTime.now(DateTimeZone.UTC); - // ZonedDateTime javaTimeNow = Instant.ofEpochMilli(dateTimeNow.getMillis()).atZone(ZoneOffset.UTC); - // assertSamePrinterOutput("E, d MMM yyyy HH:mm:ss Z", LocaleUtils.parse("de"), javaTimeNow, dateTimeNow); - // } - - public void testDuellingFormatsValidParsing() { - assertSameDate("1522332219", "epoch_second"); - assertSameDate("0", "epoch_second"); - assertSameDate("1", "epoch_second"); - assertSameDate("1522332219321", "epoch_millis"); - assertSameDate("0", "epoch_millis"); - assertSameDate("1", "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("20181126T121212+01:00", "basic_date_time_no_millis"); - assertSameDate("20181126T121212+0100", "basic_date_time_no_millis"); - assertSameDate("2018363", "basic_ordinal_date"); - assertSameDate("2018363T121212.1Z", "basic_ordinal_date_time"); - assertSameDate("2018363T121212.123Z", "basic_ordinal_date_time"); - assertSameDate("2018363T121212.123456789Z", "basic_ordinal_date_time"); - assertSameDate("2018363T121212.123+0100", "basic_ordinal_date_time"); - assertSameDate("2018363T121212.123+01:00", "basic_ordinal_date_time"); - assertSameDate("2018363T121212Z", "basic_ordinal_date_time_no_millis"); - assertSameDate("2018363T121212+0100", "basic_ordinal_date_time_no_millis"); - assertSameDate("2018363T121212+01:00", "basic_ordinal_date_time_no_millis"); - assertSameDate("121212.1Z", "basic_time"); - assertSameDate("121212.123Z", "basic_time"); - assertSameDate("121212.123456789Z", "basic_time"); - assertSameDate("121212.1+0100", "basic_time"); - assertSameDate("121212.123+0100", "basic_time"); - assertSameDate("121212.123+01:00", "basic_time"); - assertSameDate("121212Z", "basic_time_no_millis"); - assertSameDate("121212+0100", "basic_time_no_millis"); - assertSameDate("121212+01:00", "basic_time_no_millis"); - assertSameDate("T121212.1Z", "basic_t_time"); - assertSameDate("T121212.123Z", "basic_t_time"); - assertSameDate("T121212.123456789Z", "basic_t_time"); - assertSameDate("T121212.1+0100", "basic_t_time"); - assertSameDate("T121212.123+0100", "basic_t_time"); - assertSameDate("T121212.123+01:00", "basic_t_time"); - assertSameDate("T121212Z", "basic_t_time_no_millis"); - assertSameDate("T121212+0100", "basic_t_time_no_millis"); - assertSameDate("T121212+01:00", "basic_t_time_no_millis"); - assertSameDate("2018W313", "basic_week_date"); - assertSameDate("1W313", "basic_week_date"); - assertSameDate("18W313", "basic_week_date"); - assertSameDate("2018W313T121212.1Z", "basic_week_date_time"); - assertSameDate("2018W313T121212.123Z", "basic_week_date_time"); - assertSameDate("2018W313T121212.123456789Z", "basic_week_date_time"); - assertSameDate("2018W313T121212.123+0100", "basic_week_date_time"); - assertSameDate("2018W313T121212.123+01:00", "basic_week_date_time"); - assertSameDate("2018W313T121212Z", "basic_week_date_time_no_millis"); - assertSameDate("2018W313T121212+0100", "basic_week_date_time_no_millis"); - assertSameDate("2018W313T121212+01:00", "basic_week_date_time_no_millis"); - - assertSameDate("2018-12-31", "date"); - assertSameDate("18-5-6", "date"); - assertSameDate("10000-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.1", "date_hour_minute_second_fraction"); - assertSameDate("2018-12-31T12:12:12.123", "date_hour_minute_second_fraction"); - assertSameDate("2018-12-31T12:12:12.123456789", "date_hour_minute_second_fraction"); - assertSameDate("2018-12-31T12:12:12.1", "date_hour_minute_second_millis"); - assertSameDate("2018-12-31T12:12:12.123", "date_hour_minute_second_millis"); - assertParseException("2018-12-31T12:12:12.123456789", "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-05", "date_optional_time"); - assertSameDate("2018-05-30", "date_optional_time"); - assertSameDate("2018-05-30T20", "date_optional_time"); - assertSameDate("2018-05-30T20:21", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.1", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123456789", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123Z", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123456789Z", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.1+0100", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123+0100", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.1+01:00", "date_optional_time"); - assertSameDate("2018-05-30T20:21:23.123+01:00", "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.1Z", "date_time"); - assertSameDate("2018-12-31T10:15:30.123Z", "date_time"); - assertSameDate("2018-12-31T10:15:30.123456789Z", "date_time"); - assertSameDate("2018-12-31T10:15:30.1+0100", "date_time"); - assertSameDate("2018-12-31T10:15:30.123+0100", "date_time"); - assertSameDate("2018-12-31T10:15:30.123+01:00", "date_time"); - assertSameDate("2018-12-31T10:15:30.1+01:00", "date_time"); - assertSameDate("2018-12-31T10:15:30.11Z", "date_time"); - assertSameDate("2018-12-31T10:15:30.11+0100", "date_time"); - assertSameDate("2018-12-31T10:15:30.11+01:00", "date_time"); - assertSameDate("2018-12-31T10:15:3.1Z", "date_time"); - assertSameDate("2018-12-31T10:15:3.123Z", "date_time"); - assertSameDate("2018-12-31T10:15:3.123456789Z", "date_time"); - assertSameDate("2018-12-31T10:15:3.1+0100", "date_time"); - assertSameDate("2018-12-31T10:15:3.123+0100", "date_time"); - assertSameDate("2018-12-31T10:15:3.123+01:00", "date_time"); - assertSameDate("2018-12-31T10:15:3.1+01:00", "date_time"); - - assertSameDate("2018-12-31T10:15:30Z", "date_time_no_millis"); - assertSameDate("2018-12-31T10:15:30+0100", "date_time_no_millis"); - assertSameDate("2018-12-31T10:15:30+01:00", "date_time_no_millis"); - assertSameDate("2018-12-31T10:5:30Z", "date_time_no_millis"); - assertSameDate("2018-12-31T10:5:30+0100", "date_time_no_millis"); - assertSameDate("2018-12-31T10:5:30+01:00", "date_time_no_millis"); - assertSameDate("2018-12-31T10:15:3Z", "date_time_no_millis"); - assertSameDate("2018-12-31T10:15:3+0100", "date_time_no_millis"); - assertSameDate("2018-12-31T10:15:3+01:00", "date_time_no_millis"); - assertSameDate("2018-12-31T1:15:30Z", "date_time_no_millis"); - assertSameDate("2018-12-31T1:15:30+0100", "date_time_no_millis"); - assertSameDate("2018-12-31T1:15:30+01:00", "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.123456789", "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"); - assertParseException("12:12:12.123456789", "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.1Z", "ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123Z", "ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123456789Z", "ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123+0100", "ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123+01:00", "ordinal_date_time"); - assertSameDate("2018-1T10:15:30.1Z", "ordinal_date_time"); - assertSameDate("2018-1T10:15:30.123Z", "ordinal_date_time"); - assertSameDate("2018-1T10:15:30.123456789Z", "ordinal_date_time"); - assertSameDate("2018-1T10:15:30.123+0100", "ordinal_date_time"); - assertSameDate("2018-1T10:15:30.123+01:00", "ordinal_date_time"); - - assertSameDate("2018-128T10:15:30Z", "ordinal_date_time_no_millis"); - assertSameDate("2018-128T10:15:30+0100", "ordinal_date_time_no_millis"); - assertSameDate("2018-128T10:15:30+01:00", "ordinal_date_time_no_millis"); - assertSameDate("2018-1T10:15:30Z", "ordinal_date_time_no_millis"); - assertSameDate("2018-1T10:15:30+0100", "ordinal_date_time_no_millis"); - assertSameDate("2018-1T10:15:30+01:00", "ordinal_date_time_no_millis"); - - assertSameDate("10:15:30.1Z", "time"); - assertSameDate("10:15:30.123Z", "time"); - assertSameDate("10:15:30.123456789Z", "time"); - assertSameDate("10:15:30.123+0100", "time"); - assertSameDate("10:15:30.123+01:00", "time"); - assertSameDate("1:15:30.1Z", "time"); - assertSameDate("1:15:30.123Z", "time"); - assertSameDate("1:15:30.123+0100", "time"); - assertSameDate("1:15:30.123+01:00", "time"); - assertSameDate("10:1:30.1Z", "time"); - assertSameDate("10:1:30.123Z", "time"); - assertSameDate("10:1:30.123+0100", "time"); - assertSameDate("10:1:30.123+01:00", "time"); - assertSameDate("10:15:3.1Z", "time"); - assertSameDate("10:15:3.123Z", "time"); - assertSameDate("10:15:3.123+0100", "time"); - assertSameDate("10:15:3.123+01:00", "time"); - assertParseException("10:15:3.1", "time"); - assertParseException("10:15:3Z", "time"); - - assertSameDate("10:15:30Z", "time_no_millis"); - assertSameDate("10:15:30+0100", "time_no_millis"); - assertSameDate("10:15:30+01:00", "time_no_millis"); - assertSameDate("01:15:30Z", "time_no_millis"); - assertSameDate("01:15:30+0100", "time_no_millis"); - assertSameDate("01:15:30+01:00", "time_no_millis"); - assertSameDate("1:15:30Z", "time_no_millis"); - assertSameDate("1:15:30+0100", "time_no_millis"); - assertSameDate("1:15:30+01:00", "time_no_millis"); - assertSameDate("10:5:30Z", "time_no_millis"); - assertSameDate("10:5:30+0100", "time_no_millis"); - assertSameDate("10:5:30+01:00", "time_no_millis"); - assertSameDate("10:15:3Z", "time_no_millis"); - assertSameDate("10:15:3+0100", "time_no_millis"); - assertSameDate("10:15:3+01:00", "time_no_millis"); - assertParseException("10:15:3", "time_no_millis"); - - assertSameDate("T10:15:30.1Z", "t_time"); - assertSameDate("T10:15:30.123Z", "t_time"); - assertSameDate("T10:15:30.123456789Z", "t_time"); - assertSameDate("T10:15:30.1+0100", "t_time"); - assertSameDate("T10:15:30.123+0100", "t_time"); - assertSameDate("T10:15:30.123+01:00", "t_time"); - assertSameDate("T10:15:30.1+01:00", "t_time"); - assertSameDate("T1:15:30.123Z", "t_time"); - assertSameDate("T1:15:30.123+0100", "t_time"); - assertSameDate("T1:15:30.123+01:00", "t_time"); - assertSameDate("T10:1:30.123Z", "t_time"); - assertSameDate("T10:1:30.123+0100", "t_time"); - assertSameDate("T10:1:30.123+01:00", "t_time"); - assertSameDate("T10:15:3.123Z", "t_time"); - assertSameDate("T10:15:3.123+0100", "t_time"); - assertSameDate("T10:15:3.123+01:00", "t_time"); - assertParseException("T10:15:3.1", "t_time"); - assertParseException("T10:15:3Z", "t_time"); - - assertSameDate("T10:15:30Z", "t_time_no_millis"); - assertSameDate("T10:15:30+0100", "t_time_no_millis"); - assertSameDate("T10:15:30+01:00", "t_time_no_millis"); - assertSameDate("T1:15:30Z", "t_time_no_millis"); - assertSameDate("T1:15:30+0100", "t_time_no_millis"); - assertSameDate("T1:15:30+01:00", "t_time_no_millis"); - assertSameDate("T10:1:30Z", "t_time_no_millis"); - assertSameDate("T10:1:30+0100", "t_time_no_millis"); - assertSameDate("T10:1:30+01:00", "t_time_no_millis"); - assertSameDate("T10:15:3Z", "t_time_no_millis"); - assertSameDate("T10:15:3+0100", "t_time_no_millis"); - assertSameDate("T10:15:3+01:00", "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"); - - assertSameDate("2012-W48-6T10:15:30.1Z", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123Z", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123456789Z", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.1+0100", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123+0100", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.1+01:00", "week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123+01:00", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.1Z", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.123Z", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.1+0100", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.123+0100", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.1+01:00", "week_date_time"); - assertSameDate("2012-W1-6T10:15:30.123+01:00", "week_date_time"); - - assertSameDate("2012-W48-6T10:15:30Z", "week_date_time_no_millis"); - assertSameDate("2012-W48-6T10:15:30+0100", "week_date_time_no_millis"); - assertSameDate("2012-W48-6T10:15:30+01:00", "week_date_time_no_millis"); - assertSameDate("2012-W1-6T10:15:30Z", "week_date_time_no_millis"); - assertSameDate("2012-W1-6T10:15:30+0100", "week_date_time_no_millis"); - assertSameDate("2012-W1-6T10:15:30+01:00", "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", "weekyear"); - assertSameDate("1", "weekyear"); - assertSameDate("2017", "weekyear"); - - 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 testCompositeParsing() { - // in all these examples the second pattern will be used - assertSameDate("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); - assertSameDate("2014-06-06T12:01:02.123", "strictDateTimeNoMillis||yyyy-MM-dd'T'HH:mm:ss.SSS"); - assertSameDate("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS"); - } - - public void testExceptionWhenCompositeParsingFails() { - assertParseException("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"); - } - - public void testDuelingStrictParsing() { - assertSameDate("2018W313", "strict_basic_week_date"); - assertParseException("18W313", "strict_basic_week_date"); - assertSameDate("2018W313T121212.1Z", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.123Z", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.123456789Z", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.1+0100", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.123+0100", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.1+01:00", "strict_basic_week_date_time"); - assertSameDate("2018W313T121212.123+01:00", "strict_basic_week_date_time"); - assertParseException("2018W313T12128.123Z", "strict_basic_week_date_time"); - assertParseException("2018W313T12128.123456789Z", "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"); - assertSameDate("2018W313T121212+0100", "strict_basic_week_date_time_no_millis"); - assertSameDate("2018W313T121212+01:00", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12128Z", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12128+0100", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12128+01:00", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T81212Z", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T81212+0100", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T81212+01:00", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12812Z", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12812+0100", "strict_basic_week_date_time_no_millis"); - assertParseException("2018W313T12812+01:00", "strict_basic_week_date_time_no_millis"); - assertSameDate("2018-12-31", "strict_date"); - assertParseException("10000-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.1", "strict_date_hour_minute_second_fraction"); - assertSameDate("2018-12-31T12:12:12.123", "strict_date_hour_minute_second_fraction"); - assertSameDate("2018-12-31T12:12:12.123456789", "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"); - assertParseException("10000-01-31", "strict_date_optional_time"); - assertSameDate("2010-01-05T02:00", "strict_date_optional_time"); - assertSameDate("2018-12-31T10:15:30", "strict_date_optional_time"); - assertSameDate("2018-12-31T10:15:30Z", "strict_date_optional_time"); - assertSameDate("2018-12-31T10:15:30+0100", "strict_date_optional_time"); - assertSameDate("2018-12-31T10:15:30+01:00", "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("2015-01-04T00:00Z", "strict_date_optional_time"); - assertSameDate("2018-12-31T10:15:30.1Z", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.123Z", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.123456789Z", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.1+0100", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.123+0100", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.1+01:00", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.123+01:00", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.11Z", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.11+0100", "strict_date_time"); - assertSameDate("2018-12-31T10:15:30.11+01:00", "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"); - assertSameDate("2018-12-31T10:15:30+0100", "strict_date_time_no_millis"); - assertSameDate("2018-12-31T10:15:30+01:00", "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.123456789", "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.1Z", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123Z", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123456789Z", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.1+0100", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123+0100", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.1+01:00", "strict_ordinal_date_time"); - assertSameDate("2018-128T10:15:30.123+01:00", "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"); - assertSameDate("2018-128T10:15:30+0100", "strict_ordinal_date_time_no_millis"); - assertSameDate("2018-128T10:15:30+01:00", "strict_ordinal_date_time_no_millis"); - assertParseException("2018-1T10:15:30Z", "strict_ordinal_date_time_no_millis"); - - assertSameDate("10:15:30.1Z", "strict_time"); - assertSameDate("10:15:30.123Z", "strict_time"); - assertSameDate("10:15:30.123456789Z", "strict_time"); - assertSameDate("10:15:30.123+0100", "strict_time"); - assertSameDate("10:15:30.123+01:00", "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("10:15:30+0100", "strict_time_no_millis"); - assertSameDate("10:15:30+01:00", "strict_time_no_millis"); - assertSameDate("01:15:30Z", "strict_time_no_millis"); - assertSameDate("01:15:30+0100", "strict_time_no_millis"); - assertSameDate("01:15:30+01:00", "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.1Z", "strict_t_time"); - assertSameDate("T10:15:30.123Z", "strict_t_time"); - assertSameDate("T10:15:30.123456789Z", "strict_t_time"); - assertSameDate("T10:15:30.1+0100", "strict_t_time"); - assertSameDate("T10:15:30.123+0100", "strict_t_time"); - assertSameDate("T10:15:30.1+01:00", "strict_t_time"); - assertSameDate("T10:15:30.123+01:00", "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"); - assertSameDate("T10:15:30+0100", "strict_t_time_no_millis"); - assertSameDate("T10:15:30+01:00", "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"); - - assertSameDate("2012-W48-6T10:15:30.1Z", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123Z", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123456789Z", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.1+0100", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123+0100", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.1+01:00", "strict_week_date_time"); - assertSameDate("2012-W48-6T10:15:30.123+01:00", "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"); - assertSameDate("2012-W48-6T10:15:30+0100", "strict_week_date_time_no_millis"); - assertSameDate("2012-W48-6T10:15:30+01:00", "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); - - for (FormatNames format : FormatNames.values()) { - if (format == FormatNames.ISO8601 || format == FormatNames.STRICT_DATE_OPTIONAL_TIME_NANOS) { - // Nanos aren't supported by joda - continue; - } - assertSamePrinterOutput(format.getSnakeCaseName(), javaDate, jodaDate); - } - } - - public void testSamePrinterOutputWithTimeZone() { - String format = "strict_date_optional_time"; - String dateInput = "2017-02-01T08:02:00.000-01:00"; - DateFormatter javaFormatter = DateFormatter.forPattern(format); - TemporalAccessor javaDate = javaFormatter.parse(dateInput); - - DateFormatter jodaFormatter = Joda.forPattern(format); - DateTime dateTime = jodaFormatter.parseJoda(dateInput); - - String javaDateString = javaFormatter.withZone(ZoneOffset.ofHours(-1)).format(javaDate); - String jodaDateString = jodaFormatter.withZone(ZoneOffset.ofHours(-1)).formatJoda(dateTime); - String message = String.format( - Locale.ROOT, - "expected string representation to be equal for format [%s]: joda [%s], java [%s]", - format, - jodaDateString, - javaDateString - ); - assertThat(message, javaDateString, is(jodaDateString)); - } - - public void testDateFormatterWithLocale() { - Locale locale = randomLocale(random()); - String pattern = randomBoolean() ? "strict_date_optional_time||date_time" : "date_time||strict_date_optional_time"; - DateFormatter formatter = DateFormatter.forPattern(pattern).withLocale(locale); - assertThat(formatter.pattern(), is(pattern)); - assertThat(formatter.locale(), is(locale)); - } - - public void testSeveralTimeFormats() { - { - String format = "year_month_day||ordinal_date"; - DateFormatter jodaFormatter = Joda.forPattern(format); - DateFormatter javaFormatter = DateFormatter.forPattern(format); - assertSameDate("2018-12-12", format, jodaFormatter, javaFormatter); - assertSameDate("2018-128", format, jodaFormatter, javaFormatter); - } - { - String format = "strictDateOptionalTime||dd-MM-yyyy"; - DateFormatter jodaFormatter = Joda.forPattern(format); - DateFormatter javaFormatter = DateFormatter.forPattern(format); - assertSameDate("31-01-2014", format, jodaFormatter, javaFormatter); - } - } - - // the iso 8601 parser is available via Joda.forPattern(), so we have to test this slightly differently - public void testIso8601Parsers() { - String format = "iso8601"; - org.joda.time.format.DateTimeFormatter isoFormatter = ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.UTC); - JodaDateFormatter jodaFormatter = new JodaDateFormatter(format, isoFormatter, isoFormatter); - DateFormatter javaFormatter = DateFormatter.forPattern(format); - - assertSameDate("2018-10-10", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10+0430", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11-08:00", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11Z", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12+0100", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12.123", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12.123Z", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12.123+0000", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12,123", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12,123Z", format, jodaFormatter, javaFormatter); - assertSameDate("2018-10-10T10:11:12,123+05:30", format, jodaFormatter, javaFormatter); - } - - public void testParsingLocalDateFromYearOfEra() { - // with strict resolving, YearOfEra expect an era, otherwise it won't resolve to a date - assertSameDate("2018363", "yyyyDDD", Joda.forPattern("YYYYDDD"), DateFormatter.forPattern("uuuuDDD")); - } - - public void testParsingMissingTimezone() { - long millisJava = DateFormatter.forPattern("8yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17"); - long millisJoda = DateFormatter.forPattern("yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17"); - assertThat(millisJava, is(millisJoda)); - } - - private void assertSamePrinterOutput(String format, ZonedDateTime javaDate, DateTime jodaDate) { - DateFormatter dateFormatter = DateFormatter.forPattern(format); - JodaDateFormatter jodaDateFormatter = Joda.forPattern(format); - - assertSamePrinterOutput(format, javaDate, jodaDate, dateFormatter, jodaDateFormatter); - } - - private void assertSamePrinterOutput( - String format, - ZonedDateTime javaDate, - DateTime jodaDate, - DateFormatter dateFormatter, - DateFormatter jodaDateFormatter - ) { - String javaTimeOut = dateFormatter.format(javaDate); - String jodaTimeOut = jodaDateFormatter.formatJoda(jodaDate); - - assertThat(jodaDate.getMillis(), is(javaDate.toInstant().toEpochMilli())); - String message = String.format( - Locale.ROOT, - "expected string representation to be equal for format [%s]: joda [%s], java [%s]", - format, - jodaTimeOut, - javaTimeOut - ); - assertThat(message, javaTimeOut, is(jodaTimeOut)); - } - - private void assertSameDate(String input, String format) { - DateFormatter jodaFormatter = Joda.forPattern(format); - DateFormatter javaFormatter = DateFormatter.forPattern(format); - assertSameDate(input, format, jodaFormatter, javaFormatter); - } - - private void assertSameDate(String input, String format, DateFormatter jodaFormatter, DateFormatter javaFormatter) { - DateTime jodaDateTime = jodaFormatter.parseJoda(input); - - TemporalAccessor javaTimeAccessor = javaFormatter.parse(input); - ZonedDateTime zonedDateTime = DateFormatters.from(javaTimeAccessor); - - String msg = String.format( - Locale.ROOT, - "Input [%s] Format [%s] Joda [%s], Java [%s]", - input, - format, - jodaDateTime, - DateTimeFormatter.ISO_INSTANT.format(zonedDateTime.toInstant()) - ); - - assertThat(msg, jodaDateTime.getMillis(), is(zonedDateTime.toInstant().toEpochMilli())); - } - - private void assertParseException(String input, String format) { - assertJodaParseException(input, format, "Invalid format: \"" + input); - assertJavaTimeParseException(input, format); - } - - private void assertJodaParseException(String input, String format, String expectedMessage) { - DateFormatter jodaFormatter = Joda.forPattern(format); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> jodaFormatter.parseJoda(input)); - assertThat(e.getMessage(), containsString(expectedMessage)); - } - - private void assertJavaTimeParseException(String input, String format) { - DateFormatter javaTimeFormatter = DateFormatter.forPattern(format); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> javaTimeFormatter.parse(input)); - assertThat(e.getMessage(), containsString(input)); - assertThat(e.getMessage(), containsString(format)); - } - - private void assertDateMathEquals(String text, String pattern) { - long gotMillisJava = dateMathToMillis(text, DateFormatter.forPattern(pattern)); - long gotMillisJoda = dateMathToMillis(text, Joda.forPattern(pattern)); - - assertEquals(gotMillisJoda, gotMillisJava); - } - - private void assertSameDateAs(String input, String javaPattern, String jodaPattern) { - DateFormatter javaFormatter = DateFormatter.forPattern(javaPattern); - DateFormatter jodaFormatter = Joda.forPattern(jodaPattern); - assertSameDate(input, javaPattern, jodaFormatter, javaFormatter); - } -} diff --git a/server/src/test/java/org/opensearch/common/joda/JodaDateMathParserTests.java b/server/src/test/java/org/opensearch/common/joda/JodaDateMathParserTests.java deleted file mode 100644 index fe0f707f4ca3e..0000000000000 --- a/server/src/test/java/org/opensearch/common/joda/JodaDateMathParserTests.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.OpenSearchParseException; -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.DateMathParser; -import org.opensearch.test.OpenSearchTestCase; -import org.joda.time.DateTimeZone; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.LongSupplier; - -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; - -public class JodaDateMathParserTests extends OpenSearchTestCase { - - DateFormatter formatter = Joda.forPattern("date_optional_time||epoch_millis"); - DateMathParser parser = formatter.toDateMathParser(); - - void assertDateMathEquals(String toTest, String expected) { - assertDateMathEquals(toTest, expected, 0, false, null); - } - - void assertDateMathEquals(String toTest, String expected, final long now, boolean roundUp, DateTimeZone timeZone) { - long gotMillis = parser.parse(toTest, () -> now, roundUp, timeZone).toEpochMilli(); - assertDateEquals(gotMillis, toTest, expected); - } - - void assertDateEquals(long gotMillis, String original, String expected) { - long expectedMillis = parser.parse(expected, () -> 0).toEpochMilli(); - if (gotMillis != expectedMillis) { - fail( - "Date math not equal\n" - + "Original : " - + original - + "\n" - + "Parsed : " - + formatter.formatMillis(gotMillis) - + "\n" - + "Expected : " - + expected - + "\n" - + "Expected milliseconds : " - + expectedMillis - + "\n" - + "Actual milliseconds : " - + gotMillis - + "\n" - ); - } - } - - public void testOverridingLocaleOrZoneAndCompositeRoundUpParser() { - // the pattern has to be composite and the match should not be on the first one - DateFormatter formatter = Joda.forPattern("date||epoch_millis").withLocale(randomLocale(random())); - DateMathParser parser = formatter.toDateMathParser(); - long gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null).toEpochMilli(); - assertDateEquals(gotMillis, "297276785531", "297276785531"); - - formatter = Joda.forPattern("date||epoch_millis").withZone(ZoneOffset.UTC); - parser = formatter.toDateMathParser(); - gotMillis = parser.parse("297276785531", () -> 0, true, (ZoneId) null).toEpochMilli(); - assertDateEquals(gotMillis, "297276785531", "297276785531"); - } - - public void testBasicDates() { - assertDateMathEquals("2014", "2014-01-01T00:00:00.000"); - assertDateMathEquals("2014-05", "2014-05-01T00:00:00.000"); - assertDateMathEquals("2014-05-30", "2014-05-30T00:00:00.000"); - assertDateMathEquals("2014-05-30T20", "2014-05-30T20:00:00.000"); - assertDateMathEquals("2014-05-30T20:21", "2014-05-30T20:21:00.000"); - assertDateMathEquals("2014-05-30T20:21:35", "2014-05-30T20:21:35.000"); - assertDateMathEquals("2014-05-30T20:21:35.123", "2014-05-30T20:21:35.123"); - } - - public void testRoundingDoesNotAffectExactDate() { - assertDateMathEquals("2014-11-12T22:55:00.000Z", "2014-11-12T22:55:00.000Z", 0, true, null); - assertDateMathEquals("2014-11-12T22:55:00.000Z", "2014-11-12T22:55:00.000Z", 0, false, null); - - assertDateMathEquals("2014-11-12T22:55:00.000", "2014-11-12T21:55:00.000Z", 0, true, DateTimeZone.forID("+01:00")); - assertDateMathEquals("2014-11-12T22:55:00.000", "2014-11-12T21:55:00.000Z", 0, false, DateTimeZone.forID("+01:00")); - - assertDateMathEquals("2014-11-12T22:55:00.000+01:00", "2014-11-12T21:55:00.000Z", 0, true, null); - assertDateMathEquals("2014-11-12T22:55:00.000+01:00", "2014-11-12T21:55:00.000Z", 0, false, null); - } - - public void testTimezone() { - // timezone works within date format - assertDateMathEquals("2014-05-30T20:21+02:00", "2014-05-30T18:21:00.000"); - - // test alternative ways of writing zero offsets, according to ISO 8601 +00:00, +00, +0000 should work. - // joda also seems to allow for -00:00, -00, -0000 - assertDateMathEquals("2014-05-30T18:21+00:00", "2014-05-30T18:21:00.000"); - assertDateMathEquals("2014-05-30T18:21+00", "2014-05-30T18:21:00.000"); - assertDateMathEquals("2014-05-30T18:21+0000", "2014-05-30T18:21:00.000"); - assertDateMathEquals("2014-05-30T18:21-00:00", "2014-05-30T18:21:00.000"); - assertDateMathEquals("2014-05-30T18:21-00", "2014-05-30T18:21:00.000"); - assertDateMathEquals("2014-05-30T18:21-0000", "2014-05-30T18:21:00.000"); - - // but also externally - assertDateMathEquals("2014-05-30T20:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("+02:00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("+00:00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("+00:00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("+00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("+0000")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("-00:00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("-00")); - assertDateMathEquals("2014-05-30T18:21", "2014-05-30T18:21:00.000", 0, false, DateTimeZone.forID("-0000")); - - // and timezone in the date has priority - assertDateMathEquals("2014-05-30T20:21+03:00", "2014-05-30T17:21:00.000", 0, false, DateTimeZone.forID("-08:00")); - assertDateMathEquals("2014-05-30T20:21Z", "2014-05-30T20:21:00.000", 0, false, DateTimeZone.forID("-08:00")); - } - - public void testBasicMath() { - assertDateMathEquals("2014-11-18||+y", "2015-11-18"); - assertDateMathEquals("2014-11-18||-2y", "2012-11-18"); - - assertDateMathEquals("2014-11-18||+3M", "2015-02-18"); - assertDateMathEquals("2014-11-18||-M", "2014-10-18"); - - assertDateMathEquals("2014-11-18||+1w", "2014-11-25"); - assertDateMathEquals("2014-11-18||-3w", "2014-10-28"); - - assertDateMathEquals("2014-11-18||+22d", "2014-12-10"); - assertDateMathEquals("2014-11-18||-423d", "2013-09-21"); - - assertDateMathEquals("2014-11-18T14||+13h", "2014-11-19T03"); - assertDateMathEquals("2014-11-18T14||-1h", "2014-11-18T13"); - assertDateMathEquals("2014-11-18T14||+13H", "2014-11-19T03"); - assertDateMathEquals("2014-11-18T14||-1H", "2014-11-18T13"); - - assertDateMathEquals("2014-11-18T14:27||+10240m", "2014-11-25T17:07"); - assertDateMathEquals("2014-11-18T14:27||-10m", "2014-11-18T14:17"); - - assertDateMathEquals("2014-11-18T14:27:32||+60s", "2014-11-18T14:28:32"); - assertDateMathEquals("2014-11-18T14:27:32||-3600s", "2014-11-18T13:27:32"); - } - - public void testLenientEmptyMath() { - assertDateMathEquals("2014-05-30T20:21||", "2014-05-30T20:21:00.000"); - } - - public void testMultipleAdjustments() { - assertDateMathEquals("2014-11-18||+1M-1M", "2014-11-18"); - assertDateMathEquals("2014-11-18||+1M-1m", "2014-12-17T23:59"); - assertDateMathEquals("2014-11-18||-1m+1M", "2014-12-17T23:59"); - assertDateMathEquals("2014-11-18||+1M/M", "2014-12-01"); - assertDateMathEquals("2014-11-18||+1M/M+1h", "2014-12-01T01"); - } - - public void testNow() { - final long now = parser.parse("2014-11-18T14:27:32", () -> 0, false, (ZoneId) null).toEpochMilli(); - - assertDateMathEquals("now", "2014-11-18T14:27:32", now, false, null); - assertDateMathEquals("now+M", "2014-12-18T14:27:32", now, false, null); - assertDateMathEquals("now+M", "2014-12-18T14:27:32", now, true, null); - assertDateMathEquals("now-2d", "2014-11-16T14:27:32", now, false, null); - assertDateMathEquals("now-2d", "2014-11-16T14:27:32", now, true, null); - assertDateMathEquals("now/m", "2014-11-18T14:27", now, false, null); - assertDateMathEquals("now/m", "2014-11-18T14:27:59.999Z", now, true, null); - assertDateMathEquals("now/M", "2014-11-01T00:00:00", now, false, null); - assertDateMathEquals("now/M", "2014-11-30T23:59:59.999Z", now, true, null); - - // timezone does not affect now - assertDateMathEquals("now/m", "2014-11-18T14:27", now, false, DateTimeZone.forID("+02:00")); - } - - public void testRoundingPreservesEpochAsBaseDate() { - // If a user only specifies times, then the date needs to always be 1970-01-01 regardless of rounding - DateFormatter formatter = DateFormatter.forPattern("HH:mm:ss"); - DateMathParser parser = formatter.toDateMathParser(); - assertEquals( - this.formatter.parseMillis("1970-01-01T04:52:20.000Z"), - parser.parse("04:52:20", () -> 0, false, (ZoneId) null).toEpochMilli() - ); - assertEquals( - this.formatter.parseMillis("1970-01-01T04:52:20.999Z"), - parser.parse("04:52:20", () -> 0, true, (ZoneId) null).toEpochMilli() - ); - } - - // Implicit rounding happening when parts of the date are not specified - public void testImplicitRounding() { - assertDateMathEquals("2014-11-18", "2014-11-18", 0, false, null); - assertDateMathEquals("2014-11-18", "2014-11-18T23:59:59.999Z", 0, true, null); - - assertDateMathEquals("2014-11-18T09:20", "2014-11-18T09:20", 0, false, null); - assertDateMathEquals("2014-11-18T09:20", "2014-11-18T09:20:59.999Z", 0, true, null); - - assertDateMathEquals("2014-11-18", "2014-11-17T23:00:00.000Z", 0, false, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-11-18", "2014-11-18T22:59:59.999Z", 0, true, DateTimeZone.forID("CET")); - - assertDateMathEquals("2014-11-18T09:20", "2014-11-18T08:20:00.000Z", 0, false, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-11-18T09:20", "2014-11-18T08:20:59.999Z", 0, true, DateTimeZone.forID("CET")); - - // implicit rounding with explicit timezone in the date format - DateFormatter formatter = Joda.forPattern("yyyy-MM-ddZ"); - DateMathParser parser = formatter.toDateMathParser(); - Instant time = parser.parse("2011-10-09+01:00", () -> 0, false, (ZoneId) null); - assertEquals(this.parser.parse("2011-10-09T00:00:00.000+01:00", () -> 0), time); - time = parser.parse("2011-10-09+01:00", () -> 0, true, (ZoneId) null); - assertEquals(this.parser.parse("2011-10-09T23:59:59.999+01:00", () -> 0), time); - } - - // Explicit rounding using the || separator - public void testExplicitRounding() { - assertDateMathEquals("2014-11-18||/y", "2014-01-01", 0, false, null); - assertDateMathEquals("2014-11-18||/y", "2014-12-31T23:59:59.999", 0, true, null); - assertDateMathEquals("2014||/y", "2014-01-01", 0, false, null); - assertDateMathEquals("2014-01-01T00:00:00.001||/y", "2014-12-31T23:59:59.999", 0, true, null); - // rounding should also take into account time zone - assertDateMathEquals("2014-11-18||/y", "2013-12-31T23:00:00.000Z", 0, false, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-11-18||/y", "2014-12-31T22:59:59.999Z", 0, true, DateTimeZone.forID("CET")); - - assertDateMathEquals("2014-11-18||/M", "2014-11-01", 0, false, null); - assertDateMathEquals("2014-11-18||/M", "2014-11-30T23:59:59.999", 0, true, null); - assertDateMathEquals("2014-11||/M", "2014-11-01", 0, false, null); - assertDateMathEquals("2014-11||/M", "2014-11-30T23:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18||/M", "2014-10-31T23:00:00.000Z", 0, false, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-11-18||/M", "2014-11-30T22:59:59.999Z", 0, true, DateTimeZone.forID("CET")); - - assertDateMathEquals("2014-11-17T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-18T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-19T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-20T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-21T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-22T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-23T14||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-18T14||/w", "2014-11-23T23:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18||/w", "2014-11-17", 0, false, null); - assertDateMathEquals("2014-11-18||/w", "2014-11-23T23:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18||/w", "2014-11-16T23:00:00.000Z", 0, false, DateTimeZone.forID("+01:00")); - assertDateMathEquals("2014-11-18||/w", "2014-11-17T01:00:00.000Z", 0, false, DateTimeZone.forID("-01:00")); - assertDateMathEquals("2014-11-18||/w", "2014-11-16T23:00:00.000Z", 0, false, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-11-18||/w", "2014-11-23T22:59:59.999Z", 0, true, DateTimeZone.forID("CET")); - assertDateMathEquals("2014-07-22||/w", "2014-07-20T22:00:00.000Z", 0, false, DateTimeZone.forID("CET")); // with DST - - assertDateMathEquals("2014-11-18T14||/d", "2014-11-18", 0, false, null); - assertDateMathEquals("2014-11-18T14||/d", "2014-11-18T23:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18||/d", "2014-11-18", 0, false, null); - assertDateMathEquals("2014-11-18||/d", "2014-11-18T23:59:59.999", 0, true, null); - - assertDateMathEquals("2014-11-18T14:27||/h", "2014-11-18T14", 0, false, null); - assertDateMathEquals("2014-11-18T14:27||/h", "2014-11-18T14:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18T14||/H", "2014-11-18T14", 0, false, null); - assertDateMathEquals("2014-11-18T14||/H", "2014-11-18T14:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18T14:27||/h", "2014-11-18T14", 0, false, null); - assertDateMathEquals("2014-11-18T14:27||/h", "2014-11-18T14:59:59.999", 0, true, null); - assertDateMathEquals("2014-11-18T14||/H", "2014-11-18T14", 0, false, null); - assertDateMathEquals("2014-11-18T14||/H", "2014-11-18T14:59:59.999", 0, true, null); - - assertDateMathEquals("2014-11-18T14:27:32||/m", "2014-11-18T14:27", 0, false, null); - assertDateMathEquals("2014-11-18T14:27:32||/m", "2014-11-18T14:27:59.999", 0, true, null); - assertDateMathEquals("2014-11-18T14:27||/m", "2014-11-18T14:27", 0, false, null); - assertDateMathEquals("2014-11-18T14:27||/m", "2014-11-18T14:27:59.999", 0, true, null); - - assertDateMathEquals("2014-11-18T14:27:32.123||/s", "2014-11-18T14:27:32", 0, false, null); - assertDateMathEquals("2014-11-18T14:27:32.123||/s", "2014-11-18T14:27:32.999", 0, true, null); - assertDateMathEquals("2014-11-18T14:27:32||/s", "2014-11-18T14:27:32", 0, false, null); - assertDateMathEquals("2014-11-18T14:27:32||/s", "2014-11-18T14:27:32.999", 0, true, null); - } - - public void testTimestamps() { - assertDateMathEquals("1418248078000", "2014-12-10T21:47:58.000"); - assertDateMathEquals("32484216259000", "2999-05-20T17:24:19.000"); - assertDateMathEquals("253382837059000", "9999-05-20T17:24:19.000"); - - // datemath still works on timestamps - assertDateMathEquals("1418248078000||/m", "2014-12-10T21:47:00.000"); - - // also check other time units - JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("epoch_second")); - long datetime = parser.parse("1418248078", () -> 0).toEpochMilli(); - assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000"); - - // a timestamp before 10000 is a year - assertDateMathEquals("9999", "9999-01-01T00:00:00.000"); - // 10000 is also a year, breaking bwc, used to be a timestamp - assertDateMathEquals("10000", "10000-01-01T00:00:00.000"); - // but 10000 with T is still a date format - assertDateMathEquals("10000T", "10000-01-01T00:00:00.000"); - } - - void assertParseException(String msg, String date, String exc) { - try { - parser.parse(date, () -> 0); - fail("Date: " + date + "\n" + msg); - } catch (OpenSearchParseException e) { - assertThat(e.getMessage().contains(exc), equalTo(true)); - } - } - - public void testIllegalMathFormat() { - assertParseException("Expected date math unsupported operator exception", "2014-11-18||*5", "operator not supported"); - assertParseException("Expected date math incompatible rounding exception", "2014-11-18||/2m", "rounding"); - assertParseException("Expected date math illegal unit type exception", "2014-11-18||+2a", "unit [a] not supported"); - assertParseException("Expected date math truncation exception", "2014-11-18||+12", "truncated"); - assertParseException("Expected date math truncation exception", "2014-11-18||-", "truncated"); - } - - public void testIllegalDateFormat() { - assertParseException("Expected bad timestamp exception", Long.toString(Long.MAX_VALUE) + "0", "failed to parse date field"); - assertParseException("Expected bad date format exception", "123bogus", "with format"); - } - - public void testOnlyCallsNowIfNecessary() { - final AtomicBoolean called = new AtomicBoolean(); - final LongSupplier now = () -> { - called.set(true); - return 42L; - }; - parser.parse("2014-11-18T14:27:32", now, false, (ZoneId) null); - assertFalse(called.get()); - parser.parse("now/d", now, false, (ZoneId) null); - assertTrue(called.get()); - } - - public void testThatUnixTimestampMayNotHaveTimeZone() { - JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("epoch_millis")); - try { - parser.parse("1234567890123", () -> 42, false, ZoneId.of("CET")); - fail("Expected OpenSearchParseException"); - } catch (OpenSearchParseException e) { - assertThat(e.getMessage(), containsString("failed to parse date field")); - assertThat(e.getMessage(), containsString("with format [epoch_millis]")); - } - } -} diff --git a/server/src/test/java/org/opensearch/common/joda/JodaTests.java b/server/src/test/java/org/opensearch/common/joda/JodaTests.java deleted file mode 100644 index 3f6e2b2ca918a..0000000000000 --- a/server/src/test/java/org/opensearch/common/joda/JodaTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.joda; - -import org.opensearch.common.time.DateFormatter; -import org.opensearch.test.OpenSearchTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; - -import java.time.ZoneOffset; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; - -public class JodaTests extends OpenSearchTestCase { - - public void testBasicTTimePattern() { - DateFormatter formatter1 = Joda.forPattern("basic_t_time"); - assertEquals(formatter1.pattern(), "basic_t_time"); - assertEquals(formatter1.zone(), ZoneOffset.UTC); - - DateFormatter formatter2 = Joda.forPattern("basicTTime"); - assertEquals(formatter2.pattern(), "basicTTime"); - assertEquals(formatter2.zone(), ZoneOffset.UTC); - assertWarnings( - "Camel case format name basicTTime is deprecated and will be removed in a future version. " - + "Use snake case name basic_t_time instead." - ); - DateTime dt = new DateTime(2004, 6, 9, 10, 20, 30, 40, DateTimeZone.UTC); - assertEquals("T102030.040Z", formatter1.formatJoda(dt)); - assertEquals("T102030.040Z", formatter1.formatJoda(dt)); - - expectThrows(IllegalArgumentException.class, () -> Joda.forPattern("basic_t_Time")); - expectThrows(IllegalArgumentException.class, () -> Joda.forPattern("basic_T_Time")); - expectThrows(IllegalArgumentException.class, () -> Joda.forPattern("basic_T_time")); - } - - public void testEqualsAndHashcode() { - String format = randomFrom("yyyy/MM/dd HH:mm:ss", "basic_t_time"); - JodaDateFormatter first = Joda.forPattern(format); - JodaDateFormatter second = Joda.forPattern(format); - JodaDateFormatter third = Joda.forPattern(" HH:mm:ss, yyyy/MM/dd"); - - assertThat(first, is(second)); - assertThat(second, is(first)); - assertThat(first, is(not(third))); - assertThat(second, is(not(third))); - - assertThat(first.hashCode(), is(second.hashCode())); - assertThat(second.hashCode(), is(first.hashCode())); - assertThat(first.hashCode(), is(not(third.hashCode()))); - assertThat(second.hashCode(), is(not(third.hashCode()))); - } -} diff --git a/server/src/test/java/org/opensearch/common/time/DateFormattersTests.java b/server/src/test/java/org/opensearch/common/time/DateFormattersTests.java index 681daf1755890..c8560134dc208 100644 --- a/server/src/test/java/org/opensearch/common/time/DateFormattersTests.java +++ b/server/src/test/java/org/opensearch/common/time/DateFormattersTests.java @@ -32,7 +32,7 @@ package org.opensearch.common.time; -import org.opensearch.common.joda.Joda; +import org.opensearch.OpenSearchParseException; import org.opensearch.test.OpenSearchTestCase; import java.time.Clock; @@ -40,6 +40,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.util.Locale; @@ -580,107 +581,734 @@ public void testWeek_yearDeprecation() { ); } - public void testCamelCaseDeprecation() { - String[] deprecatedNames = new String[] { - "basicDate", - "basicDateTime", - "basicDateTimeNoMillis", - "basicOrdinalDate", - "basicOrdinalDateTime", - "basicOrdinalDateTimeNoMillis", - "basicTime", - "basicTimeNoMillis", - "basicTTime", - "basicTTimeNoMillis", - "basicWeekDate", - "basicWeekDateTime", - "basicWeekDateTimeNoMillis", - "dateHour", - "dateHourMinute", - "dateHourMinuteSecond", - "dateHourMinuteSecondFraction", - "dateHourMinuteSecondMillis", - "dateOptionalTime", - "dateTime", - "dateTimeNoMillis", - "hourMinute", - "hourMinuteSecond", - "hourMinuteSecondFraction", - "hourMinuteSecondMillis", - "ordinalDate", - "ordinalDateTime", - "ordinalDateTimeNoMillis", - "timeNoMillis", - "tTime", - "tTimeNoMillis", - "weekDate", - "weekDateTime", - "weekDateTimeNoMillis", - "weekyearWeek", - "weekyearWeekDay", - "yearMonth", - "yearMonthDay", - "strictBasicWeekDate", - "strictBasicWeekDateTime", - "strictBasicWeekDateTimeNoMillis", - "strictDate", - "strictDateHour", - "strictDateHourMinute", - "strictDateHourMinuteSecond", - "strictDateHourMinuteSecondFraction", - "strictDateHourMinuteSecondMillis", - "strictDateOptionalTime", - "strictDateOptionalTimeNanos", - "strictDateTime", - "strictDateTimeNoMillis", - "strictHour", - "strictHourMinute", - "strictHourMinuteSecond", - "strictHourMinuteSecondFraction", - "strictHourMinuteSecondMillis", - "strictOrdinalDate", - "strictOrdinalDateTime", - "strictOrdinalDateTimeNoMillis", - "strictTime", - "strictTimeNoMillis", - "strictTTime", - "strictTTimeNoMillis", - "strictWeekDate", - "strictWeekDateTime", - "strictWeekDateTimeNoMillis", - "strictWeekyear", - "strictWeekyearWeek", - "strictWeekyearWeekDay", - "strictYear", - "strictYearMonth", - "strictYearMonthDay" }; - for (String name : deprecatedNames) { - String snakeCaseName = FormatNames.forName(name).getSnakeCaseName(); - - DateFormatter dateFormatter = DateFormatter.forPattern(name); - assertThat(dateFormatter.pattern(), equalTo(name)); - assertWarnings( - "Camel case format name " - + name - + " is deprecated and will be removed in a future version. " - + "Use snake case name " - + snakeCaseName - + " instead." - ); - - dateFormatter = DateFormatter.forPattern(snakeCaseName); - assertThat(dateFormatter.pattern(), equalTo(snakeCaseName)); - } - - for (String name : deprecatedNames) { - if (name.equals("strictDateOptionalTimeNanos") == false) { - DateFormatter dateFormatter = Joda.forPattern(name); - assertThat(dateFormatter.pattern(), equalTo(name)); - - String snakeCaseName = FormatNames.forName(name).getSnakeCaseName(); - dateFormatter = Joda.forPattern(snakeCaseName); - assertThat(dateFormatter.pattern(), equalTo(snakeCaseName)); - } + public void testTimezoneParsing() { + /** this testcase won't work in joda. See comment in {@link #testPartialTimeParsing()} + * assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); + */ + assertSameDateAs("2016-11-30T00+01", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T00+0100", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T00+01:00", "strict_date_optional_time"); + } + + public void testPartialTimeParsing() { + /* + This does not work in Joda as it reports 2016-11-30T01:00:00Z + because strict_date_optional_time confuses +01 with an hour (which is a signed fixed length digit) + assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time"); + ES java.time implementation does not suffer from this, + but we intentionally not allow parsing timezone without an time part as it is not allowed in iso8601 + */ + assertParseException("2016-11-30T+01", "strict_date_optional_time"); + + assertSameDateAs("2016-11-30T12+01", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00+01", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00+01", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00.000+01", "strict_date_optional_time"); + + // without timezone + assertSameDateAs("2016-11-30T", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00", "strict_date_optional_time"); + assertSameDateAs("2016-11-30T12:00:00.000", "strict_date_optional_time"); + } + + // date_optional part of a parser names "strict_date_optional_time" or "date_optional"time + // means that date part can be partially parsed. + public void testPartialDateParsing() { + assertSameDateAs("2001", "strict_date_optional_time_nanos"); + assertSameDateAs("2001-01", "strict_date_optional_time_nanos"); + assertSameDateAs("2001-01-01", "strict_date_optional_time_nanos"); + + assertSameDateAs("2001", "strict_date_optional_time"); + assertSameDateAs("2001-01", "strict_date_optional_time"); + assertSameDateAs("2001-01-01", "strict_date_optional_time"); + + assertSameDateAs("2001", "date_optional_time"); + assertSameDateAs("2001-01", "date_optional_time"); + assertSameDateAs("2001-01-01", "date_optional_time"); + + assertSameDateAs("2001", "iso8601"); + assertSameDateAs("2001-01", "iso8601"); + assertSameDateAs("2001-01-01", "iso8601"); + + assertSameDateAs("9999", "date_optional_time||epoch_second"); + } + + public void testCompositeDateMathParsing() { + // in all these examples the second pattern will be used + assertDateMathEquals("2014-06-06T12:01:02.123", "2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertDateMathEquals("2014-06-06T12:01:02.123", "2014-06-06T12:01:02.123", "strict_date_time_no_millis||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertDateMathEquals( + "2014-06-06T12:01:02.123", + "2014-06-06T12:01:02.123", + "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS" + ); + } + + public void testExceptionWhenCompositeParsingFailsDateMath() { + // both parsing failures should contain pattern and input text in exception + // both patterns fail parsing the input text due to only 2 digits of millis. Hence full text was not parsed. + String pattern = "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"; + String text = "2014-06-06T12:01:02.123"; + OpenSearchParseException e1 = expectThrows( + OpenSearchParseException.class, + () -> dateMathToMillis(text, DateFormatter.forPattern(pattern)) + ); + assertThat(e1.getMessage(), containsString(pattern)); + assertThat(e1.getMessage(), containsString(text)); + } + + // these parsers should allow both ',' and '.' as a decimal point + public void testDecimalPointParsing() { + assertSameDateAs("2001-01-01T00:00:00.123Z", "strict_date_optional_time"); + assertSameDateAs("2001-01-01T00:00:00,123Z", "strict_date_optional_time"); + + assertSameDateAs("2001-01-01T00:00:00.123Z", "date_optional_time"); + assertSameDateAs("2001-01-01T00:00:00,123Z", "date_optional_time"); + + // only java.time has nanos parsing, but the results for 3digits should be the same + DateFormatter javaFormatter = DateFormatter.forPattern("strict_date_optional_time_nanos"); + assertSameDate("2001-01-01T00:00:00.123Z", javaFormatter); + assertSameDate("2001-01-01T00:00:00,123Z", javaFormatter); + + assertParseException("2001-01-01T00:00:00.123,456Z", "strict_date_optional_time"); + assertParseException("2001-01-01T00:00:00.123,456Z", "date_optional_time"); + // This should fail, but java is ok with this because the field has the same value + // assertJavaTimeParseException("2001-01-01T00:00:00.123,123Z", "strict_date_optional_time_nanos"); + } + + public void testTimeZoneFormatting() { + assertSameDateAs("2001-01-01T00:00:00Z", "date_time_no_millis"); + // the following fail under java 8 but work under java 10, needs investigation + assertSameDateAs("2001-01-01T00:00:00-0800", "date_time_no_millis"); + assertSameDateAs("2001-01-01T00:00:00+1030", "date_time_no_millis"); + assertSameDateAs("2001-01-01T00:00:00-08", "date_time_no_millis"); + assertSameDateAs("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() { + assertSameDateAs("2010 12 06 11:05:15", "yyyy dd MM HH:mm:ss"); + assertSameDateAs("12/06", "dd/MM"); + assertSameDateAs("Nov 24 01:29:01 -0800", "MMM dd HH:mm:ss Z"); + } + + public void testFormatsValidParsing() { + assertSameDateAs("1522332219", "epoch_second"); + assertSameDateAs("0", "epoch_second"); + assertSameDateAs("1", "epoch_second"); + assertSameDateAs("1522332219321", "epoch_millis"); + assertSameDateAs("0", "epoch_millis"); + assertSameDateAs("1", "epoch_millis"); + + assertSameDateAs("20181126", "basic_date"); + assertSameDateAs("20181126T121212.123Z", "basic_date_time"); + assertSameDateAs("20181126T121212.123+10:00", "basic_date_time"); + assertSameDateAs("20181126T121212.123-0800", "basic_date_time"); + + assertSameDateAs("20181126T121212Z", "basic_date_time_no_millis"); + assertSameDateAs("20181126T121212+01:00", "basic_date_time_no_millis"); + assertSameDateAs("20181126T121212+0100", "basic_date_time_no_millis"); + assertSameDateAs("2018363", "basic_ordinal_date"); + assertSameDateAs("2018363T121212.1Z", "basic_ordinal_date_time"); + assertSameDateAs("2018363T121212.123Z", "basic_ordinal_date_time"); + assertSameDateAs("2018363T121212.123456789Z", "basic_ordinal_date_time"); + assertSameDateAs("2018363T121212.123+0100", "basic_ordinal_date_time"); + assertSameDateAs("2018363T121212.123+01:00", "basic_ordinal_date_time"); + assertSameDateAs("2018363T121212Z", "basic_ordinal_date_time_no_millis"); + assertSameDateAs("2018363T121212+0100", "basic_ordinal_date_time_no_millis"); + assertSameDateAs("2018363T121212+01:00", "basic_ordinal_date_time_no_millis"); + assertSameDateAs("121212.1Z", "basic_time"); + assertSameDateAs("121212.123Z", "basic_time"); + assertSameDateAs("121212.123456789Z", "basic_time"); + assertSameDateAs("121212.1+0100", "basic_time"); + assertSameDateAs("121212.123+0100", "basic_time"); + assertSameDateAs("121212.123+01:00", "basic_time"); + assertSameDateAs("121212Z", "basic_time_no_millis"); + assertSameDateAs("121212+0100", "basic_time_no_millis"); + assertSameDateAs("121212+01:00", "basic_time_no_millis"); + assertSameDateAs("T121212.1Z", "basic_t_time"); + assertSameDateAs("T121212.123Z", "basic_t_time"); + assertSameDateAs("T121212.123456789Z", "basic_t_time"); + assertSameDateAs("T121212.1+0100", "basic_t_time"); + assertSameDateAs("T121212.123+0100", "basic_t_time"); + assertSameDateAs("T121212.123+01:00", "basic_t_time"); + assertSameDateAs("T121212Z", "basic_t_time_no_millis"); + assertSameDateAs("T121212+0100", "basic_t_time_no_millis"); + assertSameDateAs("T121212+01:00", "basic_t_time_no_millis"); + assertSameDateAs("2018W313", "basic_week_date"); + assertSameDateAs("1W313", "basic_week_date"); + assertSameDateAs("18W313", "basic_week_date"); + assertSameDateAs("2018W313T121212.1Z", "basic_week_date_time"); + assertSameDateAs("2018W313T121212.123Z", "basic_week_date_time"); + assertSameDateAs("2018W313T121212.123456789Z", "basic_week_date_time"); + assertSameDateAs("2018W313T121212.123+0100", "basic_week_date_time"); + assertSameDateAs("2018W313T121212.123+01:00", "basic_week_date_time"); + assertSameDateAs("2018W313T121212Z", "basic_week_date_time_no_millis"); + assertSameDateAs("2018W313T121212+0100", "basic_week_date_time_no_millis"); + assertSameDateAs("2018W313T121212+01:00", "basic_week_date_time_no_millis"); + + assertSameDateAs("2018-12-31", "date"); + assertSameDateAs("18-5-6", "date"); + assertSameDateAs("10000-5-6", "date"); + + assertSameDateAs("2018-12-31T12", "date_hour"); + assertSameDateAs("2018-12-31T8", "date_hour"); + + assertSameDateAs("2018-12-31T12:12", "date_hour_minute"); + assertSameDateAs("2018-12-31T8:3", "date_hour_minute"); + + assertSameDateAs("2018-12-31T12:12:12", "date_hour_minute_second"); + assertSameDateAs("2018-12-31T12:12:1", "date_hour_minute_second"); + + assertSameDateAs("2018-12-31T12:12:12.1", "date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.123", "date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.123456789", "date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.1", "date_hour_minute_second_millis"); + assertSameDateAs("2018-12-31T12:12:12.123", "date_hour_minute_second_millis"); + assertParseException("2018-12-31T12:12:12.123456789", "date_hour_minute_second_millis"); + assertSameDateAs("2018-12-31T12:12:12.1", "date_hour_minute_second_millis"); + assertSameDateAs("2018-12-31T12:12:12.1", "date_hour_minute_second_fraction"); + + assertSameDateAs("2018-05", "date_optional_time"); + assertSameDateAs("2018-05-30", "date_optional_time"); + assertSameDateAs("2018-05-30T20", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.1", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123456789", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123Z", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123456789Z", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.1+0100", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123+0100", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.1+01:00", "date_optional_time"); + assertSameDateAs("2018-05-30T20:21:23.123+01:00", "date_optional_time"); + assertSameDateAs("2018-12-1", "date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30", "date_optional_time"); + assertSameDateAs("2018-12-31T10:15:3", "date_optional_time"); + assertSameDateAs("2018-12-31T10:5:30", "date_optional_time"); + assertSameDateAs("2018-12-31T1:15:30", "date_optional_time"); + + assertSameDateAs("2018-12-31T10:15:30.1Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.123Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.123456789Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.1+0100", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.123+0100", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.123+01:00", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.1+01:00", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.11Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.11+0100", "date_time"); + assertSameDateAs("2018-12-31T10:15:30.11+01:00", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.1Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.123Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.123456789Z", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.1+0100", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.123+0100", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.123+01:00", "date_time"); + assertSameDateAs("2018-12-31T10:15:3.1+01:00", "date_time"); + + assertSameDateAs("2018-12-31T10:15:30Z", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:30+0100", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:30+01:00", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:5:30Z", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:5:30+0100", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:5:30+01:00", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:3Z", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:3+0100", "date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:3+01:00", "date_time_no_millis"); + assertSameDateAs("2018-12-31T1:15:30Z", "date_time_no_millis"); + assertSameDateAs("2018-12-31T1:15:30+0100", "date_time_no_millis"); + assertSameDateAs("2018-12-31T1:15:30+01:00", "date_time_no_millis"); + + assertSameDateAs("12", "hour"); + assertSameDateAs("01", "hour"); + assertSameDateAs("1", "hour"); + + assertSameDateAs("12:12", "hour_minute"); + assertSameDateAs("12:01", "hour_minute"); + assertSameDateAs("12:1", "hour_minute"); + + assertSameDateAs("12:12:12", "hour_minute_second"); + assertSameDateAs("12:12:01", "hour_minute_second"); + assertSameDateAs("12:12:1", "hour_minute_second"); + + assertSameDateAs("12:12:12.123", "hour_minute_second_fraction"); + assertSameDateAs("12:12:12.123456789", "hour_minute_second_fraction"); + assertSameDateAs("12:12:12.1", "hour_minute_second_fraction"); + assertParseException("12:12:12", "hour_minute_second_fraction"); + assertSameDateAs("12:12:12.123", "hour_minute_second_millis"); + assertParseException("12:12:12.123456789", "hour_minute_second_millis"); + assertSameDateAs("12:12:12.1", "hour_minute_second_millis"); + assertParseException("12:12:12", "hour_minute_second_millis"); + + assertSameDateAs("2018-128", "ordinal_date"); + assertSameDateAs("2018-1", "ordinal_date"); + + assertSameDateAs("2018-128T10:15:30.1Z", "ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123Z", "ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123456789Z", "ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123+0100", "ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123+01:00", "ordinal_date_time"); + assertSameDateAs("2018-1T10:15:30.1Z", "ordinal_date_time"); + assertSameDateAs("2018-1T10:15:30.123Z", "ordinal_date_time"); + assertSameDateAs("2018-1T10:15:30.123456789Z", "ordinal_date_time"); + assertSameDateAs("2018-1T10:15:30.123+0100", "ordinal_date_time"); + assertSameDateAs("2018-1T10:15:30.123+01:00", "ordinal_date_time"); + + assertSameDateAs("2018-128T10:15:30Z", "ordinal_date_time_no_millis"); + assertSameDateAs("2018-128T10:15:30+0100", "ordinal_date_time_no_millis"); + assertSameDateAs("2018-128T10:15:30+01:00", "ordinal_date_time_no_millis"); + assertSameDateAs("2018-1T10:15:30Z", "ordinal_date_time_no_millis"); + assertSameDateAs("2018-1T10:15:30+0100", "ordinal_date_time_no_millis"); + assertSameDateAs("2018-1T10:15:30+01:00", "ordinal_date_time_no_millis"); + + assertSameDateAs("10:15:30.1Z", "time"); + assertSameDateAs("10:15:30.123Z", "time"); + assertSameDateAs("10:15:30.123456789Z", "time"); + assertSameDateAs("10:15:30.123+0100", "time"); + assertSameDateAs("10:15:30.123+01:00", "time"); + assertSameDateAs("1:15:30.1Z", "time"); + assertSameDateAs("1:15:30.123Z", "time"); + assertSameDateAs("1:15:30.123+0100", "time"); + assertSameDateAs("1:15:30.123+01:00", "time"); + assertSameDateAs("10:1:30.1Z", "time"); + assertSameDateAs("10:1:30.123Z", "time"); + assertSameDateAs("10:1:30.123+0100", "time"); + assertSameDateAs("10:1:30.123+01:00", "time"); + assertSameDateAs("10:15:3.1Z", "time"); + assertSameDateAs("10:15:3.123Z", "time"); + assertSameDateAs("10:15:3.123+0100", "time"); + assertSameDateAs("10:15:3.123+01:00", "time"); + assertParseException("10:15:3.1", "time"); + assertParseException("10:15:3Z", "time"); + + assertSameDateAs("10:15:30Z", "time_no_millis"); + assertSameDateAs("10:15:30+0100", "time_no_millis"); + assertSameDateAs("10:15:30+01:00", "time_no_millis"); + assertSameDateAs("01:15:30Z", "time_no_millis"); + assertSameDateAs("01:15:30+0100", "time_no_millis"); + assertSameDateAs("01:15:30+01:00", "time_no_millis"); + assertSameDateAs("1:15:30Z", "time_no_millis"); + assertSameDateAs("1:15:30+0100", "time_no_millis"); + assertSameDateAs("1:15:30+01:00", "time_no_millis"); + assertSameDateAs("10:5:30Z", "time_no_millis"); + assertSameDateAs("10:5:30+0100", "time_no_millis"); + assertSameDateAs("10:5:30+01:00", "time_no_millis"); + assertSameDateAs("10:15:3Z", "time_no_millis"); + assertSameDateAs("10:15:3+0100", "time_no_millis"); + assertSameDateAs("10:15:3+01:00", "time_no_millis"); + assertParseException("10:15:3", "time_no_millis"); + + assertSameDateAs("T10:15:30.1Z", "t_time"); + assertSameDateAs("T10:15:30.123Z", "t_time"); + assertSameDateAs("T10:15:30.123456789Z", "t_time"); + assertSameDateAs("T10:15:30.1+0100", "t_time"); + assertSameDateAs("T10:15:30.123+0100", "t_time"); + assertSameDateAs("T10:15:30.123+01:00", "t_time"); + assertSameDateAs("T10:15:30.1+01:00", "t_time"); + assertSameDateAs("T1:15:30.123Z", "t_time"); + assertSameDateAs("T1:15:30.123+0100", "t_time"); + assertSameDateAs("T1:15:30.123+01:00", "t_time"); + assertSameDateAs("T10:1:30.123Z", "t_time"); + assertSameDateAs("T10:1:30.123+0100", "t_time"); + assertSameDateAs("T10:1:30.123+01:00", "t_time"); + assertSameDateAs("T10:15:3.123Z", "t_time"); + assertSameDateAs("T10:15:3.123+0100", "t_time"); + assertSameDateAs("T10:15:3.123+01:00", "t_time"); + assertParseException("T10:15:3.1", "t_time"); + assertParseException("T10:15:3Z", "t_time"); + + assertSameDateAs("T10:15:30Z", "t_time_no_millis"); + assertSameDateAs("T10:15:30+0100", "t_time_no_millis"); + assertSameDateAs("T10:15:30+01:00", "t_time_no_millis"); + assertSameDateAs("T1:15:30Z", "t_time_no_millis"); + assertSameDateAs("T1:15:30+0100", "t_time_no_millis"); + assertSameDateAs("T1:15:30+01:00", "t_time_no_millis"); + assertSameDateAs("T10:1:30Z", "t_time_no_millis"); + assertSameDateAs("T10:1:30+0100", "t_time_no_millis"); + assertSameDateAs("T10:1:30+01:00", "t_time_no_millis"); + assertSameDateAs("T10:15:3Z", "t_time_no_millis"); + assertSameDateAs("T10:15:3+0100", "t_time_no_millis"); + assertSameDateAs("T10:15:3+01:00", "t_time_no_millis"); + assertParseException("T10:15:3", "t_time_no_millis"); + + assertSameDateAs("2012-W48-6", "week_date"); + assertSameDateAs("2012-W01-6", "week_date"); + assertSameDateAs("2012-W1-6", "week_date"); + assertParseException("2012-W1-8", "week_date"); + + assertSameDateAs("2012-W48-6T10:15:30.1Z", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123Z", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123456789Z", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.1+0100", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123+0100", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.1+01:00", "week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123+01:00", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.1Z", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.123Z", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.1+0100", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.123+0100", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.1+01:00", "week_date_time"); + assertSameDateAs("2012-W1-6T10:15:30.123+01:00", "week_date_time"); + + assertSameDateAs("2012-W48-6T10:15:30Z", "week_date_time_no_millis"); + assertSameDateAs("2012-W48-6T10:15:30+0100", "week_date_time_no_millis"); + assertSameDateAs("2012-W48-6T10:15:30+01:00", "week_date_time_no_millis"); + assertSameDateAs("2012-W1-6T10:15:30Z", "week_date_time_no_millis"); + assertSameDateAs("2012-W1-6T10:15:30+0100", "week_date_time_no_millis"); + assertSameDateAs("2012-W1-6T10:15:30+01:00", "week_date_time_no_millis"); + + assertSameDateAs("2012", "year"); + assertSameDateAs("1", "year"); + assertSameDateAs("-2000", "year"); + + assertSameDateAs("2012-12", "year_month"); + assertSameDateAs("1-1", "year_month"); + + assertSameDateAs("2012-12-31", "year_month_day"); + assertSameDateAs("1-12-31", "year_month_day"); + assertSameDateAs("2012-1-31", "year_month_day"); + assertSameDateAs("2012-12-1", "year_month_day"); + + assertSameDateAs("2018", "weekyear"); + assertSameDateAs("1", "weekyear"); + assertSameDateAs("2017", "weekyear"); + + assertSameDateAs("2018-W29", "weekyear_week"); + assertSameDateAs("2018-W1", "weekyear_week"); + + assertSameDateAs("2012-W31-5", "weekyear_week_day"); + assertSameDateAs("2012-W1-1", "weekyear_week_day"); + } + + public void testCompositeParsing() { + // in all these examples the second pattern will be used + assertSameDateAs("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertSameDateAs("2014-06-06T12:01:02.123", "strict_date_time_no_millis||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertSameDateAs("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS"); + } + + public void testStrictParsing() { + assertSameDateAs("2018W313", "strict_basic_week_date"); + assertParseException("18W313", "strict_basic_week_date"); + assertSameDateAs("2018W313T121212.1Z", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.123Z", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.123456789Z", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.1+0100", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.123+0100", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.1+01:00", "strict_basic_week_date_time"); + assertSameDateAs("2018W313T121212.123+01:00", "strict_basic_week_date_time"); + assertParseException("2018W313T12128.123Z", "strict_basic_week_date_time"); + assertParseException("2018W313T12128.123456789Z", "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"); + assertSameDateAs("2018W313T121212Z", "strict_basic_week_date_time_no_millis"); + assertSameDateAs("2018W313T121212+0100", "strict_basic_week_date_time_no_millis"); + assertSameDateAs("2018W313T121212+01:00", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12128Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12128+0100", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12128+01:00", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T81212Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T81212+0100", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T81212+01:00", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12812Z", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12812+0100", "strict_basic_week_date_time_no_millis"); + assertParseException("2018W313T12812+01:00", "strict_basic_week_date_time_no_millis"); + assertSameDateAs("2018-12-31", "strict_date"); + assertParseException("10000-12-31", "strict_date"); + assertParseException("2018-8-31", "strict_date"); + assertSameDateAs("2018-12-31T12", "strict_date_hour"); + assertParseException("2018-12-31T8", "strict_date_hour"); + assertSameDateAs("2018-12-31T12:12", "strict_date_hour_minute"); + assertParseException("2018-12-31T8:3", "strict_date_hour_minute"); + assertSameDateAs("2018-12-31T12:12:12", "strict_date_hour_minute_second"); + assertParseException("2018-12-31T12:12:1", "strict_date_hour_minute_second"); + assertSameDateAs("2018-12-31T12:12:12.1", "strict_date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.123", "strict_date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.123456789", "strict_date_hour_minute_second_fraction"); + assertSameDateAs("2018-12-31T12:12:12.123", "strict_date_hour_minute_second_millis"); + assertSameDateAs("2018-12-31T12:12:12.1", "strict_date_hour_minute_second_millis"); + assertSameDateAs("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"); + assertSameDateAs("2018-12-31", "strict_date_optional_time"); + assertParseException("2018-12-1", "strict_date_optional_time"); + assertParseException("2018-1-31", "strict_date_optional_time"); + assertParseException("10000-01-31", "strict_date_optional_time"); + assertSameDateAs("2010-01-05T02:00", "strict_date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30", "strict_date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30Z", "strict_date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30+0100", "strict_date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30+01:00", "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"); + assertSameDateAs("2015-01-04T00:00Z", "strict_date_optional_time"); + assertSameDateAs("2018-12-31T10:15:30.1Z", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.123Z", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.123456789Z", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.1+0100", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.123+0100", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.1+01:00", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.123+01:00", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.11Z", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.11+0100", "strict_date_time"); + assertSameDateAs("2018-12-31T10:15:30.11+01:00", "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"); + assertSameDateAs("2018-12-31T10:15:30Z", "strict_date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:30+0100", "strict_date_time_no_millis"); + assertSameDateAs("2018-12-31T10:15:30+01:00", "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"); + assertSameDateAs("12", "strict_hour"); + assertSameDateAs("01", "strict_hour"); + assertParseException("1", "strict_hour"); + assertSameDateAs("12:12", "strict_hour_minute"); + assertSameDateAs("12:01", "strict_hour_minute"); + assertParseException("12:1", "strict_hour_minute"); + assertSameDateAs("12:12:12", "strict_hour_minute_second"); + assertSameDateAs("12:12:01", "strict_hour_minute_second"); + assertParseException("12:12:1", "strict_hour_minute_second"); + assertSameDateAs("12:12:12.123", "strict_hour_minute_second_fraction"); + assertSameDateAs("12:12:12.123456789", "strict_hour_minute_second_fraction"); + assertSameDateAs("12:12:12.1", "strict_hour_minute_second_fraction"); + assertParseException("12:12:12", "strict_hour_minute_second_fraction"); + assertSameDateAs("12:12:12.123", "strict_hour_minute_second_millis"); + assertSameDateAs("12:12:12.1", "strict_hour_minute_second_millis"); + assertParseException("12:12:12", "strict_hour_minute_second_millis"); + assertSameDateAs("2018-128", "strict_ordinal_date"); + assertParseException("2018-1", "strict_ordinal_date"); + + assertSameDateAs("2018-128T10:15:30.1Z", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123Z", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123456789Z", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.1+0100", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123+0100", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.1+01:00", "strict_ordinal_date_time"); + assertSameDateAs("2018-128T10:15:30.123+01:00", "strict_ordinal_date_time"); + assertParseException("2018-1T10:15:30.123Z", "strict_ordinal_date_time"); + + assertSameDateAs("2018-128T10:15:30Z", "strict_ordinal_date_time_no_millis"); + assertSameDateAs("2018-128T10:15:30+0100", "strict_ordinal_date_time_no_millis"); + assertSameDateAs("2018-128T10:15:30+01:00", "strict_ordinal_date_time_no_millis"); + assertParseException("2018-1T10:15:30Z", "strict_ordinal_date_time_no_millis"); + + assertSameDateAs("10:15:30.1Z", "strict_time"); + assertSameDateAs("10:15:30.123Z", "strict_time"); + assertSameDateAs("10:15:30.123456789Z", "strict_time"); + assertSameDateAs("10:15:30.123+0100", "strict_time"); + assertSameDateAs("10:15:30.123+01:00", "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"); + + assertSameDateAs("10:15:30Z", "strict_time_no_millis"); + assertSameDateAs("10:15:30+0100", "strict_time_no_millis"); + assertSameDateAs("10:15:30+01:00", "strict_time_no_millis"); + assertSameDateAs("01:15:30Z", "strict_time_no_millis"); + assertSameDateAs("01:15:30+0100", "strict_time_no_millis"); + assertSameDateAs("01:15:30+01:00", "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"); + + assertSameDateAs("T10:15:30.1Z", "strict_t_time"); + assertSameDateAs("T10:15:30.123Z", "strict_t_time"); + assertSameDateAs("T10:15:30.123456789Z", "strict_t_time"); + assertSameDateAs("T10:15:30.1+0100", "strict_t_time"); + assertSameDateAs("T10:15:30.123+0100", "strict_t_time"); + assertSameDateAs("T10:15:30.1+01:00", "strict_t_time"); + assertSameDateAs("T10:15:30.123+01:00", "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"); + + assertSameDateAs("T10:15:30Z", "strict_t_time_no_millis"); + assertSameDateAs("T10:15:30+0100", "strict_t_time_no_millis"); + assertSameDateAs("T10:15:30+01:00", "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"); + + assertSameDateAs("2012-W48-6", "strict_week_date"); + assertSameDateAs("2012-W01-6", "strict_week_date"); + assertParseException("2012-W1-6", "strict_week_date"); + assertParseException("2012-W1-8", "strict_week_date"); + + assertSameDateAs("2012-W48-6", "strict_week_date"); + assertSameDateAs("2012-W01-6", "strict_week_date"); + assertParseException("2012-W1-6", "strict_week_date"); + assertParseException("2012-W01-8", "strict_week_date"); + + assertSameDateAs("2012-W48-6T10:15:30.1Z", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123Z", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123456789Z", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.1+0100", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123+0100", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.1+01:00", "strict_week_date_time"); + assertSameDateAs("2012-W48-6T10:15:30.123+01:00", "strict_week_date_time"); + assertParseException("2012-W1-6T10:15:30.123Z", "strict_week_date_time"); + + assertSameDateAs("2012-W48-6T10:15:30Z", "strict_week_date_time_no_millis"); + assertSameDateAs("2012-W48-6T10:15:30+0100", "strict_week_date_time_no_millis"); + assertSameDateAs("2012-W48-6T10:15:30+01:00", "strict_week_date_time_no_millis"); + assertParseException("2012-W1-6T10:15:30Z", "strict_week_date_time_no_millis"); + + assertSameDateAs("2012", "strict_year"); + assertParseException("1", "strict_year"); + assertSameDateAs("-2000", "strict_year"); + + assertSameDateAs("2012-12", "strict_year_month"); + assertParseException("1-1", "strict_year_month"); + + assertSameDateAs("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"); + + assertSameDateAs("2018", "strict_weekyear"); + assertParseException("1", "strict_weekyear"); + + assertSameDateAs("2018", "strict_weekyear"); + assertSameDateAs("2017", "strict_weekyear"); + assertParseException("1", "strict_weekyear"); + + assertSameDateAs("2018-W29", "strict_weekyear_week"); + assertSameDateAs("2018-W01", "strict_weekyear_week"); + assertParseException("2018-W1", "strict_weekyear_week"); + + assertSameDateAs("2012-W31-5", "strict_weekyear_week_day"); + assertParseException("2012-W1-1", "strict_weekyear_week_day"); + } + + public void testSeveralTimeFormats() { + { + String format = "year_month_day||ordinal_date"; + DateFormatter javaFormatter = DateFormatter.forPattern(format); + assertSameDate("2018-12-12", javaFormatter); + assertSameDate("2018-128", javaFormatter); } + { + String format = "strict_date_optional_time||dd-MM-yyyy"; + DateFormatter javaFormatter = DateFormatter.forPattern(format); + assertSameDate("31-01-2014", javaFormatter); + } + } + + public void testParsingLocalDateFromYearOfEra() { + // with strict resolving, YearOfEra expect an era, otherwise it won't resolve to a date + assertSameDate("2018363", DateFormatter.forPattern("uuuuDDD")); + } + + public void testDateFormatterWithLocale() { + Locale locale = randomLocale(random()); + String pattern = randomBoolean() ? "strict_date_optional_time||date_time" : "date_time||strict_date_optional_time"; + DateFormatter formatter = DateFormatter.forPattern(pattern).withLocale(locale); + assertThat(formatter.pattern(), is(pattern)); + assertThat(formatter.locale(), is(locale)); + } + + public void testParsingMissingTimezone() { + long millisJava = DateFormatter.forPattern("8yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17"); + long millisJoda = DateFormatter.forPattern("yyyy-MM-dd HH:mm:ss").parseMillis("2018-02-18 17:47:17"); + assertThat(millisJava, is(millisJoda)); + } + + // the iso 8601 parser is available via Joda.forPattern(), so we have to test this slightly differently + public void testIso8601Parsers() { + String format = "iso8601"; + DateFormatter javaFormatter = DateFormatter.forPattern(format); + + assertSameDate("2018-10-10", javaFormatter); + assertSameDate("2018-10-10T", javaFormatter); + assertSameDate("2018-10-10T10", javaFormatter); + assertSameDate("2018-10-10T10+0430", javaFormatter); + assertSameDate("2018-10-10T10:11", javaFormatter); + assertSameDate("2018-10-10T10:11-08:00", javaFormatter); + assertSameDate("2018-10-10T10:11Z", javaFormatter); + assertSameDate("2018-10-10T10:11:12", javaFormatter); + assertSameDate("2018-10-10T10:11:12+0100", javaFormatter); + assertSameDate("2018-10-10T10:11:12.123", javaFormatter); + assertSameDate("2018-10-10T10:11:12.123Z", javaFormatter); + assertSameDate("2018-10-10T10:11:12.123+0000", javaFormatter); + assertSameDate("2018-10-10T10:11:12,123", javaFormatter); + assertSameDate("2018-10-10T10:11:12,123Z", javaFormatter); + assertSameDate("2018-10-10T10:11:12,123+05:30", javaFormatter); + } + + public void testExceptionWhenCompositeParsingFails() { + assertParseException("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"); + } + + private void assertSameDateAs(String input, String javaPattern) { + DateFormatter javaFormatter = DateFormatter.forPattern(javaPattern); + assertSameDate(input, javaFormatter); } + + private void assertSameDate(String input, DateFormatter javaFormatter) { + TemporalAccessor javaTimeAccessor = javaFormatter.parse(input); + ZonedDateTime zonedDateTime = DateFormatters.from(javaTimeAccessor); + assertNotNull(zonedDateTime); + } + + private void assertDateMathEquals(String text, String expected, String pattern) { + Locale locale = randomLocale(random()); + assertDateMathEquals(text, expected, pattern, locale); + } + + private void assertDateMathEquals(String text, String expected, String pattern, Locale locale) { + long gotMillisJava = dateMathToMillis(text, DateFormatter.forPattern(pattern), locale); + long expectedMillis = DateFormatters.from(DateFormatter.forPattern("strict_date_optional_time").withLocale(locale).parse(expected)) + .toInstant() + .toEpochMilli(); + + assertThat(gotMillisJava, equalTo(expectedMillis)); + } + + private long dateMathToMillis(final String text, final DateFormatter dateFormatter) { + DateFormatter javaFormatter = dateFormatter.withLocale(randomLocale(random())); + DateMathParser javaDateMath = javaFormatter.toDateMathParser(); + return javaDateMath.parse(text, () -> 0, true, null).toEpochMilli(); + } + + private long dateMathToMillis(final String text, final DateFormatter dateFormatter, final Locale locale) { + DateFormatter javaFormatter = dateFormatter.withLocale(locale); + DateMathParser javaDateMath = javaFormatter.toDateMathParser(); + return javaDateMath.parse(text, () -> 0, true, null).toEpochMilli(); + } + + private void assertParseException(String input, String format) { + DateFormatter javaTimeFormatter = DateFormatter.forPattern(format); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> javaTimeFormatter.parse(input)); + assertThat(e.getMessage(), containsString(input)); + assertThat(e.getMessage(), containsString(format)); + } + } diff --git a/server/src/test/java/org/opensearch/common/time/DateUtilsTests.java b/server/src/test/java/org/opensearch/common/time/DateUtilsTests.java index d9662d1de9e0c..283bea1d1c2e9 100644 --- a/server/src/test/java/org/opensearch/common/time/DateUtilsTests.java +++ b/server/src/test/java/org/opensearch/common/time/DateUtilsTests.java @@ -33,20 +33,15 @@ package org.opensearch.common.time; import org.opensearch.test.OpenSearchTestCase; -import org.joda.time.DateTimeZone; import java.time.Instant; import java.time.LocalDate; import java.time.Month; import java.time.Year; import java.time.YearMonth; -import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; import static org.opensearch.common.time.DateUtils.clampToNanosRange; import static org.opensearch.common.time.DateUtils.toInstant; @@ -58,28 +53,6 @@ import static org.hamcrest.Matchers.is; public class DateUtilsTests extends OpenSearchTestCase { - private static final Set IGNORE = new HashSet<>(Arrays.asList("America/Ciudad_Juarez")); - - public void testTimezoneIds() { - assertNull(DateUtils.dateTimeZoneToZoneId(null)); - assertNull(DateUtils.zoneIdToDateTimeZone(null)); - for (String jodaId : DateTimeZone.getAvailableIDs()) { - if (IGNORE.contains(jodaId)) continue; - DateTimeZone jodaTz = DateTimeZone.forID(jodaId); - ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(jodaTz); // does not throw - long now = 0; - assertThat( - jodaId, - zoneId.getRules().getOffset(Instant.ofEpochMilli(now)).getTotalSeconds() * 1000, - equalTo(jodaTz.getOffset(now)) - ); - if (DateUtils.DEPRECATED_SHORT_TIMEZONES.containsKey(jodaTz.getID())) { - assertWarnings("Use of short timezone id " + jodaId + " is deprecated. Use " + zoneId.getId() + " instead"); - } - // roundtrip does not throw either - assertNotNull(DateUtils.zoneIdToDateTimeZone(zoneId)); - } - } public void testInstantToLong() { assertThat(toLong(Instant.EPOCH), is(0L)); diff --git a/server/src/test/java/org/opensearch/index/mapper/DateFieldMapperTests.java b/server/src/test/java/org/opensearch/index/mapper/DateFieldMapperTests.java index 054d3956596af..74a4cbfa99787 100644 --- a/server/src/test/java/org/opensearch/index/mapper/DateFieldMapperTests.java +++ b/server/src/test/java/org/opensearch/index/mapper/DateFieldMapperTests.java @@ -34,10 +34,12 @@ import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexableField; +import org.opensearch.Version; import org.opensearch.common.time.DateFormatter; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.termvectors.TermVectorsService; import org.opensearch.search.DocValueFormat; +import org.opensearch.test.VersionUtils; import java.io.IOException; import java.time.ZoneId; @@ -216,6 +218,29 @@ public void testNullValue() throws IOException { assertFalse(dvField.fieldType().stored()); } + public void testLegacyDateFormatName() throws IOException { + // check that indexes created prior to 2.12.0 support camel case + final DocumentMapper legacyMapper = createDocumentMapper( + VersionUtils.randomVersionBetween(random(), Version.V_2_0_0, VersionUtils.getPreviousVersion(Version.V_2_12_0)), // BWC + // compatible + fieldMapping(b -> { + b.field("type", "date"); + b.field("format", "strictDateOptionalTime||strictDateOptionalTimeNanos"); + }) + ); + + // check that indexes created on or after 2.12 + MapperParsingException e = expectThrows( + MapperParsingException.class, + () -> createDocumentMapper(VersionUtils.randomVersionBetween(random(), Version.V_2_12_0, Version.CURRENT), fieldMapping(b -> { + b.field("type", "date"); + b.field("format", "strictDateOptionalTime||strictDateOptionalTimeNanos"); + })) + ); + assertThat(e.getMessage(), containsString("Invalid format: [strictDateOptionalTime]: Unknown pattern letter: t")); + assertThat(e.getMessage(), containsString("Error parsing [format] on field [field]: Invalid")); + } + public void testNanosNullValue() throws IOException { DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping)); diff --git a/server/src/test/java/org/opensearch/index/mapper/DateFieldTypeTests.java b/server/src/test/java/org/opensearch/index/mapper/DateFieldTypeTests.java index ab53ae81ab0ce..098a196f697ed 100644 --- a/server/src/test/java/org/opensearch/index/mapper/DateFieldTypeTests.java +++ b/server/src/test/java/org/opensearch/index/mapper/DateFieldTypeTests.java @@ -65,9 +65,9 @@ import org.opensearch.index.query.DateRangeIncludingNowQuery; import org.opensearch.index.query.QueryRewriteContext; import org.opensearch.index.query.QueryShardContext; -import org.joda.time.DateTimeZone; import java.io.IOException; +import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Collections; @@ -110,8 +110,8 @@ public void isFieldWithinRangeTestCase(DateFieldType ft) throws IOException { DateMathParser alternateFormat = DateFieldMapper.getDefaultDateTimeFormatter().toDateMathParser(); doTestIsFieldWithinQuery(ft, reader, null, null); doTestIsFieldWithinQuery(ft, reader, null, alternateFormat); - doTestIsFieldWithinQuery(ft, reader, DateTimeZone.UTC, null); - doTestIsFieldWithinQuery(ft, reader, DateTimeZone.UTC, alternateFormat); + doTestIsFieldWithinQuery(ft, reader, ZoneOffset.UTC, null); + doTestIsFieldWithinQuery(ft, reader, ZoneOffset.UTC, alternateFormat); QueryRewriteContext context = new QueryRewriteContext(xContentRegistry(), writableRegistry(), null, () -> nowInMillis); @@ -123,37 +123,41 @@ public void isFieldWithinRangeTestCase(DateFieldType ft) throws IOException { IOUtils.close(reader, w, dir); } - private void doTestIsFieldWithinQuery(DateFieldType ft, DirectoryReader reader, DateTimeZone zone, DateMathParser alternateFormat) - throws IOException { + private void doTestIsFieldWithinQuery( + final DateFieldType ft, + final DirectoryReader reader, + final ZoneId zone, + final DateMathParser alternateFormat + ) throws IOException { QueryRewriteContext context = new QueryRewriteContext(xContentRegistry(), writableRegistry(), null, () -> nowInMillis); assertEquals( Relation.INTERSECTS, - ft.isFieldWithinQuery(reader, "2015-10-09", "2016-01-02", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2015-10-09", "2016-01-02", randomBoolean(), randomBoolean(), zone, null, context) ); assertEquals( Relation.INTERSECTS, - ft.isFieldWithinQuery(reader, "2016-01-02", "2016-06-20", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2016-01-02", "2016-06-20", randomBoolean(), randomBoolean(), zone, null, context) ); assertEquals( Relation.INTERSECTS, - ft.isFieldWithinQuery(reader, "2016-01-02", "2016-02-12", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2016-01-02", "2016-02-12", randomBoolean(), randomBoolean(), zone, null, context) ); assertEquals( Relation.DISJOINT, - ft.isFieldWithinQuery(reader, "2014-01-02", "2015-02-12", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2014-01-02", "2015-02-12", randomBoolean(), randomBoolean(), zone, null, context) ); assertEquals( Relation.DISJOINT, - ft.isFieldWithinQuery(reader, "2016-05-11", "2016-08-30", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2016-05-11", "2016-08-30", randomBoolean(), randomBoolean(), zone, null, context) ); assertEquals( Relation.WITHIN, - ft.isFieldWithinQuery(reader, "2015-09-25", "2016-05-29", randomBoolean(), randomBoolean(), null, null, context) + ft.isFieldWithinQuery(reader, "2015-09-25", "2016-05-29", randomBoolean(), randomBoolean(), zone, null, context) ); - assertEquals(Relation.WITHIN, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", true, true, null, null, context)); - assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", false, false, null, null, context)); - assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", false, true, null, null, context)); - assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", true, false, null, null, context)); + assertEquals(Relation.WITHIN, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", true, true, zone, null, context)); + assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", false, false, zone, null, context)); + assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", false, true, zone, null, context)); + assertEquals(Relation.INTERSECTS, ft.isFieldWithinQuery(reader, "2015-10-12", "2016-04-03", true, false, zone, null, context)); } public void testValueFormat() { diff --git a/server/src/test/java/org/opensearch/index/mapper/RangeFieldTypeTests.java b/server/src/test/java/org/opensearch/index/mapper/RangeFieldTypeTests.java index 755d77c6ae392..81d38bb0e5028 100644 --- a/server/src/test/java/org/opensearch/index/mapper/RangeFieldTypeTests.java +++ b/server/src/test/java/org/opensearch/index/mapper/RangeFieldTypeTests.java @@ -57,11 +57,13 @@ import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.QueryShardException; import org.opensearch.test.IndexSettingsModule; -import org.joda.time.DateTime; import org.junit.Before; import java.io.IOException; import java.net.InetAddress; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Collections; import java.util.Map; @@ -125,8 +127,8 @@ public void testRangeQueryIntersectsAdjacentValues() throws Exception { } case DATE: { long fromValue = randomInt(); - from = new DateTime(fromValue); - to = new DateTime(fromValue + 1); + from = ZonedDateTime.ofInstant(Instant.ofEpochMilli(fromValue), ZoneOffset.UTC); + to = ZonedDateTime.ofInstant(Instant.ofEpochMilli(fromValue + 1), ZoneOffset.UTC); break; } case INTEGER: { @@ -182,8 +184,8 @@ public void testFromLargerToErrors() throws Exception { } case DATE: { long fromValue = randomInt(); - from = new DateTime(fromValue); - to = new DateTime(fromValue - 1); + from = ZonedDateTime.ofInstant(Instant.ofEpochMilli(fromValue), ZoneOffset.UTC); + to = ZonedDateTime.ofInstant(Instant.ofEpochMilli(fromValue - 1), ZoneOffset.UTC); break; } case INTEGER: { @@ -339,7 +341,7 @@ public void testDateVsDateRangeBounds() { private Query getExpectedRangeQuery(ShapeRelation relation, Object from, Object to, boolean includeLower, boolean includeUpper) { switch (type) { case DATE: - return getDateRangeQuery(relation, (DateTime) from, (DateTime) to, includeLower, includeUpper); + return getDateRangeQuery(relation, (ZonedDateTime) from, (ZonedDateTime) to, includeLower, includeUpper); case INTEGER: return getIntRangeQuery(relation, (int) from, (int) to, includeLower, includeUpper); case LONG: @@ -353,9 +355,15 @@ private Query getExpectedRangeQuery(ShapeRelation relation, Object from, Object } } - private Query getDateRangeQuery(ShapeRelation relation, DateTime from, DateTime to, boolean includeLower, boolean includeUpper) { - long[] lower = new long[] { from.getMillis() + (includeLower ? 0 : 1) }; - long[] upper = new long[] { to.getMillis() - (includeUpper ? 0 : 1) }; + private Query getDateRangeQuery( + final ShapeRelation relation, + final ZonedDateTime from, + final ZonedDateTime to, + final boolean includeLower, + final boolean includeUpper + ) { + long[] lower = new long[] { from.toInstant().toEpochMilli() + (includeLower ? 0 : 1) }; + long[] upper = new long[] { to.toInstant().toEpochMilli() - (includeUpper ? 0 : 1) }; Query indexQuery; BinaryDocValuesRangeQuery.QueryType queryType; if (relation == ShapeRelation.WITHIN) { @@ -368,7 +376,14 @@ private Query getDateRangeQuery(ShapeRelation relation, DateTime from, DateTime indexQuery = LongRange.newIntersectsQuery("field", lower, upper); queryType = BinaryDocValuesRangeQuery.QueryType.INTERSECTS; } - Query dvQuery = RangeType.DATE.dvRangeQuery("field", queryType, from.getMillis(), to.getMillis(), includeLower, includeUpper); + Query dvQuery = RangeType.DATE.dvRangeQuery( + "field", + queryType, + from.toInstant().toEpochMilli(), + to.toInstant().toEpochMilli(), + includeLower, + includeUpper + ); return new IndexOrDocValuesQuery(indexQuery, dvQuery); } @@ -478,7 +493,7 @@ private Object nextFrom() throws Exception { case INTEGER: return (int) (random().nextInt() * 0.5 - DISTANCE); case DATE: - return DateTime.now(); + return ZonedDateTime.now(ZoneOffset.UTC); case LONG: return (long) (random().nextLong() * 0.5 - DISTANCE); case FLOAT: @@ -495,7 +510,7 @@ private Object nextTo(Object from) throws Exception { case INTEGER: return (Integer) from + DISTANCE; case DATE: - return DateTime.now().plusDays(DISTANCE); + return ZonedDateTime.now(ZoneOffset.UTC).plusDays(DISTANCE); case LONG: return (Long) from + DISTANCE; case DOUBLE: diff --git a/server/src/test/java/org/opensearch/index/query/DistanceFeatureQueryBuilderTests.java b/server/src/test/java/org/opensearch/index/query/DistanceFeatureQueryBuilderTests.java index 8489a65d26c91..af010747b4567 100644 --- a/server/src/test/java/org/opensearch/index/query/DistanceFeatureQueryBuilderTests.java +++ b/server/src/test/java/org/opensearch/index/query/DistanceFeatureQueryBuilderTests.java @@ -45,7 +45,6 @@ import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.DistanceFeatureQueryBuilder.Origin; import org.opensearch.test.AbstractQueryTestCase; -import org.joda.time.DateTime; import java.io.IOException; import java.time.Instant; @@ -66,7 +65,7 @@ protected DistanceFeatureQueryBuilder doCreateTestQueryBuilder() { break; case DATE_FIELD_NAME: long randomDateMills = randomLongBetween(0, 2_000_000_000_000L); - origin = randomBoolean() ? new Origin(randomDateMills) : new Origin(new DateTime(randomDateMills).toString()); + origin = randomBoolean() ? new Origin(randomDateMills) : new Origin(Instant.ofEpochMilli(randomDateMills).toString()); pivot = randomTimeValue(1, 1000, "d", "h", "ms", "s", "m"); break; default: // DATE_NANOS_FIELD_NAME diff --git a/server/src/test/java/org/opensearch/index/query/RangeQueryBuilderTests.java b/server/src/test/java/org/opensearch/index/query/RangeQueryBuilderTests.java index e72be29b85b63..4d0dc884b3d5a 100644 --- a/server/src/test/java/org/opensearch/index/query/RangeQueryBuilderTests.java +++ b/server/src/test/java/org/opensearch/index/query/RangeQueryBuilderTests.java @@ -54,8 +54,6 @@ import org.opensearch.index.mapper.MappedFieldType.Relation; import org.opensearch.index.mapper.MapperService; import org.opensearch.test.AbstractQueryTestCase; -import org.joda.time.DateTime; -import org.joda.time.chrono.ISOChronology; import java.io.IOException; import java.time.Instant; @@ -306,8 +304,8 @@ public void testDateRangeQueryFormat() throws IOException { assertEquals( LongPoint.newRangeQuery( DATE_FIELD_NAME, - DateTime.parse("2012-01-01T00:00:00.000+00").getMillis(), - DateTime.parse("2030-01-01T00:00:00.000+00").getMillis() - 1 + ZonedDateTime.parse("2012-01-01T00:00:00.000+00").toInstant().toEpochMilli(), + ZonedDateTime.parse("2030-01-01T00:00:00.000+00").toInstant().toEpochMilli() - 1 ), parsedQuery ); @@ -345,8 +343,8 @@ public void testDateRangeBoundaries() throws IOException { assertEquals( LongPoint.newRangeQuery( DATE_FIELD_NAME, - DateTime.parse("2014-11-01T00:00:00.000+00").getMillis(), - DateTime.parse("2014-12-08T23:59:59.999+00").getMillis() + ZonedDateTime.parse("2014-11-01T00:00:00.000+00").toInstant().toEpochMilli(), + ZonedDateTime.parse("2014-12-08T23:59:59.999+00").toInstant().toEpochMilli() ), parsedQuery ); @@ -368,8 +366,8 @@ public void testDateRangeBoundaries() throws IOException { assertEquals( LongPoint.newRangeQuery( DATE_FIELD_NAME, - DateTime.parse("2014-11-30T23:59:59.999+00").getMillis() + 1, - DateTime.parse("2014-12-08T00:00:00.000+00").getMillis() - 1 + ZonedDateTime.parse("2014-11-30T23:59:59.999+00").toInstant().toEpochMilli() + 1, + ZonedDateTime.parse("2014-12-08T00:00:00.000+00").toInstant().toEpochMilli() - 1 ), parsedQuery ); @@ -454,8 +452,8 @@ protected MappedFieldType.Relation getRelation(QueryRewriteContext queryRewriteC return Relation.WITHIN; } }; - DateTime queryFromValue = new DateTime(2015, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); - DateTime queryToValue = new DateTime(2016, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); + ZonedDateTime queryFromValue = ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + ZonedDateTime queryToValue = ZonedDateTime.of(2016, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); query.from(queryFromValue); query.to(queryToValue); QueryShardContext queryShardContext = createShardContext(); @@ -489,8 +487,8 @@ protected MappedFieldType.Relation getRelation(QueryRewriteContext queryRewriteC return Relation.WITHIN; } }; - DateTime queryFromValue = new DateTime(2015, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); - DateTime queryToValue = new DateTime(2016, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); + ZonedDateTime queryFromValue = ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + ZonedDateTime queryToValue = ZonedDateTime.of(2016, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); query.from(queryFromValue); query.to(queryToValue); query.timeZone(randomZone().getId()); @@ -514,8 +512,8 @@ protected MappedFieldType.Relation getRelation(QueryRewriteContext queryRewriteC return Relation.DISJOINT; } }; - DateTime queryFromValue = new DateTime(2015, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); - DateTime queryToValue = new DateTime(2016, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); + ZonedDateTime queryFromValue = ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + ZonedDateTime queryToValue = ZonedDateTime.of(2016, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); query.from(queryFromValue); query.to(queryToValue); QueryShardContext queryShardContext = createShardContext(); @@ -531,8 +529,8 @@ protected MappedFieldType.Relation getRelation(QueryRewriteContext queryRewriteC return Relation.INTERSECTS; } }; - DateTime queryFromValue = new DateTime(2015, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); - DateTime queryToValue = new DateTime(2016, 1, 1, 0, 0, 0, ISOChronology.getInstanceUTC()); + ZonedDateTime queryFromValue = ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + ZonedDateTime queryToValue = ZonedDateTime.of(2016, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); query.from(queryFromValue); query.to(queryToValue); QueryShardContext queryShardContext = createShardContext(); diff --git a/server/src/test/java/org/opensearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java b/server/src/test/java/org/opensearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java index 4e64a1ec03688..b66bd7afd48e5 100644 --- a/server/src/test/java/org/opensearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java +++ b/server/src/test/java/org/opensearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java @@ -69,12 +69,13 @@ import org.opensearch.search.MultiValueMode; import org.opensearch.test.AbstractQueryTestCase; import org.opensearch.test.TestGeoShapeFieldMapperPlugin; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.hamcrest.CoreMatchers; import org.hamcrest.Matcher; import java.io.IOException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -253,7 +254,10 @@ private static DecayFunctionBuilder createRandomDecayFunction() { offset = randomFrom(DistanceUnit.values()).toString(randomDouble()); break; case DATE_FIELD_NAME: - origin = new DateTime(System.currentTimeMillis() - randomIntBetween(0, 1000000), DateTimeZone.UTC).toString(); + origin = ZonedDateTime.ofInstant( + Instant.ofEpochMilli(System.currentTimeMillis() - randomIntBetween(0, 1000000)), + ZoneOffset.UTC + ).toString(); scale = randomTimeValue(1, 1000, "d", "h", "ms", "s", "m"); offset = randomPositiveTimeValue(); break; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/DateScriptMocksPlugin.java b/server/src/test/java/org/opensearch/search/aggregations/bucket/DateScriptMocksPlugin.java index d6ba4eedd3a19..d6b5d47a3bb24 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/DateScriptMocksPlugin.java +++ b/server/src/test/java/org/opensearch/search/aggregations/bucket/DateScriptMocksPlugin.java @@ -34,9 +34,10 @@ import org.opensearch.script.MockScriptPlugin; import org.opensearch.search.lookup.LeafDocLookup; -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.HashMap; import java.util.Map; import java.util.function.Function; @@ -63,16 +64,25 @@ public Map, Object>> pluginScripts() { }); scripts.put( DOUBLE_PLUS_ONE_MONTH, - params -> new DateTime(Double.valueOf((double) params.get("_value")).longValue(), DateTimeZone.UTC).plusMonths(1).getMillis() + params -> ZonedDateTime.ofInstant( + Instant.ofEpochMilli(Double.valueOf((double) params.get("_value")).longValue()), + ZoneOffset.UTC + ).plusMonths(1).toInstant().toEpochMilli() + ); + scripts.put( + LONG_PLUS_ONE_MONTH, + params -> ZonedDateTime.ofInstant(Instant.ofEpochMilli((long) params.get("_value")), ZoneOffset.UTC) + .plusMonths(1) + .toInstant() + .toEpochMilli() ); - scripts.put(LONG_PLUS_ONE_MONTH, params -> new DateTime((long) params.get("_value"), DateTimeZone.UTC).plusMonths(1).getMillis()); return scripts; } @Override protected Map, Object>> nonDeterministicPluginScripts() { Map, Object>> scripts = new HashMap<>(); - scripts.put(CURRENT_DATE, params -> new DateTime().getMillis()); + scripts.put(CURRENT_DATE, params -> ZonedDateTime.now().toInstant().toEpochMilli()); return scripts; } } diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/range/InternalDateRangeTests.java b/server/src/test/java/org/opensearch/search/aggregations/bucket/range/InternalDateRangeTests.java index 9a126abea3135..701473175be7f 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/range/InternalDateRangeTests.java +++ b/server/src/test/java/org/opensearch/search/aggregations/bucket/range/InternalDateRangeTests.java @@ -37,9 +37,9 @@ import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.InternalMultiBucketAggregation; import org.opensearch.search.aggregations.ParsedMultiBucketAggregation; -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.Collections; import java.util.HashMap; @@ -57,7 +57,7 @@ public void setUp() throws Exception { super.setUp(); format = randomNumericDocValueFormat(); - Function interval = randomFrom( + Function interval = randomFrom( dateTime -> dateTime.plusSeconds(1), dateTime -> dateTime.plusMinutes(1), dateTime -> dateTime.plusHours(1), @@ -69,13 +69,13 @@ public void setUp() throws Exception { final int numRanges = randomNumberOfBuckets(); final List> listOfRanges = new ArrayList<>(numRanges); - DateTime date = new DateTime(DateTimeZone.UTC); - double start = date.getMillis(); + ZonedDateTime date = ZonedDateTime.now(ZoneOffset.UTC); + double start = date.toInstant().toEpochMilli(); double end = 0; for (int i = 0; i < numRanges; i++) { - double from = date.getMillis(); + double from = date.toInstant().toEpochMilli(); date = interval.apply(date); - double to = date.getMillis(); + double to = date.toInstant().toEpochMilli(); if (to > end) { end = to; } diff --git a/test/framework/src/main/java/org/opensearch/test/AbstractQueryTestCase.java b/test/framework/src/main/java/org/opensearch/test/AbstractQueryTestCase.java index afd93e1b72fbb..437a9ac2db60d 100644 --- a/test/framework/src/main/java/org/opensearch/test/AbstractQueryTestCase.java +++ b/test/framework/src/main/java/org/opensearch/test/AbstractQueryTestCase.java @@ -68,11 +68,11 @@ import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.Rewriteable; import org.opensearch.index.query.support.QueryParsers; -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.ArrayList; import java.util.Collections; import java.util.Deque; @@ -691,7 +691,7 @@ protected static Object getRandomValueForFieldName(String fieldName) { value = randomBoolean(); break; case DATE_FIELD_NAME: - value = new DateTime(System.currentTimeMillis(), DateTimeZone.UTC).toString(); + value = ZonedDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneOffset.UTC).toString(); break; case DATE_NANOS_FIELD_NAME: value = Instant.now().toString(); diff --git a/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java b/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java index b5ff30deecf5c..2a60e40a558aa 100644 --- a/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java +++ b/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java @@ -74,7 +74,6 @@ import org.opensearch.common.io.PathUtils; import org.opensearch.common.io.PathUtilsForTesting; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.joda.JodaDeprecationPatterns; import org.opensearch.common.logging.DeprecatedMessage; import org.opensearch.common.logging.HeaderWarning; import org.opensearch.common.logging.HeaderWarningAppender; @@ -133,7 +132,6 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import org.opensearch.transport.nio.MockNioTransportPlugin; -import org.joda.time.DateTimeZone; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -211,7 +209,6 @@ @LuceneTestCase.SuppressReproduceLine public abstract class OpenSearchTestCase extends LuceneTestCase { - protected static final List JODA_TIMEZONE_IDS; protected static final List JAVA_TIMEZONE_IDS; protected static final List JAVA_ZONE_IDS; @@ -272,20 +269,52 @@ public void append(LogEvent event) { TransportService.ensureClassloaded(); // ensure server streamables are registered // filter out joda timezones that are deprecated for the java time migration - List jodaTZIds = DateTimeZone.getAvailableIDs() - .stream() - .filter(s -> DateUtils.DEPRECATED_SHORT_TZ_IDS.contains(s) == false) + Set removedJodaTZIds = Set.of( + "ACT", + "AET", + "AGT", + "ART", + "AST", + "BET", + "BST", + "CAT", + "CNT", + "CST", + "CTT", + "EAT", + "ECT", + "EST", + "HST", + "IET", + "IST", + "JST", + "MIT", + "MST", + "NET", + "NST", + "PLT", + "PNT", + "PRT", + "PST", + "SST", + "VST" + ); + final Predicate removedJodaTZIdsFilter = removedJodaTZIds::contains; + final Predicate removedZoneIdsFilter = tz -> tz.startsWith("System/") || DateUtils.DEPRECATED_SHORT_TZ_IDS.contains(tz); + + // filter time zones that aren't supported by joda since there is no java date equivalent. + // this is only needed until 3.0 (when wire compatibility no longer communicates w/ joda compatible nodes) + JAVA_TIMEZONE_IDS = Arrays.stream(TimeZone.getAvailableIDs()) + .filter(removedJodaTZIdsFilter.negate()) + .filter(removedZoneIdsFilter.negate()) .sorted() .collect(Collectors.toList()); - JODA_TIMEZONE_IDS = Collections.unmodifiableList(jodaTZIds); - 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); + JAVA_ZONE_IDS = ZoneId.getAvailableZoneIds() + .stream() + .filter(removedZoneIdsFilter.negate()) + .sorted() + .collect(Collectors.toUnmodifiableList()); } @SuppressForbidden(reason = "force log4j and netty sysprops") @@ -422,10 +451,6 @@ protected boolean enableWarningsCheck() { return true; } - protected boolean enableJodaDeprecationWarningsCheck() { - return false; - } - @After public final void after() throws Exception { checkStaticState(false); @@ -461,9 +486,6 @@ private void ensureNoWarnings() { final List warnings = threadContext.getResponseHeaders().get("Warning"); if (warnings != null) { List filteredWarnings = new ArrayList<>(warnings); - if (enableJodaDeprecationWarningsCheck() == false) { - filteredWarnings = filterJodaDeprecationWarnings(filteredWarnings); - } if (JvmInfo.jvmInfo().getBundledJdk() == false) { // unit tests do not run with the bundled JDK, if there are warnings we need to filter the no-jdk deprecation warning filteredWarnings = filteredWarnings.stream() @@ -560,45 +582,30 @@ protected final void assertWarnings(boolean stripXContentPosition, String... exp } try { final List actualWarnings = threadContext.getResponseHeaders().get("Warning"); - if (actualWarnings != null && enableJodaDeprecationWarningsCheck() == false) { - List filteredWarnings = filterJodaDeprecationWarnings(actualWarnings); - assertWarnings(stripXContentPosition, filteredWarnings, expectedWarnings); - } else { - assertWarnings(stripXContentPosition, actualWarnings, expectedWarnings); + assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings); + final Set actualWarningValues = actualWarnings.stream() + .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, stripXContentPosition)) + .collect(Collectors.toSet()); + for (String msg : expectedWarnings) { + assertThat(actualWarningValues, hasItem(HeaderWarning.escapeAndEncode(msg))); } + assertEquals( + "Expected " + + expectedWarnings.length + + " warnings but found " + + actualWarnings.size() + + "\nExpected: " + + Arrays.asList(expectedWarnings) + + "\nActual: " + + actualWarnings, + expectedWarnings.length, + actualWarnings.size() + ); } finally { resetDeprecationLogger(); } } - private List filterJodaDeprecationWarnings(List actualWarnings) { - return actualWarnings.stream() - .filter(m -> m.contains(JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS) == false) - .collect(Collectors.toList()); - } - - private void assertWarnings(boolean stripXContentPosition, List actualWarnings, String[] expectedWarnings) { - assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings); - final Set actualWarningValues = actualWarnings.stream() - .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, stripXContentPosition)) - .collect(Collectors.toSet()); - for (String msg : expectedWarnings) { - assertThat(actualWarningValues, hasItem(HeaderWarning.escapeAndEncode(msg))); - } - assertEquals( - "Expected " - + expectedWarnings.length - + " warnings but found " - + actualWarnings.size() - + "\nExpected: " - + Arrays.asList(expectedWarnings) - + "\nActual: " - + actualWarnings, - expectedWarnings.length, - actualWarnings.size() - ); - } - /** * Reset the deprecation logger by clearing the current thread context. */ @@ -988,35 +995,18 @@ public static String randomPositiveTimeValue() { return randomTimeValue(1, 1000); } - /** - * generate a random DateTimeZone from the ones available in joda library - */ - public static DateTimeZone randomDateTimeZone() { - return DateTimeZone.forID(randomFrom(JODA_TIMEZONE_IDS)); - } - /** * generate a random TimeZone from the ones available in java.util */ public static TimeZone randomTimeZone() { - return TimeZone.getTimeZone(randomJodaAndJavaSupportedTimezone(JAVA_TIMEZONE_IDS)); + 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(randomJodaAndJavaSupportedTimezone(JAVA_ZONE_IDS)); - } - - /** - * We need to exclude time zones not supported by joda (like SystemV* timezones) - * because they cannot be converted back to DateTimeZone which we currently - * still need to do internally e.g. in bwc serialization and in the extract() method - * //TODO remove once joda is not supported - */ - private static String randomJodaAndJavaSupportedTimezone(List zoneIds) { - return randomValueOtherThanMany(id -> JODA_TIMEZONE_IDS.contains(id) == false, () -> randomFrom(zoneIds)); + return ZoneId.of(randomFrom(JAVA_ZONE_IDS)); } /**