@@ -128,9 +128,10 @@ public class CLDRConverter {
128
128
static Map <String , String > pluralRules ;
129
129
static Map <String , String > dayPeriodRules ;
130
130
131
- // TZDB Short Names Map
131
+ // TZDB maps
132
132
private static final Map <String , String > tzdbShortNamesMap = HashMap .newHashMap (512 );
133
133
private static final Map <String , String > tzdbSubstLetters = HashMap .newHashMap (512 );
134
+ private static final Map <String , String > tzdbLinks = HashMap .newHashMap (512 );
134
135
135
136
static enum DraftType {
136
137
UNCONFIRMED ,
@@ -762,12 +763,32 @@ private static Map<String, Object> extractCurrencyNames(Map<String, Object> map,
762
763
763
764
private static Map <String , Object > extractZoneNames (Map <String , Object > map , String id ) {
764
765
Map <String , Object > names = new TreeMap <>(KeyComparator .INSTANCE );
766
+ var availableIds = getAvailableZoneIds ();
765
767
766
- getAvailableZoneIds (). stream () .forEach (tzid -> {
768
+ availableIds .forEach (tzid -> {
767
769
// If the tzid is deprecated, get the data for the replacement id
768
770
String tzKey = Optional .ofNullable ((String )handlerSupplMeta .get (tzid ))
769
771
.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
+ }
770
787
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
+ }
771
792
772
793
if (data instanceof String [] tznames ) {
773
794
// 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
777
798
names .put ("UTC" , META_ETCUTC_ZONE_NAME );
778
799
} else {
779
800
// TZDB short names
801
+ tznames = Arrays .copyOf (tznames , tznames .length );
780
802
fillTZDBShortNames (tzid , tznames );
781
803
names .put (tzid , tznames );
782
804
}
783
805
} else {
784
806
String meta = handlerMetaZones .get (tzKey );
807
+ if (meta == null && tzLink != null ) {
808
+ // Check for tzLink
809
+ meta = handlerMetaZones .get (tzLink );
810
+ }
785
811
if (meta != null ) {
786
812
String metaKey = METAZONE_ID_PREFIX + meta ;
787
813
data = map .get (metaKey );
788
814
if (data instanceof String [] tznames ) {
789
815
// TZDB short names
816
+ tznames = Arrays .copyOf ((String [])names .getOrDefault (metaKey , tznames ), 6 );
790
817
fillTZDBShortNames (tzid , tznames );
791
818
// Keep the metazone prefix here.
792
- names .put (metaKey , data );
819
+ names .putIfAbsent (metaKey , tznames );
793
820
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 );
794
831
}
795
832
}
796
833
}
@@ -1263,7 +1300,7 @@ private static Map<Locale, String> coverageLevelsMap() throws Exception {
1263
1300
}
1264
1301
1265
1302
/*
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
1267
1304
* of the time zone names as "FORMAT".
1268
1305
*
1269
1306
* `tzdbShortNamesMap` maps the time zone id, such as "America/Los_Angeles" to
@@ -1273,53 +1310,46 @@ private static Map<Locale, String> coverageLevelsMap() throws Exception {
1273
1310
*
1274
1311
* "America/Los_Angeles" -> "P%sT<NBSP>US"
1275
1312
*
1276
- * The other map, `tzdbSubstLetters` maps the Rule to its substitution letters.
1313
+ * The map, `tzdbSubstLetters` maps the Rule to its substitution letters.
1277
1314
* The key of the map is the Rule name, appended with "<NBSP>std" or "<NBSP>dst"
1278
1315
* depending on the savings, e.g.,
1279
1316
*
1280
1317
* "US<NBSP>std" -> "S"
1281
1318
* "US<NBSP>dst" -> "D"
1282
1319
*
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,
1284
1321
* such as:
1285
1322
*
1286
1323
* Standard short name for "America/Los_Angeles" -> "PST"
1287
1324
* DST short name for "America/Los_Angeles" -> "PDT"
1288
1325
* 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.
1289
1334
*/
1290
1335
private static void generateTZDBShortNamesMap () throws IOException {
1291
1336
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 ())
1293
1338
.forEach (p -> {
1294
1339
try {
1295
1340
String zone = null ;
1296
1341
String rule = null ;
1297
1342
String format = null ;
1298
1343
boolean inVanguard = false ;
1299
- boolean inRearguard = false ;
1300
1344
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" )) {
1311
1347
inVanguard = true ;
1312
1348
continue ;
1313
1349
}
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 ;
1323
1353
}
1324
1354
if (line .isBlank () || line .matches ("^[ \t ]*#.*" )) {
1325
1355
// ignore blank/comment lines
@@ -1336,7 +1366,7 @@ private static void generateTZDBShortNamesMap() throws IOException {
1336
1366
var zl = line .split ("[ \t ]+" , -1 );
1337
1367
zone = zl [1 ];
1338
1368
rule = zl [3 ];
1339
- format = zl [4 ];
1369
+ format = flipIfNeeded ( inVanguard , zl [4 ]) ;
1340
1370
} else {
1341
1371
if (zone != null ) {
1342
1372
if (line .startsWith ("Rule" ) ||
@@ -1348,7 +1378,7 @@ private static void generateTZDBShortNamesMap() throws IOException {
1348
1378
} else {
1349
1379
var s = line .split ("[ \t ]+" , -1 );
1350
1380
rule = s [2 ];
1351
- format = s [3 ];
1381
+ format = flipIfNeeded ( inVanguard , s [3 ]) ;
1352
1382
}
1353
1383
}
1354
1384
}
@@ -1359,18 +1389,42 @@ private static void generateTZDBShortNamesMap() throws IOException {
1359
1389
tzdbSubstLetters .put (rl [1 ] + NBSP + (rl [8 ].equals ("0" ) ? STD : DST ),
1360
1390
rl [9 ].replace (NO_SUBST , "" ));
1361
1391
}
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 );
1362
1403
}
1363
1404
} catch (IOException ioe ) {
1364
1405
throw new UncheckedIOException (ioe );
1365
1406
}
1366
1407
});
1367
1408
}
1368
1409
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
+
1369
1423
/*
1370
1424
* Fill the TZDB short names if there is no name provided by the CLDR
1371
1425
*/
1372
1426
private static void fillTZDBShortNames (String tzid , String [] names ) {
1373
- var val = tzdbShortNamesMap .get (tzid );
1427
+ var val = tzdbShortNamesMap .get (tzdbLinks . getOrDefault ( tzid , tzid ) );
1374
1428
if (val != null ) {
1375
1429
var format = val .split (NBSP )[0 ];
1376
1430
var rule = val .split (NBSP )[1 ];
0 commit comments