From 71de5baae9418e5325000175cf6c37c5f3f11ce5 Mon Sep 17 00:00:00 2001 From: Nicholas Walter Knize Date: Thu, 26 Oct 2023 13:09:17 -0500 Subject: [PATCH 1/4] [Remove] Remaining Joda and Joda Dependency Joda was deprecated in Elasticsearch 7.x. OpenSearch should have proactively removed in 2.0. This commit removes the remaining Joda dependency and formatting logic in favor of java 8 time. It adds a new LegacyFormat class, however, to ensure camelCase DateFormat is supported for indexes created in ElasticSearch 7.x (and carried through OpenSearch 1.x). Signed-off-by: Nicholas Walter Knize --- .../time/DateFormatterBenchmark.java | 7 - .../opensearch-server-signatures.txt | 9 - buildSrc/version.properties | 1 - .../java/org/opensearch/client/CrudIT.java | 11 +- .../script/expression/DateObject.java | 97 +- .../expression/DateObjectValueSource.java | 16 +- plugins/repository-s3/build.gradle | 2 +- .../30_camel_case_dateformat.yml | 19 + .../test/old_cluster/20_date_range.yml | 80 - .../old_cluster/30_camel_case_dateformat.yml | 79 + .../30_camel_case_dateformat.yml | 20 + server/build.gradle | 3 - server/licenses/joda-time-2.12.2.jar.sha1 | 1 - server/licenses/joda-time-LICENSE.txt | 202 -- server/licenses/joda-time-NOTICE.txt | 5 - ...DateMathIndexExpressionsIntegrationIT.java | 104 +- .../search/fields/SearchFieldsIT.java | 19 +- .../time/format/StrictISODateTimeFormat.java | 1906 ----------------- .../org/opensearch/bootstrap/Security.java | 2 +- .../java/org/opensearch/common/joda/Joda.java | 567 ----- .../common/joda/JodaDateFormatter.java | 164 -- .../common/joda/JodaDateMathParser.java | 234 -- .../common/joda/JodaDeprecationPatterns.java | 108 - .../opensearch/common/joda/package-info.java | 10 - .../opensearch/common/time/DateFormatter.java | 39 +- .../common/time/DateFormatters.java | 17 +- .../common/time/DateMathParser.java | 18 +- .../org/opensearch/common/time/DateUtils.java | 23 - .../opensearch/common/time/FormatNames.java | 197 +- .../common/time/LegacyFormatNames.java | 122 ++ .../index/mapper/DateFieldMapper.java | 11 +- .../index/mapper/ParametrizedFieldMapper.java | 3 +- .../opensearch/script/ClassPermission.java | 15 +- .../org/opensearch/search/DocValueFormat.java | 7 +- .../support/values/ScriptDoubleValues.java | 7 - .../support/values/ScriptLongValues.java | 4 - .../DateMathExpressionResolverTests.java | 93 +- .../org/opensearch/common/RoundingTests.java | 2 +- .../joda/JavaJodaTimeDuellingTests.java | 952 -------- .../common/joda/JodaDateMathParserTests.java | 364 ---- .../org/opensearch/common/joda/JodaTests.java | 84 - .../common/time/DateFormattersTests.java | 832 ++++++- .../common/time/DateUtilsTests.java | 27 - .../index/mapper/DateFieldMapperTests.java | 25 + .../index/mapper/DateFieldTypeTests.java | 34 +- .../index/mapper/RangeFieldTypeTests.java | 39 +- .../DistanceFeatureQueryBuilderTests.java | 3 +- .../index/query/RangeQueryBuilderTests.java | 30 +- .../FunctionScoreQueryBuilderTests.java | 10 +- .../bucket/DateScriptMocksPlugin.java | 20 +- .../bucket/range/InternalDateRangeTests.java | 14 +- .../test/AbstractQueryTestCase.java | 6 +- .../opensearch/test/OpenSearchTestCase.java | 136 +- 53 files changed, 1492 insertions(+), 5308 deletions(-) create mode 100644 qa/rolling-upgrade/src/test/resources/rest-api-spec/test/mixed_cluster/30_camel_case_dateformat.yml create mode 100644 qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/30_camel_case_dateformat.yml create mode 100644 qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/30_camel_case_dateformat.yml delete mode 100644 server/licenses/joda-time-2.12.2.jar.sha1 delete mode 100644 server/licenses/joda-time-LICENSE.txt delete mode 100644 server/licenses/joda-time-NOTICE.txt delete mode 100644 server/src/main/java/org/joda/time/format/StrictISODateTimeFormat.java delete mode 100644 server/src/main/java/org/opensearch/common/joda/Joda.java delete mode 100644 server/src/main/java/org/opensearch/common/joda/JodaDateFormatter.java delete mode 100644 server/src/main/java/org/opensearch/common/joda/JodaDateMathParser.java delete mode 100644 server/src/main/java/org/opensearch/common/joda/JodaDeprecationPatterns.java delete mode 100644 server/src/main/java/org/opensearch/common/joda/package-info.java create mode 100644 server/src/main/java/org/opensearch/common/time/LegacyFormatNames.java delete mode 100644 server/src/test/java/org/opensearch/common/joda/JavaJodaTimeDuellingTests.java delete mode 100644 server/src/test/java/org/opensearch/common/joda/JodaDateMathParserTests.java delete mode 100644 server/src/test/java/org/opensearch/common/joda/JodaTests.java 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..61533cf8286ad --- /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": "2019-02-01T00:00+01:00"}' + + - do: + search: + rest_total_hits_as_int: true + index: camel_case_date_format + body: + query: + range: + date_field: + gte: "2019-01-01T00:00+01:00" + lte: "2019-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..119fcf33c036f --- /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 on 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 bwc version is > 8.0.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_on_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_on_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..35f38f37968db 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 @@ -155,7 +137,7 @@ default String formatJoda(DateTime dateTime) { */ DateMathParser toDateMathParser(); - static DateFormatter forPattern(String input, String printPattern, Boolean canCacheFormatter) { + 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 +146,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 +166,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..bdd739bf04e15 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,56 @@ 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); + 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)); } /** From 3739a5eb82f59f209b45fa031d471e330a16cef7 Mon Sep 17 00:00:00 2001 From: Nicholas Walter Knize Date: Fri, 27 Oct 2023 11:53:49 -0500 Subject: [PATCH 2/4] fix failing tests Signed-off-by: Nicholas Walter Knize --- .../test/old_cluster/30_camel_case_dateformat.yml | 8 ++++---- .../cluster/metadata/DateMathExpressionResolverTests.java | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) 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 index 119fcf33c036f..66c0f400ebb5d 100644 --- 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 @@ -1,5 +1,5 @@ --- -"Create index with camel case on date format (allowed with warning prior to 2.12.0)": +"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" @@ -40,14 +40,14 @@ - match: { hits.total: 1 } --- -"Create index with camel case date format (when bwc version is > 8.0.0)": +"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_on_format + index: camel_case_date_format body: settings: index: @@ -69,7 +69,7 @@ - do: search: rest_total_hits_as_int: true - index: camel_case_on_format + index: camel_case_date_format body: query: range: 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 bdd739bf04e15..20137c7316333 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DateMathExpressionResolverTests.java @@ -208,6 +208,9 @@ public void testExpression_CustomTimeZoneInIndexName() throws Exception { } else { hoursOffset = randomIntBetween(-11, 13); minutesOffset = randomIntBetween(0, 59); + if (hoursOffset < 0) { + minutesOffset = -minutesOffset; + } timeZone = ZoneOffset.ofHoursMinutes(hoursOffset, minutesOffset); } ZonedDateTime now; From 2e9e1c6bbfd7101c6673d0378a367624a32ee445 Mon Sep 17 00:00:00 2001 From: Nicholas Walter Knize Date: Tue, 31 Oct 2023 09:58:40 -0500 Subject: [PATCH 3/4] update changelog, fix tests Signed-off-by: Nicholas Walter Knize --- CHANGELOG.md | 1 + .../test/mixed_cluster/30_camel_case_dateformat.yml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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 index 61533cf8286ad..528f4afa5fdd1 100644 --- 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 @@ -5,7 +5,7 @@ refresh: true body: - '{"index": {"_index": "camel_case_date_format"}}' - - '{"date_field": "2019-02-01T00:00+01:00"}' + - '{"date_field": "2023-02-01T00:00+01:00"}' - do: search: @@ -15,5 +15,5 @@ query: range: date_field: - gte: "2019-01-01T00:00+01:00" - lte: "2019-03-01T00:00+01:00" + gte: "2023-01-01T00:00+01:00" + lte: "2023-03-01T00:00+01:00" From 07b930e073c1616f70fbc1419420b49f1d2d0686 Mon Sep 17 00:00:00 2001 From: Nicholas Walter Knize Date: Mon, 13 Nov 2023 09:00:46 -0600 Subject: [PATCH 4/4] add original forPattern method to preserve interface. Signed-off-by: Nicholas Walter Knize --- .../main/java/org/opensearch/common/time/DateFormatter.java | 4 ++++ 1 file changed, 4 insertions(+) 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 35f38f37968db..66ebca4010e6c 100644 --- a/server/src/main/java/org/opensearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/opensearch/common/time/DateFormatter.java @@ -137,6 +137,10 @@ default String formatMillis(long millis) { */ 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) {