diff --git a/server/src/main/java/org/elasticsearch/common/joda/Joda.java b/server/src/main/java/org/elasticsearch/common/joda/Joda.java index 45587f6bb3df9..977e1ad56dc11 100644 --- a/server/src/main/java/org/elasticsearch/common/joda/Joda.java +++ b/server/src/main/java/org/elasticsearch/common/joda/Joda.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.time.DateFormatter; +import org.elasticsearch.common.time.FormatNames; import org.joda.time.Chronology; import org.joda.time.DateTime; import org.joda.time.DateTimeField; @@ -65,173 +66,173 @@ public static JodaDateFormatter forPattern(String input) { } DateTimeFormatter formatter; - if ("basicDate".equals(input) || "basic_date".equals(input)) { + if (FormatNames.BASIC_DATE.matches(input)) { formatter = ISODateTimeFormat.basicDate(); - } else if ("basicDateTime".equals(input) || "basic_date_time".equals(input)) { + } else if (FormatNames.BASIC_DATE_TIME.matches(input) ) { formatter = ISODateTimeFormat.basicDateTime(); - } else if ("basicDateTimeNoMillis".equals(input) || "basic_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_DATE_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.basicDateTimeNoMillis(); - } else if ("basicOrdinalDate".equals(input) || "basic_ordinal_date".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE.matches(input) ) { formatter = ISODateTimeFormat.basicOrdinalDate(); - } else if ("basicOrdinalDateTime".equals(input) || "basic_ordinal_date_time".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE_TIME.matches(input) ) { formatter = ISODateTimeFormat.basicOrdinalDateTime(); - } else if ("basicOrdinalDateTimeNoMillis".equals(input) || "basic_ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.basicOrdinalDateTimeNoMillis(); - } else if ("basicTime".equals(input) || "basic_time".equals(input)) { + } else if (FormatNames.BASIC_TIME.matches(input) ) { formatter = ISODateTimeFormat.basicTime(); - } else if ("basicTimeNoMillis".equals(input) || "basic_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.basicTimeNoMillis(); - } else if ("basicTTime".equals(input) || "basic_t_time".equals(input)) { + } else if (FormatNames.BASIC_T_TIME.matches(input) ) { formatter = ISODateTimeFormat.basicTTime(); - } else if ("basicTTimeNoMillis".equals(input) || "basic_t_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_T_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.basicTTimeNoMillis(); - } else if ("basicWeekDate".equals(input) || "basic_week_date".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE.matches(input)) { formatter = ISODateTimeFormat.basicWeekDate(); - } else if ("basicWeekDateTime".equals(input) || "basic_week_date_time".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE_TIME.matches(input) ) { formatter = ISODateTimeFormat.basicWeekDateTime(); - } else if ("basicWeekDateTimeNoMillis".equals(input) || "basic_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { formatter = ISODateTimeFormat.basicWeekDateTimeNoMillis(); - } else if ("date".equals(input)) { + } else if (FormatNames.DATE.matches(input)) { formatter = ISODateTimeFormat.date(); - } else if ("dateHour".equals(input) || "date_hour".equals(input)) { + } else if (FormatNames.DATE_HOUR.matches(input)) { formatter = ISODateTimeFormat.dateHour(); - } else if ("dateHourMinute".equals(input) || "date_hour_minute".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE.matches(input)) { formatter = ISODateTimeFormat.dateHourMinute(); - } else if ("dateHourMinuteSecond".equals(input) || "date_hour_minute_second".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND.matches(input)) { formatter = ISODateTimeFormat.dateHourMinuteSecond(); - } else if ("dateHourMinuteSecondFraction".equals(input) || "date_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { formatter = ISODateTimeFormat.dateHourMinuteSecondFraction(); - } else if ("dateHourMinuteSecondMillis".equals(input) || "date_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.dateHourMinuteSecondMillis(); - } else if ("dateOptionalTime".equals(input) || "date_optional_time".equals(input)) { + } 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 ("dateTime".equals(input) || "date_time".equals(input)) { + } else if (FormatNames.DATE_TIME.matches(input)) { formatter = ISODateTimeFormat.dateTime(); - } else if ("dateTimeNoMillis".equals(input) || "date_time_no_millis".equals(input)) { + } else if (FormatNames.DATE_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.dateTimeNoMillis(); - } else if ("hour".equals(input)) { + } else if (FormatNames.HOUR.matches(input)) { formatter = ISODateTimeFormat.hour(); - } else if ("hourMinute".equals(input) || "hour_minute".equals(input)) { + } else if (FormatNames.HOUR_MINUTE.matches(input)) { formatter = ISODateTimeFormat.hourMinute(); - } else if ("hourMinuteSecond".equals(input) || "hour_minute_second".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND.matches(input) ) { formatter = ISODateTimeFormat.hourMinuteSecond(); - } else if ("hourMinuteSecondFraction".equals(input) || "hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND_FRACTION.matches(input)) { formatter = ISODateTimeFormat.hourMinuteSecondFraction(); - } else if ("hourMinuteSecondMillis".equals(input) || "hour_minute_second_millis".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND_MILLIS.matches(input)) { formatter = ISODateTimeFormat.hourMinuteSecondMillis(); - } else if ("ordinalDate".equals(input) || "ordinal_date".equals(input)) { + } else if (FormatNames.ORDINAL_DATE.matches(input)) { formatter = ISODateTimeFormat.ordinalDate(); - } else if ("ordinalDateTime".equals(input) || "ordinal_date_time".equals(input)) { + } else if (FormatNames.ORDINAL_DATE_TIME.matches(input)) { formatter = ISODateTimeFormat.ordinalDateTime(); - } else if ("ordinalDateTimeNoMillis".equals(input) || "ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.ORDINAL_DATE_TIME_NO_MILLIS.matches(input) ) { formatter = ISODateTimeFormat.ordinalDateTimeNoMillis(); - } else if ("time".equals(input)) { + } else if (FormatNames.TIME.matches(input)) { formatter = ISODateTimeFormat.time(); - } else if ("timeNoMillis".equals(input) || "time_no_millis".equals(input)) { + } else if (FormatNames.TIME_NO_MILLIS.matches(input)) { formatter = ISODateTimeFormat.timeNoMillis(); - } else if ("tTime".equals(input) || "t_time".equals(input)) { + } else if (FormatNames.T_TIME.matches(input)) { formatter = ISODateTimeFormat.tTime(); - } else if ("tTimeNoMillis".equals(input) || "t_time_no_millis".equals(input)) { + } else if (FormatNames.T_TIME_NO_MILLIS.matches(input)) { formatter = ISODateTimeFormat.tTimeNoMillis(); - } else if ("weekDate".equals(input) || "week_date".equals(input)) { + } else if (FormatNames.WEEK_DATE.matches(input)) { formatter = ISODateTimeFormat.weekDate(); - } else if ("weekDateTime".equals(input) || "week_date_time".equals(input)) { + } else if (FormatNames.WEEK_DATE_TIME.matches(input)) { formatter = ISODateTimeFormat.weekDateTime(); - } else if ("weekDateTimeNoMillis".equals(input) || "week_date_time_no_millis".equals(input)) { + } else if (FormatNames.WEEK_DATE_TIME_NO_MILLIS.matches(input)) { formatter = ISODateTimeFormat.weekDateTimeNoMillis(); - } else if ("weekyear".equals(input) || "week_year".equals(input)) { + } else if (FormatNames.WEEK_YEAR.matches(input)) { formatter = ISODateTimeFormat.weekyear(); - } else if ("weekyearWeek".equals(input) || "weekyear_week".equals(input)) { + } else if (FormatNames.WEEK_YEAR_WEEK.matches(input)) { formatter = ISODateTimeFormat.weekyearWeek(); - } else if ("weekyearWeekDay".equals(input) || "weekyear_week_day".equals(input)) { + } else if (FormatNames.WEEKYEAR_WEEK_DAY.matches(input)) { formatter = ISODateTimeFormat.weekyearWeekDay(); - } else if ("year".equals(input)) { + } else if (FormatNames.YEAR.matches(input)) { formatter = ISODateTimeFormat.year(); - } else if ("yearMonth".equals(input) || "year_month".equals(input)) { + } else if (FormatNames.YEAR_MONTH.matches(input) ) { formatter = ISODateTimeFormat.yearMonth(); - } else if ("yearMonthDay".equals(input) || "year_month_day".equals(input)) { + } else if (FormatNames.YEAR_MONTH_DAY.matches(input)) { formatter = ISODateTimeFormat.yearMonthDay(); - } else if ("epoch_second".equals(input)) { + } else if (FormatNames.EPOCH_SECOND.matches(input)) { formatter = new DateTimeFormatterBuilder().append(new EpochTimePrinter(false), new EpochTimeParser(false)).toFormatter(); - } else if ("epoch_millis".equals(input)) { + } 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 ("strictBasicWeekDate".equals(input) || "strict_basic_week_date".equals(input)) { + } else if (FormatNames.STRICT_BASIC_WEEK_DATE.matches(input) ) { formatter = StrictISODateTimeFormat.basicWeekDate(); - } else if ("strictBasicWeekDateTime".equals(input) || "strict_basic_week_date_time".equals(input)) { + } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME.matches(input)) { formatter = StrictISODateTimeFormat.basicWeekDateTime(); - } else if ("strictBasicWeekDateTimeNoMillis".equals(input) || "strict_basic_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.basicWeekDateTimeNoMillis(); - } else if ("strictDate".equals(input) || "strict_date".equals(input)) { + } else if (FormatNames.STRICT_DATE.matches(input)) { formatter = StrictISODateTimeFormat.date(); - } else if ("strictDateHour".equals(input) || "strict_date_hour".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR.matches(input)) { formatter = StrictISODateTimeFormat.dateHour(); - } else if ("strictDateHourMinute".equals(input) || "strict_date_hour_minute".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE.matches(input)) { formatter = StrictISODateTimeFormat.dateHourMinute(); - } else if ("strictDateHourMinuteSecond".equals(input) || "strict_date_hour_minute_second".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND.matches(input)) { formatter = StrictISODateTimeFormat.dateHourMinuteSecond(); - } else if ("strictDateHourMinuteSecondFraction".equals(input) || "strict_date_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { formatter = StrictISODateTimeFormat.dateHourMinuteSecondFraction(); - } else if ("strictDateHourMinuteSecondMillis".equals(input) || "strict_date_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.dateHourMinuteSecondMillis(); - } else if ("strictDateOptionalTime".equals(input) || "strict_date_optional_time".equals(input)) { + } 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 ("strictDateTime".equals(input) || "strict_date_time".equals(input)) { + } else if (FormatNames.STRICT_DATE_TIME.matches(input)) { formatter = StrictISODateTimeFormat.dateTime(); - } else if ("strictDateTimeNoMillis".equals(input) || "strict_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_DATE_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.dateTimeNoMillis(); - } else if ("strictHour".equals(input) || "strict_hour".equals(input)) { + } else if (FormatNames.STRICT_HOUR.matches(input)) { formatter = StrictISODateTimeFormat.hour(); - } else if ("strictHourMinute".equals(input) || "strict_hour_minute".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE.matches(input)) { formatter = StrictISODateTimeFormat.hourMinute(); - } else if ("strictHourMinuteSecond".equals(input) || "strict_hour_minute_second".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND.matches(input)) { formatter = StrictISODateTimeFormat.hourMinuteSecond(); - } else if ("strictHourMinuteSecondFraction".equals(input) || "strict_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_FRACTION.matches(input)) { formatter = StrictISODateTimeFormat.hourMinuteSecondFraction(); - } else if ("strictHourMinuteSecondMillis".equals(input) || "strict_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.hourMinuteSecondMillis(); - } else if ("strictOrdinalDate".equals(input) || "strict_ordinal_date".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE.matches(input)) { formatter = StrictISODateTimeFormat.ordinalDate(); - } else if ("strictOrdinalDateTime".equals(input) || "strict_ordinal_date_time".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE_TIME.matches(input)) { formatter = StrictISODateTimeFormat.ordinalDateTime(); - } else if ("strictOrdinalDateTimeNoMillis".equals(input) || "strict_ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.ordinalDateTimeNoMillis(); - } else if ("strictTime".equals(input) || "strict_time".equals(input)) { + } else if (FormatNames.STRICT_TIME.matches(input)) { formatter = StrictISODateTimeFormat.time(); - } else if ("strictTimeNoMillis".equals(input) || "strict_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.timeNoMillis(); - } else if ("strictTTime".equals(input) || "strict_t_time".equals(input)) { + } else if (FormatNames.STRICT_T_TIME.matches(input) ) { formatter = StrictISODateTimeFormat.tTime(); - } else if ("strictTTimeNoMillis".equals(input) || "strict_t_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_T_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.tTimeNoMillis(); - } else if ("strictWeekDate".equals(input) || "strict_week_date".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE.matches(input)) { formatter = StrictISODateTimeFormat.weekDate(); - } else if ("strictWeekDateTime".equals(input) || "strict_week_date_time".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE_TIME.matches(input)) { formatter = StrictISODateTimeFormat.weekDateTime(); - } else if ("strictWeekDateTimeNoMillis".equals(input) || "strict_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE_TIME_NO_MILLIS.matches(input)) { formatter = StrictISODateTimeFormat.weekDateTimeNoMillis(); - } else if ("strictWeekyear".equals(input) || "strict_weekyear".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR.matches(input) ) { formatter = StrictISODateTimeFormat.weekyear(); - } else if ("strictWeekyearWeek".equals(input) || "strict_weekyear_week".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR_WEEK.matches(input)) { formatter = StrictISODateTimeFormat.weekyearWeek(); - } else if ("strictWeekyearWeekDay".equals(input) || "strict_weekyear_week_day".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR_WEEK_DAY.matches(input)) { formatter = StrictISODateTimeFormat.weekyearWeekDay(); - } else if ("strictYear".equals(input) || "strict_year".equals(input)) { + } else if (FormatNames.STRICT_YEAR.matches(input)) { formatter = StrictISODateTimeFormat.year(); - } else if ("strictYearMonth".equals(input) || "strict_year_month".equals(input)) { + } else if (FormatNames.STRICT_YEAR_MONTH.matches(input)) { formatter = StrictISODateTimeFormat.yearMonth(); - } else if ("strictYearMonthDay".equals(input) || "strict_year_month_day".equals(input)) { + } else if (FormatNames.STRICT_YEAR_MONTH_DAY.matches(input)) { formatter = StrictISODateTimeFormat.yearMonthDay(); } else if (Strings.hasLength(input) && input.contains("||")) { String[] formats = Strings.delimitedListToStringArray(input, "||"); @@ -267,18 +268,11 @@ public static JodaDateFormatter forPattern(String input) { return new JodaDateFormatter(input, formatter, formatter); } - private static void maybeLogJodaDeprecation(String input) { - if (input.contains("CC")) { - deprecationLogger.deprecatedAndMaybeLog("joda-century-of-era-format", - "Use of 'C' (century-of-era) is deprecated and will not be supported in the next major version of Elasticsearch."); - } - if (input.contains("YY")) { - deprecationLogger.deprecatedAndMaybeLog("joda-year-of-era-format", "Use of 'Y' (year-of-era) will change to 'y' in the" + - " next major version of Elasticsearch. Prefix your date format with '8' to use the new specifier."); - } - if (input.contains("xx")) { - deprecationLogger.deprecatedAndMaybeLog("joda-week-based-year-format","Use of 'x' (week-based-year) will change" + - " to 'Y' in the next major version of Elasticsearch. Prefix your date format with '8' to use the new specifier."); + private static void maybeLogJodaDeprecation(String format) { + if (JodaDeprecationPatterns.isDeprecatedPattern(format)) { + String suggestion = JodaDeprecationPatterns.formatSuggestion(format); + deprecationLogger.deprecatedAndMaybeLog("joda-pattern-deprecation", + suggestion + " " + JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); } } diff --git a/server/src/main/java/org/elasticsearch/common/joda/JodaDeprecationPatterns.java b/server/src/main/java/org/elasticsearch/common/joda/JodaDeprecationPatterns.java new file mode 100644 index 0000000000000..6bd121da50076 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/joda/JodaDeprecationPatterns.java @@ -0,0 +1,94 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.common.joda; + +import org.elasticsearch.common.time.DateFormatter; +import org.elasticsearch.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; + +public class JodaDeprecationPatterns { + public static final String USE_NEW_FORMAT_SPECIFIERS = "Use new java.time date format specifiiers."; + 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/elasticsearch/common/time/DateFormatter.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java index bf7999067b05a..3ed6d95efe52a 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; public interface DateFormatter { @@ -137,13 +138,10 @@ static DateFormatter forPattern(String input) { input = input.substring(1); } - List formatters = new ArrayList<>(); - for (String pattern : Strings.delimitedListToStringArray(input, "||")) { - if (Strings.hasLength(pattern) == false) { - throw new IllegalArgumentException("Cannot have empty element in multi date format pattern: " + input); - } - formatters.add(DateFormatters.forPattern(pattern)); - } + List patterns = splitCombinedPatterns(input); + List formatters = patterns.stream() + .map(DateFormatters::forPattern) + .collect(Collectors.toList()); if (formatters.size() == 1) { return formatters.get(0); @@ -151,4 +149,15 @@ static DateFormatter forPattern(String input) { return DateFormatters.merge(input, formatters); } + + static List splitCombinedPatterns(String input) { + List patterns = new ArrayList<>(); + for (String pattern : Strings.delimitedListToStringArray(input, "||")) { + if (Strings.hasLength(pattern) == false) { + throw new IllegalArgumentException("Cannot have empty element in multi date format pattern: " + input); + } + patterns.add(pattern); + } + return patterns; + } } diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java index 330681e2624a2..c8aa15d9b92da 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -1423,166 +1423,166 @@ static DateFormatter forPattern(String input) { throw new IllegalArgumentException("No date pattern provided"); } - if ("iso8601".equals(input)) { + if (FormatNames.ISO8601.matches(input)) { return ISO_8601; - } else if ("basicDate".equals(input) || "basic_date".equals(input)) { + } else if (FormatNames.BASIC_DATE.matches(input) ) { return BASIC_DATE; - } else if ("basicDateTime".equals(input) || "basic_date_time".equals(input)) { + } else if (FormatNames.BASIC_DATE_TIME.matches(input) ) { return BASIC_DATE_TIME; - } else if ("basicDateTimeNoMillis".equals(input) || "basic_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_DATE_TIME_NO_MILLIS.matches(input) ) { return BASIC_DATE_TIME_NO_MILLIS; - } else if ("basicOrdinalDate".equals(input) || "basic_ordinal_date".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE.matches(input) ) { return BASIC_ORDINAL_DATE; - } else if ("basicOrdinalDateTime".equals(input) || "basic_ordinal_date_time".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE_TIME.matches(input) ) { return BASIC_ORDINAL_DATE_TIME; - } else if ("basicOrdinalDateTimeNoMillis".equals(input) || "basic_ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_ORDINAL_DATE_TIME_NO_MILLIS.matches(input) ) { return BASIC_ORDINAL_DATE_TIME_NO_MILLIS; - } else if ("basicTime".equals(input) || "basic_time".equals(input)) { + } else if (FormatNames.BASIC_TIME.matches(input) ) { return BASIC_TIME; - } else if ("basicTimeNoMillis".equals(input) || "basic_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_TIME_NO_MILLIS.matches(input) ) { return BASIC_TIME_NO_MILLIS; - } else if ("basicTTime".equals(input) || "basic_t_time".equals(input)) { + } else if (FormatNames.BASIC_T_TIME.matches(input) ) { return BASIC_T_TIME; - } else if ("basicTTimeNoMillis".equals(input) || "basic_t_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_T_TIME_NO_MILLIS.matches(input) ) { return BASIC_T_TIME_NO_MILLIS; - } else if ("basicWeekDate".equals(input) || "basic_week_date".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE.matches(input) ) { return BASIC_WEEK_DATE; - } else if ("basicWeekDateTime".equals(input) || "basic_week_date_time".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE_TIME.matches(input) ) { return BASIC_WEEK_DATE_TIME; - } else if ("basicWeekDateTimeNoMillis".equals(input) || "basic_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input) ) { return BASIC_WEEK_DATE_TIME_NO_MILLIS; - } else if ("date".equals(input)) { + } else if (FormatNames.DATE.matches(input)) { return DATE; - } else if ("dateHour".equals(input) || "date_hour".equals(input)) { + } else if (FormatNames.DATE_HOUR.matches(input) ) { return DATE_HOUR; - } else if ("dateHourMinute".equals(input) || "date_hour_minute".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE.matches(input) ) { return DATE_HOUR_MINUTE; - } else if ("dateHourMinuteSecond".equals(input) || "date_hour_minute_second".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND.matches(input) ) { return DATE_HOUR_MINUTE_SECOND; - } else if ("dateHourMinuteSecondFraction".equals(input) || "date_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input) ) { return DATE_HOUR_MINUTE_SECOND_FRACTION; - } else if ("dateHourMinuteSecondMillis".equals(input) || "date_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input) ) { return DATE_HOUR_MINUTE_SECOND_MILLIS; - } else if ("dateOptionalTime".equals(input) || "date_optional_time".equals(input)) { + } else if (FormatNames.DATE_OPTIONAL_TIME.matches(input) ) { return DATE_OPTIONAL_TIME; - } else if ("dateTime".equals(input) || "date_time".equals(input)) { + } else if (FormatNames.DATE_TIME.matches(input) ) { return DATE_TIME; - } else if ("dateTimeNoMillis".equals(input) || "date_time_no_millis".equals(input)) { + } else if (FormatNames.DATE_TIME_NO_MILLIS.matches(input) ) { return DATE_TIME_NO_MILLIS; - } else if ("hour".equals(input)) { + } else if (FormatNames.HOUR.matches(input)) { return HOUR; - } else if ("hourMinute".equals(input) || "hour_minute".equals(input)) { + } else if (FormatNames.HOUR_MINUTE.matches(input) ) { return HOUR_MINUTE; - } else if ("hourMinuteSecond".equals(input) || "hour_minute_second".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND.matches(input) ) { return HOUR_MINUTE_SECOND; - } else if ("hourMinuteSecondFraction".equals(input) || "hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND_FRACTION.matches(input) ) { return HOUR_MINUTE_SECOND_FRACTION; - } else if ("hourMinuteSecondMillis".equals(input) || "hour_minute_second_millis".equals(input)) { + } else if (FormatNames.HOUR_MINUTE_SECOND_MILLIS.matches(input) ) { return HOUR_MINUTE_SECOND_MILLIS; - } else if ("ordinalDate".equals(input) || "ordinal_date".equals(input)) { + } else if (FormatNames.ORDINAL_DATE.matches(input) ) { return ORDINAL_DATE; - } else if ("ordinalDateTime".equals(input) || "ordinal_date_time".equals(input)) { + } else if (FormatNames.ORDINAL_DATE_TIME.matches(input) ) { return ORDINAL_DATE_TIME; - } else if ("ordinalDateTimeNoMillis".equals(input) || "ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.ORDINAL_DATE_TIME_NO_MILLIS.matches(input) ) { return ORDINAL_DATE_TIME_NO_MILLIS; - } else if ("time".equals(input)) { + } else if (FormatNames.TIME.matches(input)) { return TIME; - } else if ("timeNoMillis".equals(input) || "time_no_millis".equals(input)) { + } else if (FormatNames.TIME_NO_MILLIS.matches(input) ) { return TIME_NO_MILLIS; - } else if ("tTime".equals(input) || "t_time".equals(input)) { + } else if (FormatNames.T_TIME.matches(input) ) { return T_TIME; - } else if ("tTimeNoMillis".equals(input) || "t_time_no_millis".equals(input)) { + } else if (FormatNames.T_TIME_NO_MILLIS.matches(input) ) { return T_TIME_NO_MILLIS; - } else if ("weekDate".equals(input) || "week_date".equals(input)) { + } else if (FormatNames.WEEK_DATE.matches(input) ) { return WEEK_DATE; - } else if ("weekDateTime".equals(input) || "week_date_time".equals(input)) { + } else if (FormatNames.WEEK_DATE_TIME.matches(input) ) { return WEEK_DATE_TIME; - } else if ("weekDateTimeNoMillis".equals(input) || "week_date_time_no_millis".equals(input)) { + } else if (FormatNames.WEEK_DATE_TIME_NO_MILLIS.matches(input) ) { return WEEK_DATE_TIME_NO_MILLIS; - } else if ("weekyear".equals(input) || "week_year".equals(input)) { + } else if (FormatNames.WEEK_YEAR.matches(input) ) { return WEEK_YEAR; - } else if ("weekyearWeek".equals(input) || "weekyear_week".equals(input)) { + } else if (FormatNames.WEEK_YEAR_WEEK.matches(input) ) { return WEEKYEAR_WEEK; - } else if ("weekyearWeekDay".equals(input) || "weekyear_week_day".equals(input)) { + } else if (FormatNames.WEEKYEAR_WEEK_DAY.matches(input) ) { return WEEKYEAR_WEEK_DAY; - } else if ("year".equals(input)) { + } else if (FormatNames.YEAR.matches(input)) { return YEAR; - } else if ("yearMonth".equals(input) || "year_month".equals(input)) { + } else if (FormatNames.YEAR_MONTH.matches(input) ) { return YEAR_MONTH; - } else if ("yearMonthDay".equals(input) || "year_month_day".equals(input)) { + } else if (FormatNames.YEAR_MONTH_DAY.matches(input) ) { return YEAR_MONTH_DAY; - } else if ("epoch_second".equals(input)) { + } else if (FormatNames.EPOCH_SECOND.matches(input)) { return EpochTime.SECONDS_FORMATTER; - } else if ("epoch_millis".equals(input)) { + } else if (FormatNames.EPOCH_MILLIS.matches(input)) { return EpochTime.MILLIS_FORMATTER; - // strict date formats here, must be at least 4 digits for year and two for months and two for day - } else if ("strictBasicWeekDate".equals(input) || "strict_basic_week_date".equals(input)) { + // 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) ) { return STRICT_BASIC_WEEK_DATE; - } else if ("strictBasicWeekDateTime".equals(input) || "strict_basic_week_date_time".equals(input)) { + } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME.matches(input) ) { return STRICT_BASIC_WEEK_DATE_TIME; - } else if ("strictBasicWeekDateTimeNoMillis".equals(input) || "strict_basic_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS.matches(input) ) { return STRICT_BASIC_WEEK_DATE_TIME_NO_MILLIS; - } else if ("strictDate".equals(input) || "strict_date".equals(input)) { + } else if (FormatNames.STRICT_DATE.matches(input) ) { return STRICT_DATE; - } else if ("strictDateHour".equals(input) || "strict_date_hour".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR.matches(input) ) { return STRICT_DATE_HOUR; - } else if ("strictDateHourMinute".equals(input) || "strict_date_hour_minute".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE.matches(input) ) { return STRICT_DATE_HOUR_MINUTE; - } else if ("strictDateHourMinuteSecond".equals(input) || "strict_date_hour_minute_second".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND.matches(input) ) { return STRICT_DATE_HOUR_MINUTE_SECOND; - } else if ("strictDateHourMinuteSecondFraction".equals(input) || "strict_date_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION.matches(input) ) { return STRICT_DATE_HOUR_MINUTE_SECOND_FRACTION; - } else if ("strictDateHourMinuteSecondMillis".equals(input) || "strict_date_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS.matches(input) ) { return STRICT_DATE_HOUR_MINUTE_SECOND_MILLIS; - } else if ("strictDateOptionalTime".equals(input) || "strict_date_optional_time".equals(input)) { + } else if (FormatNames.STRICT_DATE_OPTIONAL_TIME.matches(input) ) { return STRICT_DATE_OPTIONAL_TIME; - } else if ("strictDateOptionalTimeNanos".equals(input) || "strict_date_optional_time_nanos".equals(input)) { + } else if (FormatNames.STRICT_DATE_OPTIONAL_TIME_NANOS.matches(input) ) { return STRICT_DATE_OPTIONAL_TIME_NANOS; - } else if ("strictDateTime".equals(input) || "strict_date_time".equals(input)) { + } else if (FormatNames.STRICT_DATE_TIME.matches(input) ) { return STRICT_DATE_TIME; - } else if ("strictDateTimeNoMillis".equals(input) || "strict_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_DATE_TIME_NO_MILLIS.matches(input) ) { return STRICT_DATE_TIME_NO_MILLIS; - } else if ("strictHour".equals(input) || "strict_hour".equals(input)) { + } else if (FormatNames.STRICT_HOUR.matches(input) ) { return STRICT_HOUR; - } else if ("strictHourMinute".equals(input) || "strict_hour_minute".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE.matches(input) ) { return STRICT_HOUR_MINUTE; - } else if ("strictHourMinuteSecond".equals(input) || "strict_hour_minute_second".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND.matches(input) ) { return STRICT_HOUR_MINUTE_SECOND; - } else if ("strictHourMinuteSecondFraction".equals(input) || "strict_hour_minute_second_fraction".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_FRACTION.matches(input) ) { return STRICT_HOUR_MINUTE_SECOND_FRACTION; - } else if ("strictHourMinuteSecondMillis".equals(input) || "strict_hour_minute_second_millis".equals(input)) { + } else if (FormatNames.STRICT_HOUR_MINUTE_SECOND_MILLIS.matches(input) ) { return STRICT_HOUR_MINUTE_SECOND_MILLIS; - } else if ("strictOrdinalDate".equals(input) || "strict_ordinal_date".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE.matches(input) ) { return STRICT_ORDINAL_DATE; - } else if ("strictOrdinalDateTime".equals(input) || "strict_ordinal_date_time".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE_TIME.matches(input) ) { return STRICT_ORDINAL_DATE_TIME; - } else if ("strictOrdinalDateTimeNoMillis".equals(input) || "strict_ordinal_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_ORDINAL_DATE_TIME_NO_MILLIS.matches(input) ) { return STRICT_ORDINAL_DATE_TIME_NO_MILLIS; - } else if ("strictTime".equals(input) || "strict_time".equals(input)) { + } else if (FormatNames.STRICT_TIME.matches(input) ) { return STRICT_TIME; - } else if ("strictTimeNoMillis".equals(input) || "strict_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_TIME_NO_MILLIS.matches(input) ) { return STRICT_TIME_NO_MILLIS; - } else if ("strictTTime".equals(input) || "strict_t_time".equals(input)) { + } else if (FormatNames.STRICT_T_TIME.matches(input) ) { return STRICT_T_TIME; - } else if ("strictTTimeNoMillis".equals(input) || "strict_t_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_T_TIME_NO_MILLIS.matches(input) ) { return STRICT_T_TIME_NO_MILLIS; - } else if ("strictWeekDate".equals(input) || "strict_week_date".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE.matches(input) ) { return STRICT_WEEK_DATE; - } else if ("strictWeekDateTime".equals(input) || "strict_week_date_time".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE_TIME.matches(input) ) { return STRICT_WEEK_DATE_TIME; - } else if ("strictWeekDateTimeNoMillis".equals(input) || "strict_week_date_time_no_millis".equals(input)) { + } else if (FormatNames.STRICT_WEEK_DATE_TIME_NO_MILLIS.matches(input) ) { return STRICT_WEEK_DATE_TIME_NO_MILLIS; - } else if ("strictWeekyear".equals(input) || "strict_weekyear".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR.matches(input) ) { return STRICT_WEEKYEAR; - } else if ("strictWeekyearWeek".equals(input) || "strict_weekyear_week".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR_WEEK.matches(input) ) { return STRICT_WEEKYEAR_WEEK; - } else if ("strictWeekyearWeekDay".equals(input) || "strict_weekyear_week_day".equals(input)) { + } else if (FormatNames.STRICT_WEEKYEAR_WEEK_DAY.matches(input) ) { return STRICT_WEEKYEAR_WEEK_DAY; - } else if ("strictYear".equals(input) || "strict_year".equals(input)) { + } else if (FormatNames.STRICT_YEAR.matches(input) ) { return STRICT_YEAR; - } else if ("strictYearMonth".equals(input) || "strict_year_month".equals(input)) { + } else if (FormatNames.STRICT_YEAR_MONTH.matches(input) ) { return STRICT_YEAR_MONTH; - } else if ("strictYearMonthDay".equals(input) || "strict_year_month_day".equals(input)) { + } else if (FormatNames.STRICT_YEAR_MONTH_DAY.matches(input) ) { return STRICT_YEAR_MONTH_DAY; } else { try { diff --git a/server/src/main/java/org/elasticsearch/common/time/FormatNames.java b/server/src/main/java/org/elasticsearch/common/time/FormatNames.java new file mode 100644 index 0000000000000..bf4c6d18446ea --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/time/FormatNames.java @@ -0,0 +1,128 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.common.time; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum FormatNames { + ISO8601("iso8601", "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("date", "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("hour", "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("time", "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("weekyear", "week_year"), + WEEK_YEAR_WEEK("weekyearWeek", "weekyear_week"), + WEEKYEAR_WEEK_DAY("weekyearWeekDay", "weekyear_week_day"), + YEAR("year", "year"), + YEAR_MONTH("yearMonth", "year_month"), + YEAR_MONTH_DAY("yearMonthDay", "year_month_day"), + EPOCH_SECOND("epoch_second", "epoch_second"), + EPOCH_MILLIS("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"); + + 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; + this.snakeCaseName = snakeCaseName; + } + + public static boolean exist(String format) { + return ALL_NAMES.contains(format); + } + + public boolean matches(String format) { + return format.equals(camelCaseName) || format.equals(snakeCaseName); + } +} diff --git a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java index 061d83c9c3865..16ff794979eb2 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -26,6 +26,7 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; +import org.joda.time.format.DateTimeFormat; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -34,10 +35,53 @@ import java.util.Locale; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; public class JavaJodaTimeDuellingTests extends ESTestCase { + 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 diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 6b36f985c210b..ff56921b33920 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -65,6 +65,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.joda.JodaDeprecationPatterns; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.Loggers; @@ -355,6 +356,10 @@ protected boolean enableWarningsCheck() { return true; } + protected boolean enableJodaDeprecationWarningsCheck() { + return false; + } + @After public final void after() throws Exception { checkStaticState(false); @@ -385,7 +390,13 @@ private void ensureNoWarnings() { //appropriate test try { final List warnings = threadContext.getResponseHeaders().get("Warning"); - assertNull("unexpected warning headers", warnings); + if (warnings != null && enableJodaDeprecationWarningsCheck() == false) { + List filteredWarnings = filterJodaDeprecationWarnings(warnings); + assertThat( filteredWarnings, empty()); + + } else { + assertNull("unexpected warning headers", warnings); + } } finally { resetDeprecationLogger(false); } @@ -418,20 +429,35 @@ protected final void assertWarnings(String... expectedWarnings) { } try { final List actualWarnings = threadContext.getResponseHeaders().get("Warning"); - assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings); - final Set actualWarningValues = - actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet()); - for (String msg : expectedWarnings) { - assertThat(actualWarningValues, hasItem(DeprecationLogger.escapeAndEncode(msg))); + if (actualWarnings != null && enableJodaDeprecationWarningsCheck() == false) { + List filteredWarnings = filterJodaDeprecationWarnings(actualWarnings); + assertWarnings(filteredWarnings, expectedWarnings); + } else { + assertWarnings(actualWarnings, expectedWarnings); } - assertEquals("Expected " + expectedWarnings.length + " warnings but found " + actualWarnings.size() + "\nExpected: " - + Arrays.asList(expectedWarnings) + "\nActual: " + actualWarnings, - expectedWarnings.length, actualWarnings.size()); } finally { resetDeprecationLogger(true); } } + private List filterJodaDeprecationWarnings(List actualWarnings) { + return actualWarnings.stream() + .filter(m -> m.contains(JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS) == false) + .collect(Collectors.toList()); + } + + private void assertWarnings(List actualWarnings, String[] expectedWarnings) { + assertNotNull("no warnings, expected: " + Arrays.asList(expectedWarnings), actualWarnings); + final Set actualWarningValues = + actualWarnings.stream().map(DeprecationLogger::extractWarningValueFromWarningHeader).collect(Collectors.toSet()); + for (String msg : expectedWarnings) { + assertThat(actualWarningValues, hasItem(DeprecationLogger.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 removing the current thread context, and setting a new thread context if {@code setNewThreadContext} * is set to {@code true} and otherwise clearing the current thread context. diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java index 57a474744b6f2..162a2a6bf9329 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java @@ -58,7 +58,6 @@ static DeprecationIssue checkUserAgentPipelines(ClusterState state) { "Ingest pipelines " + pipelinesWithDeprecatedEcsConfig + " uses the [ecs] option which needs to be removed to work in 8.0"); } return null; - } static DeprecationIssue checkTemplatesWithTooManyFields(ClusterState state) { diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java index b63a828ecbb67..194c412ffe259 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java @@ -48,7 +48,8 @@ private DeprecationChecks() { Collections.unmodifiableList(Arrays.asList( IndexDeprecationChecks::oldIndicesCheck, IndexDeprecationChecks::tooManyFieldsCheck, - IndexDeprecationChecks::chainedMultiFieldsCheck + IndexDeprecationChecks::chainedMultiFieldsCheck, + IndexDeprecationChecks::deprecatedDateTimeFormat )); static List> ML_SETTINGS_CHECKS = diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java index 1e9876a87fe36..ead1b665ba7d5 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java @@ -10,6 +10,7 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; +import org.elasticsearch.common.joda.JodaDeprecationPatterns; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; @@ -21,8 +22,10 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; + /** * Index-specific deprecation checks */ @@ -42,11 +45,13 @@ private static void fieldLevelMappingIssue(IndexMetaData indexMetaData, BiConsum * @param type the document type * @param parentMap the mapping to read properties from * @param predicate the predicate to check against for issues, issue is returned if predicate evaluates to true + * @param fieldFormatter a function that takes a type and mapping field entry and returns a formatted field representation * @return a list of issues found in fields */ @SuppressWarnings("unchecked") static List findInPropertiesRecursively(String type, Map parentMap, - Function, Boolean> predicate) { + Function, Boolean> predicate, + BiFunction, String> fieldFormatter) { List issues = new ArrayList<>(); Map properties = (Map) parentMap.get("properties"); if (properties == null) { @@ -55,7 +60,7 @@ static List findInPropertiesRecursively(String type, Map for (Map.Entry entry : properties.entrySet()) { Map valueMap = (Map) entry.getValue(); if (predicate.apply(valueMap)) { - issues.add("[type: " + type + ", field: " + entry.getKey() + "]"); + issues.add("[" + fieldFormatter.apply(type, entry) + "]"); } Map values = (Map) valueMap.get("fields"); @@ -63,21 +68,31 @@ static List findInPropertiesRecursively(String type, Map for (Map.Entry multifieldEntry : values.entrySet()) { Map multifieldValueMap = (Map) multifieldEntry.getValue(); if (predicate.apply(multifieldValueMap)) { - issues.add("[type: " + type + ", field: " + entry.getKey() + ", multifield: " + multifieldEntry.getKey() + "]"); + issues.add("[" + fieldFormatter.apply(type, entry) + ", multifield: " + multifieldEntry.getKey() + "]"); } if (multifieldValueMap.containsKey("properties")) { - issues.addAll(findInPropertiesRecursively(type, multifieldValueMap, predicate)); + issues.addAll(findInPropertiesRecursively(type, multifieldValueMap, predicate, fieldFormatter)); } } } if (valueMap.containsKey("properties")) { - issues.addAll(findInPropertiesRecursively(type, valueMap, predicate)); + issues.addAll(findInPropertiesRecursively(type, valueMap, predicate, fieldFormatter)); } } return issues; } + private static String formatDateField(String type, Map.Entry entry) { + Map value = (Map) entry.getValue(); + return "type: " + type + ", field: " + entry.getKey() +", format: "+ value.get("format") +", suggestion: " + + JodaDeprecationPatterns.formatSuggestion((String)value.get("format")); + } + + private static String formatField(String type, Map.Entry entry) { + return "type: " + type + ", field: " + entry.getKey(); + } + static DeprecationIssue oldIndicesCheck(IndexMetaData indexMetaData) { Version createdWith = indexMetaData.getCreationVersion(); if (createdWith.before(Version.V_7_0_0)) { @@ -86,7 +101,7 @@ static DeprecationIssue oldIndicesCheck(IndexMetaData indexMetaData) { "https://www.elastic.co/guide/en/elasticsearch/reference/master/" + "breaking-changes-8.0.html", "This index was created using version: " + createdWith); - } + } return null; } @@ -115,10 +130,38 @@ static DeprecationIssue tooManyFieldsCheck(IndexMetaData indexMetaData) { return null; } + static DeprecationIssue deprecatedDateTimeFormat(IndexMetaData indexMetaData) { + Version createdWith = indexMetaData.getCreationVersion(); + if (createdWith.before(Version.V_7_0_0)) { + List fields = new ArrayList<>(); + + fieldLevelMappingIssue(indexMetaData, ((mappingMetaData, sourceAsMap) -> fields.addAll( + findInPropertiesRecursively(mappingMetaData.type(), sourceAsMap, + IndexDeprecationChecks::isDateFieldWithDeprecatedPattern, + IndexDeprecationChecks::formatDateField)))); + + if (fields.size() > 0) { + return new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: " + fields + ". " + + JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + } + } + return null; + } + + private static boolean isDateFieldWithDeprecatedPattern(Map property) { + return "date".equals(property.get("type")) && + property.containsKey("format") && + JodaDeprecationPatterns.isDeprecatedPattern((String) property.get("format")); + } + static DeprecationIssue chainedMultiFieldsCheck(IndexMetaData indexMetaData) { List issues = new ArrayList<>(); fieldLevelMappingIssue(indexMetaData, ((mappingMetaData, sourceAsMap) -> issues.addAll( - findInPropertiesRecursively(mappingMetaData.type(), sourceAsMap, IndexDeprecationChecks::containsChainedMultiFields)))); + findInPropertiesRecursively(mappingMetaData.type(), sourceAsMap, + IndexDeprecationChecks::containsChainedMultiFields, IndexDeprecationChecks::formatField)))); if (issues.size() > 0) { return new DeprecationIssue(DeprecationIssue.Level.WARNING, "Multi-fields within multi-fields", diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java index a2634f0206abd..34e85a8952701 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.joda.JodaDeprecationPatterns; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.IndexSettings; @@ -25,6 +26,7 @@ import static java.util.Collections.singletonList; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.deprecation.DeprecationChecks.INDEX_SETTINGS_CHECKS; +import static org.hamcrest.Matchers.hasItem; public class IndexDeprecationChecksTests extends ESTestCase { public void testOldIndicesCheck() { @@ -156,6 +158,195 @@ public void testChainedMultiFields() throws IOException { "The names of fields that contain chained multi-fields: [[type: _doc, field: invalid-field]]"); assertEquals(singletonList(expected), issues); } + public void testDefinedPatternsDoNotWarn() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"strictWeekyearWeek\"\n" + + " }\n" + + " }" + + "}"; + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue issue = IndexDeprecationChecks.deprecatedDateTimeFormat(simpleIndex); + assertNull(issue); + } + + public void testMigratedPatterns() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"8MM-YYYY\"\n" + + " }\n" + + " }" + + "}"; + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue issue = IndexDeprecationChecks.deprecatedDateTimeFormat(simpleIndex); + assertNull(issue); + } + + public void testMultipleWarningsOnCombinedPattern() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"dd-CC||MM-YYYY\"\n" + + " }\n" + + " }" + + "}"; + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue expected = new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: ["+ + "[type: _doc, field: date_time_field_Y, format: dd-CC||MM-YYYY, " + + "suggestion: 'C' century of era is no longer supported." + + "; "+ + "'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.]"+ + "]. "+ JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex)); + assertThat(issues, hasItem(expected)); + } + + public void testDuplicateWarningsOnCombinedPattern() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"dd-YYYY||MM-YYYY\"\n" + + " }\n" + + " }" + + "}"; + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue expected = new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: ["+ + "[type: _doc, field: date_time_field_Y, format: dd-YYYY||MM-YYYY, " + + "suggestion: 'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.]"+ + "]. "+ JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex)); + assertThat(issues, hasItem(expected)); + } + + public void testWarningsOnMixCustomAndDefinedPattern() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"strictWeekyearWeek||MM-YYYY\"\n" + + " }\n" + + " }" + + "}"; + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue expected = new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: ["+ + "[type: _doc, field: date_time_field_Y, format: strictWeekyearWeek||MM-YYYY, " + + "suggestion: 'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.]"+ + "]. "+ JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex)); + assertThat(issues, hasItem(expected)); + } + + public void testJodaPatternDeprecations() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field_Y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"MM-YYYY\"\n" + + " },\n" + + " \"date_time_field_C\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"CC\"\n" + + " },\n" + + " \"date_time_field_x\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"xx-MM\"\n" + + " },\n" + + " \"date_time_field_y\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"yy-MM\"\n" + + " },\n" + + " \"date_time_field_Z\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"HH:mmZ\"\n" + + " },\n" + + " \"date_time_field_z\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"HH:mmz\"\n" + + " }\n" + + " }" + + "}"; + + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue expected = new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: ["+ + "[type: _doc, field: date_time_field_Y, format: MM-YYYY, " + + "suggestion: 'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.], "+ + "[type: _doc, field: date_time_field_C, format: CC, " + + "suggestion: 'C' century of era is no longer supported.], "+ + "[type: _doc, field: date_time_field_x, format: xx-MM, " + + "suggestion: 'x' weak-year should be replaced with 'Y'. Use 'x' for zone-offset.], "+ + "[type: _doc, field: date_time_field_y, format: yy-MM, " + + "suggestion: 'y' year should be replaced with 'u'. Use 'y' for year-of-era.], "+ + "[type: _doc, field: date_time_field_Z, format: HH:mmZ, " + + "suggestion: 'Z' time zone offset/id fails when parsing 'Z' for Zulu timezone. Consider using 'X'.], "+ + "[type: _doc, field: date_time_field_z, format: HH:mmz, " + + "suggestion: 'z' time zone text. Will print 'Z' for Zulu given UTC timezone." + + "]"+ + "]. "+ JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex)); + assertThat(issues, hasItem(expected)); + } + + public void testMultipleJodaPatternDeprecationInOneField() throws IOException { + String simpleMapping = "{\n" + + "\"properties\" : {\n" + + " \"date_time_field\" : {\n" + + " \"type\" : \"date\",\n" + + " \"format\" : \"Y-C-x-y\"\n" + + " }\n" + + " }" + + "}"; + + IndexMetaData simpleIndex = createV6Index(simpleMapping); + + DeprecationIssue expected = new DeprecationIssue(DeprecationIssue.Level.WARNING, + "Date field format uses patterns which has changed meaning in 7.0", + "https://www.elastic.co/guide/en/elasticsearch/reference/7.0/breaking-changes-7.0.html#breaking_70_java_time_changes", + "This index has date fields with deprecated formats: ["+ + "[type: _doc, field: date_time_field, format: Y-C-x-y, " + + "suggestion: 'Y' year-of-era should be replaced with 'y'. Use 'Y' for week-based-year.; " + + "'y' year should be replaced with 'u'. Use 'y' for year-of-era.; " + + "'C' century of era is no longer supported.; " + + "'x' weak-year should be replaced with 'Y'. Use 'x' for zone-offset." + + "]"+ + "]. "+ JodaDeprecationPatterns.USE_NEW_FORMAT_SPECIFIERS); + List issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex)); + assertThat(issues, hasItem(expected)); + } + + public IndexMetaData createV6Index(String simpleMapping) throws IOException { + return IndexMetaData.builder(randomAlphaOfLengthBetween(5, 10)) + .settings(settings( + VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, + VersionUtils.getPreviousVersion(Version.V_7_0_0)))) + .numberOfShards(randomIntBetween(1, 100)) + .numberOfReplicas(randomIntBetween(1, 100)) + .putMapping("_doc", simpleMapping) + .build(); + } static void addRandomFields(final int fieldLimit, XContentBuilder mappingBuilder) throws IOException {