1212import org .elasticsearch .cluster .metadata .MappingMetadata ;
1313import org .elasticsearch .common .TriFunction ;
1414import org .elasticsearch .common .time .DateFormatter ;
15+ import org .elasticsearch .common .time .DateUtils ;
1516import org .elasticsearch .common .time .LegacyFormatNames ;
1617import org .elasticsearch .core .Strings ;
1718import org .elasticsearch .index .IndexModule ;
@@ -98,6 +99,21 @@ private DeprecationIssue oldIndicesCheck(
9899 IndexVersion currentCompatibilityVersion = indexMetadata .getCompatibilityVersion ();
99100 // We intentionally exclude indices that are in data streams because they will be picked up by DataStreamDeprecationChecks
100101 if (DeprecatedIndexPredicate .reindexRequired (indexMetadata , false , false ) && isNotDataStreamIndex (indexMetadata , clusterState )) {
102+ List <String > cldrIncompatibleFieldMappings = new ArrayList <>();
103+ fieldLevelMappingIssue (
104+ indexMetadata ,
105+ (mappingMetadata , sourceAsMap ) -> cldrIncompatibleFieldMappings .addAll (
106+ findInPropertiesRecursively (
107+ mappingMetadata .type (),
108+ sourceAsMap ,
109+ this ::isDateFieldWithCompatFormatPattern ,
110+ this ::cldrIncompatibleFormatPattern ,
111+ "" ,
112+ ""
113+ )
114+ )
115+ );
116+
101117 var transforms = transformIdsForIndex (indexMetadata , indexToTransformIds );
102118 if (transforms .isEmpty () == false ) {
103119 return new DeprecationIssue (
@@ -115,6 +131,17 @@ private DeprecationIssue oldIndicesCheck(
115131 false ,
116132 Map .of ("reindex_required" , true , "transform_ids" , transforms )
117133 );
134+ } else if (cldrIncompatibleFieldMappings .isEmpty () == false ) {
135+ return new DeprecationIssue (
136+ DeprecationIssue .Level .CRITICAL ,
137+ "Field mappings with incompatible date format patterns in old index" ,
138+ "https://www.elastic.co/blog/locale-changes-elasticsearch-8-16-jdk-23" ,
139+ "The index was created before 8.0 and contains mappings that must be reindexed due to locale changes in 8.16+. "
140+ + "Manual reindexing is required. "
141+ + String .join (", " , cldrIncompatibleFieldMappings ),
142+ false ,
143+ null
144+ );
118145 } else {
119146 return new DeprecationIssue (
120147 DeprecationIssue .Level .CRITICAL ,
@@ -393,6 +420,24 @@ private DeprecationIssue deprecatedCamelCasePattern(
393420 return null ;
394421 }
395422
423+ private boolean isDateFieldWithCompatFormatPattern (Map <?, ?> property ) {
424+ if ("date" .equals (property .get ("type" )) && property .containsKey ("format" )) {
425+ String [] patterns = DateFormatter .splitCombinedPatterns ((String ) property .get ("format" ));
426+ for (String pattern : patterns ) {
427+ if (DateUtils .containsCompatOnlyDateFormat (pattern )) {
428+ return true ;
429+ }
430+ }
431+ }
432+ return false ;
433+ }
434+
435+ private String cldrIncompatibleFormatPattern (String type , Map .Entry <?, ?> entry ) {
436+ Map <?, ?> value = (Map <?, ?>) entry .getValue ();
437+ final String formatFieldValue = (String ) value .get ("format" );
438+ return "Field [" + entry .getKey () + "] with format pattern [" + formatFieldValue + "]." ;
439+ }
440+
396441 private boolean isDateFieldWithCamelCasePattern (Map <?, ?> property ) {
397442 if ("date" .equals (property .get ("type" )) && property .containsKey ("format" )) {
398443 String [] patterns = DateFormatter .splitCombinedPatterns ((String ) property .get ("format" ));
0 commit comments