@@ -128,9 +128,10 @@ public class CLDRConverter {
128128 static Map <String , String > pluralRules ;
129129 static Map <String , String > dayPeriodRules ;
130130
131- // TZDB Short Names Map
131+ // TZDB maps
132132 private static final Map <String , String > tzdbShortNamesMap = HashMap .newHashMap (512 );
133133 private static final Map <String , String > tzdbSubstLetters = HashMap .newHashMap (512 );
134+ private static final Map <String , String > tzdbLinks = HashMap .newHashMap (512 );
134135
135136 static enum DraftType {
136137 UNCONFIRMED ,
@@ -762,12 +763,32 @@ private static Map<String, Object> extractCurrencyNames(Map<String, Object> map,
762763
763764 private static Map <String , Object > extractZoneNames (Map <String , Object > map , String id ) {
764765 Map <String , Object > names = new TreeMap <>(KeyComparator .INSTANCE );
766+ var availableIds = getAvailableZoneIds ();
765767
766- getAvailableZoneIds (). stream () .forEach (tzid -> {
768+ availableIds .forEach (tzid -> {
767769 // If the tzid is deprecated, get the data for the replacement id
768770 String tzKey = Optional .ofNullable ((String )handlerSupplMeta .get (tzid ))
769771 .orElse (tzid );
772+ // Follow link, if needed
773+ var tzLink = tzdbLinks .get (tzKey );
774+ if (tzLink == null && tzdbLinks .containsValue (tzKey )) {
775+ // reverse link search
776+ // this is needed as in tzdb, "America/Buenos_Aires" links to
777+ // "America/Argentina/Buenos_Aires", but CLDR contains metaZone
778+ // "Argentina" only for "America/Buenos_Aires" (as of CLDR 44)
779+ // Both tzids should have "Argentina" meta zone names
780+ tzLink = tzdbLinks .entrySet ().stream ()
781+ .filter (e -> e .getValue ().equals (tzKey ))
782+ .map (Map .Entry ::getKey )
783+ .findAny ()
784+ .orElse (null );
785+
786+ }
770787 Object data = map .get (TIMEZONE_ID_PREFIX + tzKey );
788+ if (data == null && tzLink != null ) {
789+ // data for tzLink
790+ data = map .get (TIMEZONE_ID_PREFIX + tzLink );
791+ }
771792
772793 if (data instanceof String [] tznames ) {
773794 // Hack for UTC. UTC is an alias to Etc/UTC in CLDR
@@ -777,20 +798,36 @@ private static Map<String, Object> extractZoneNames(Map<String, Object> map, Str
777798 names .put ("UTC" , META_ETCUTC_ZONE_NAME );
778799 } else {
779800 // TZDB short names
801+ tznames = Arrays .copyOf (tznames , tznames .length );
780802 fillTZDBShortNames (tzid , tznames );
781803 names .put (tzid , tznames );
782804 }
783805 } else {
784806 String meta = handlerMetaZones .get (tzKey );
807+ if (meta == null && tzLink != null ) {
808+ // Check for tzLink
809+ meta = handlerMetaZones .get (tzLink );
810+ }
785811 if (meta != null ) {
786812 String metaKey = METAZONE_ID_PREFIX + meta ;
787813 data = map .get (metaKey );
788814 if (data instanceof String [] tznames ) {
789815 // TZDB short names
816+ tznames = Arrays .copyOf ((String [])names .getOrDefault (metaKey , tznames ), 6 );
790817 fillTZDBShortNames (tzid , tznames );
791818 // Keep the metazone prefix here.
792- names .put (metaKey , data );
819+ names .putIfAbsent (metaKey , tznames );
793820 names .put (tzid , meta );
821+ if (tzLink != null && availableIds .contains (tzLink )) {
822+ names .put (tzLink , meta );
823+ }
824+ }
825+ } else if (id .equals ("root" )) {
826+ // supply TZDB short names if available
827+ if (tzdbShortNamesMap .containsKey (tzid )) {
828+ var tznames = new String [6 ];
829+ fillTZDBShortNames (tzid , tznames );
830+ names .put (tzid , tznames );
794831 }
795832 }
796833 }
@@ -1263,7 +1300,7 @@ private static Map<Locale, String> coverageLevelsMap() throws Exception {
12631300 }
12641301
12651302 /*
1266- * Generates two maps from TZ database files, where they have usual abbreviation
1303+ * Generates three maps from TZ database files, where they have usual abbreviation
12671304 * of the time zone names as "FORMAT".
12681305 *
12691306 * `tzdbShortNamesMap` maps the time zone id, such as "America/Los_Angeles" to
@@ -1273,53 +1310,46 @@ private static Map<Locale, String> coverageLevelsMap() throws Exception {
12731310 *
12741311 * "America/Los_Angeles" -> "P%sT<NBSP>US"
12751312 *
1276- * The other map, `tzdbSubstLetters` maps the Rule to its substitution letters.
1313+ * The map, `tzdbSubstLetters` maps the Rule to its substitution letters.
12771314 * The key of the map is the Rule name, appended with "<NBSP>std" or "<NBSP>dst"
12781315 * depending on the savings, e.g.,
12791316 *
12801317 * "US<NBSP>std" -> "S"
12811318 * "US<NBSP>dst" -> "D"
12821319 *
1283- * These two mappings resolve the short names for time zones in each type,
1320+ * These mappings resolve the short names for time zones in each type,
12841321 * such as:
12851322 *
12861323 * Standard short name for "America/Los_Angeles" -> "PST"
12871324 * DST short name for "America/Los_Angeles" -> "PDT"
12881325 * Generic short name for "America/Los_Angeles" -> "PT"
1326+ *
1327+ * The map, `tzdbLinks` retains `Link`s of time zones. For example,
1328+ * the mapping:
1329+ *
1330+ * "US/Hawaii" -> "Pacific/Honolulu"
1331+ *
1332+ * resolves names for "US/Hawaii" correctly with "Pacific/Honolulu"
1333+ * names.
12891334 */
12901335 private static void generateTZDBShortNamesMap () throws IOException {
12911336 Files .walk (Path .of (tzDataDir ), 1 , FileVisitOption .FOLLOW_LINKS )
1292- .filter (p -> p .toFile ().isFile () && ! p . endsWith ( "jdk11_backward" ) )
1337+ .filter (p -> p .toFile ().isFile ())
12931338 .forEach (p -> {
12941339 try {
12951340 String zone = null ;
12961341 String rule = null ;
12971342 String format = null ;
12981343 boolean inVanguard = false ;
1299- boolean inRearguard = false ;
13001344 for (var line : Files .readAllLines (p )) {
1301- // Interpret the line in rearguard mode so that STD/DST
1302- // correctly handles negative DST cases, such as "GMT/IST"
1303- // vs. "IST/GMT" case for Europe/Dublin
1304- if (inVanguard ) {
1305- if (line .startsWith ("# Rearguard" )) {
1306- inVanguard = false ;
1307- inRearguard = true ;
1308- }
1309- continue ;
1310- } else if (line .startsWith ("# Vanguard" )) {
1345+ // check for Vanguard lines
1346+ if (line .startsWith ("# Vanguard section" )) {
13111347 inVanguard = true ;
13121348 continue ;
13131349 }
1314- if (inRearguard ) {
1315- if (line .startsWith ("# End of rearguard" )) {
1316- inRearguard = false ;
1317- continue ;
1318- } else {
1319- if (line .startsWith ("#\t " )) {
1320- line = line .substring (1 ); // omit #
1321- }
1322- }
1350+ if (inVanguard && line .startsWith ("# Rearguard section" )) {
1351+ inVanguard = false ;
1352+ continue ;
13231353 }
13241354 if (line .isBlank () || line .matches ("^[ \t ]*#.*" )) {
13251355 // ignore blank/comment lines
@@ -1336,7 +1366,7 @@ private static void generateTZDBShortNamesMap() throws IOException {
13361366 var zl = line .split ("[ \t ]+" , -1 );
13371367 zone = zl [1 ];
13381368 rule = zl [3 ];
1339- format = zl [4 ];
1369+ format = flipIfNeeded ( inVanguard , zl [4 ]) ;
13401370 } else {
13411371 if (zone != null ) {
13421372 if (line .startsWith ("Rule" ) ||
@@ -1348,7 +1378,7 @@ private static void generateTZDBShortNamesMap() throws IOException {
13481378 } else {
13491379 var s = line .split ("[ \t ]+" , -1 );
13501380 rule = s [2 ];
1351- format = s [3 ];
1381+ format = flipIfNeeded ( inVanguard , s [3 ]) ;
13521382 }
13531383 }
13541384 }
@@ -1359,18 +1389,42 @@ private static void generateTZDBShortNamesMap() throws IOException {
13591389 tzdbSubstLetters .put (rl [1 ] + NBSP + (rl [8 ].equals ("0" ) ? STD : DST ),
13601390 rl [9 ].replace (NO_SUBST , "" ));
13611391 }
1392+
1393+ // Link line
1394+ if (line .startsWith ("Link" )) {
1395+ var ll = line .split ("[ \t ]+" , -1 );
1396+ tzdbLinks .put (ll [2 ], ll [1 ]);
1397+ }
1398+ }
1399+
1400+ // Last entry
1401+ if (zone != null ) {
1402+ tzdbShortNamesMap .put (zone , format + NBSP + rule );
13621403 }
13631404 } catch (IOException ioe ) {
13641405 throw new UncheckedIOException (ioe );
13651406 }
13661407 });
13671408 }
13681409
1410+ // Reverse the std/dst FORMAT in Vanguard so that it
1411+ // correctly handles negative DST cases, such as "GMT/IST"
1412+ // vs. "IST/GMT" case for Europe/Dublin
1413+ private static String flipIfNeeded (boolean inVanguard , String format ) {
1414+ if (inVanguard ) {
1415+ var stddst = format .split ("/" );
1416+ if (stddst .length == 2 ) {
1417+ return stddst [1 ] + "/" + stddst [0 ];
1418+ }
1419+ }
1420+ return format ;
1421+ }
1422+
13691423 /*
13701424 * Fill the TZDB short names if there is no name provided by the CLDR
13711425 */
13721426 private static void fillTZDBShortNames (String tzid , String [] names ) {
1373- var val = tzdbShortNamesMap .get (tzid );
1427+ var val = tzdbShortNamesMap .get (tzdbLinks . getOrDefault ( tzid , tzid ) );
13741428 if (val != null ) {
13751429 var format = val .split (NBSP )[0 ];
13761430 var rule = val .split (NBSP )[1 ];
0 commit comments