--- old/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java 2018-04-23 15:33:58.500207808 -0700 +++ new/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java 2018-04-23 15:33:58.187201823 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -318,16 +318,17 @@ } for (Iterator it = myMap.keySet().iterator(); it.hasNext();) { String key = it.next(); - if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX) + if (key.startsWith(CLDRConverter.TIMEZONE_ID_PREFIX) || key.startsWith(CLDRConverter.METAZONE_ID_PREFIX)) { @SuppressWarnings("unchecked") Map nameMap = (Map) myMap.get(key); + // Convert key/value pairs to an array. String[] names = new String[ZONE_NAME_KEYS.length]; int ix = 0; for (String nameKey : ZONE_NAME_KEYS) { String name = nameMap.get(nameKey); - if (name == null) { + if (name == null && parentsMap != null) { @SuppressWarnings("unchecked") Map parentNames = (Map) parentsMap.get(key); if (parentNames != null) { @@ -357,29 +358,6 @@ } } } - // If there are still any nulls, try filling in them from en data. - if (hasNulls(names) && !id.equals("en")) { - @SuppressWarnings("unchecked") - String[] enNames = (String[]) Bundle.getBundle("en").getTargetMap().get(key); - if (enNames == null) { - if (metaKey != null) { - @SuppressWarnings("unchecked") - String[] metaNames = (String[]) Bundle.getBundle("en").getTargetMap().get(metaKey); - enNames = metaNames; - } - } - if (enNames != null) { - for (int i = 0; i < names.length; i++) { - if (names[i] == null) { - names[i] = enNames[i]; - } - } - } - // If there are still nulls, give up names. - if (hasNulls(names)) { - names = null; - } - } } // replace the Map with the array if (names != null) { @@ -662,12 +640,12 @@ if (CLDRConverter.handlerMetaZones.get(tz).equals(meta)) { tzid = tz; break; - } } } + } } else { tzid = key.substring(CLDRConverter.TIMEZONE_ID_PREFIX.length()); - } + } if (tzid != null) { for (Object[] jreZone : jreTimeZoneNames) { @@ -676,13 +654,13 @@ if (map.get(ZONE_NAME_KEYS[i]) == null) { String[] jreNames = (String[])jreZone[1]; map.put(ZONE_NAME_KEYS[i], jreNames[i]); + } + } + break; } } - break; } } - } - } private void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) { switch (cldrLetter) { --- old/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java 2018-04-23 15:33:59.905234674 -0700 +++ new/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java 2018-04-23 15:33:59.566228192 -0700 @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.*; +import java.text.MessageFormat; import java.time.*; import java.util.*; import java.util.ResourceBundle.Control; @@ -82,9 +83,11 @@ static final String CALENDAR_FIRSTDAY_PREFIX = "firstDay."; static final String CALENDAR_MINDAYS_PREFIX = "minDays."; static final String TIMEZONE_ID_PREFIX = "timezone.id."; + static final String EXEMPLAR_CITY_PREFIX = "timezone.excity."; static final String ZONE_NAME_PREFIX = "timezone.displayname."; static final String METAZONE_ID_PREFIX = "metazone.id."; static final String PARENT_LOCALE_PREFIX = "parentLocale."; + static final String[] EMPTY_ZONE = {"", "", "", "", "", ""}; private static SupplementDataParseHandler handlerSuppl; private static SupplementalMetadataParseHandler handlerSupplMeta; @@ -662,23 +665,18 @@ Arrays.deepEquals(data, (String[])map.get(METAZONE_ID_PREFIX + me.getValue()))) .findAny(); - if (cldrMeta.isPresent()) { - names.put(tzid, cldrMeta.get().getValue()); - } else { + cldrMeta.ifPresentOrElse(meta -> names.put(tzid, meta.getValue()), () -> { // check the JRE meta key, add if there is not. Optional> jreMeta = jreMetaMap.entrySet().stream() .filter(jm -> Arrays.deepEquals(data, jm.getKey())) .findAny(); - if (jreMeta.isPresent()) { - names.put(tzid, jreMeta.get().getValue()); - } else { - String metaName = "JRE_" + tzid.replaceAll("[/-]", "_"); - names.put(METAZONE_ID_PREFIX + metaName, data); - names.put(tzid, metaName); - jreMetaMap.put(data, metaName); - } - } + jreMeta.ifPresentOrElse(meta -> names.put(tzid, meta.getValue()), () -> { + String metaName = "JRE_" + tzid.replaceAll("[/-]", "_"); + names.put(METAZONE_ID_PREFIX + metaName, data); + names.put(tzid, metaName); + }); + }); } }); } @@ -705,6 +703,26 @@ } }); + // exemplar cities. + Map exCities = map.entrySet().stream() + .filter(e -> e.getKey().startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX)) + .collect(Collectors + .toMap(Map.Entry::getKey, Map.Entry::getValue)); + names.putAll(exCities); + + if (!id.equals("en") && + !names.isEmpty()) { + // CLDR does not have UTC entry, so add it here. + names.put("UTC", EMPTY_ZONE); + + // no metazone zones + Arrays.asList(handlerMetaZones.get(MetaZonesParseHandler.NO_METAZONE_KEY) + .split("\\s")).stream() + .forEach(tz -> { + names.put(tz, EMPTY_ZONE); + }); + } + return names; } @@ -769,6 +787,10 @@ "field.hour", "timezone.hourFormat", "timezone.gmtFormat", + "timezone.gmtZeroFormat", + "timezone.regionFormat", + "timezone.regionFormat.daylight", + "timezone.regionFormat.standard", "field.minute", "field.second", "field.zone", --- old/make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java 2018-04-23 15:34:01.291261176 -0700 +++ new/make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java 2018-04-23 15:34:00.985255325 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,19 +103,30 @@ case "key": // for LocaleNames // copy string - pushStringEntry(qName, attributes, - CLDRConverter.LOCALE_KEY_PREFIX + - convertOldKeyName(attributes.getValue("type"))); + { + String key = convertOldKeyName(attributes.getValue("type")); + if (key.length() == 2) { + pushStringEntry(qName, attributes, + CLDRConverter.LOCALE_KEY_PREFIX + key); + } else { + pushIgnoredContainer(qName); + } + } break; case "type": // for LocaleNames/CalendarNames // copy string - pushStringEntry(qName, attributes, - CLDRConverter.LOCALE_TYPE_PREFIX + - convertOldKeyName(attributes.getValue("key")) + "." + - attributes.getValue("type")); - + { + String key = convertOldKeyName(attributes.getValue("key")); + if (key.length() == 2) { + pushStringEntry(qName, attributes, + CLDRConverter.LOCALE_TYPE_PREFIX + key + "." + + attributes.getValue("type")); + } else { + pushIgnoredContainer(qName); + } + } break; // @@ -445,6 +456,16 @@ case "gmtFormat": pushStringEntry(qName, attributes, "timezone.gmtFormat"); break; + case "gmtZeroFormat": + pushStringEntry(qName, attributes, "timezone.gmtZeroFormat"); + break; + case "regionFormat": + { + String type = attributes.getValue("type"); + pushStringEntry(qName, attributes, "timezone.regionFormat" + + (type == null ? "" : "." + type)); + } + break; case "zone": { String tzid = attributes.getValue("type"); // Olson tz id @@ -474,8 +495,8 @@ case "daylight": // daylight saving (summer) time name pushStringEntry(qName, attributes, CLDRConverter.ZONE_NAME_PREFIX + qName + "." + zoneNameStyle); break; - case "exemplarCity": // not used in JDK - pushIgnoredContainer(qName); + case "exemplarCity": + pushStringEntry(qName, attributes, CLDRConverter.EXEMPLAR_CITY_PREFIX); break; // @@ -877,11 +898,16 @@ case "generic": case "standard": case "daylight": + case "exemplarCity": if (zonePrefix != null && (currentContainer instanceof Entry)) { @SuppressWarnings("unchecked") Map valmap = (Map) get(zonePrefix + getContainerKey()); Entry entry = (Entry) currentContainer; - valmap.put(entry.getKey(), (String) entry.getValue()); + if (qName.equals("exemplarCity")) { + put(CLDRConverter.EXEMPLAR_CITY_PREFIX + getContainerKey(), (String) entry.getValue()); + } else { + valmap.put(entry.getKey(), (String) entry.getValue()); + } } break; --- old/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java 2018-04-23 15:34:02.657287296 -0700 +++ new/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java 2018-04-23 15:34:02.366281731 -0700 @@ -35,6 +35,8 @@ import org.xml.sax.SAXException; class MetaZonesParseHandler extends AbstractLDMLHandler { + final static String NO_METAZONE_KEY = "no.metazone.defined"; + private String tzid, metazone; // for java.time.format.ZoneNames.java @@ -101,10 +103,17 @@ assert qName.equals(currentContainer.getqName()) : "current=" + currentContainer.getqName() + ", param=" + qName; switch (qName) { case "timezone": - if (tzid == null || metazone == null) { + if (tzid == null) { throw new InternalError(); + } else if (metazone == null) { + String no_meta = get(NO_METAZONE_KEY); + put(NO_METAZONE_KEY, no_meta == null ? tzid : no_meta + " " + tzid); + CLDRConverter.info("No metazone defined for %s%n", tzid); + } else { + put(tzid, metazone); } - put(tzid, metazone); + tzid = null; + metazone = null; break; } currentContainer = currentContainer.getParent(); --- old/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java 2018-04-23 15:34:04.037313683 -0700 +++ new/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java 2018-04-23 15:34:03.724307698 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -211,11 +211,13 @@ if (value == null) { CLDRConverter.warning("null value for " + key); } else if (value instanceof String) { - if (type == BundleType.TIMEZONE || - ((String)value).startsWith(META_VALUE_PREFIX)) { - out.printf(" { \"%s\", %s },\n", key, CLDRConverter.saveConvert((String) value, useJava)); + String valStr = (String)value; + if (type == BundleType.TIMEZONE && + !key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || + valStr.startsWith(META_VALUE_PREFIX)) { + out.printf(" { \"%s\", %s },\n", key, CLDRConverter.saveConvert(valStr, useJava)); } else { - out.printf(" { \"%s\", \"%s\" },\n", key, CLDRConverter.saveConvert((String) value, useJava)); + out.printf(" { \"%s\", \"%s\" },\n", key, CLDRConverter.saveConvert(valStr, useJava)); } } else if (value instanceof String[]) { String[] values = (String[]) value; @@ -308,15 +310,20 @@ // end of static initializer block. - // Short TZ names for delayed initialization + // Canonical TZ names for delayed initialization if (CLDRConverter.isBaseModule) { - out.printf(" private static class TZShortIDMapHolder {\n"); - out.printf(" static final Map tzShortIDMap = new HashMap<>();\n"); + out.printf(" private static class TZCanonicalIDMapHolder {\n"); + out.printf(" static final Map tzCanonicalIDMap = new HashMap<>(600);\n"); out.printf(" static {\n"); CLDRConverter.handlerTimeZone.getData().entrySet().stream() .forEach(e -> { - out.printf(" tzShortIDMap.put(\"%s\", \"%s\");\n", e.getKey(), - ((String)e.getValue())); + String[] ids = ((String)e.getValue()).split("\\s"); + out.printf(" tzCanonicalIDMap.put(\"%s\", \"%s\");\n", e.getKey(), + ids[0]); + for (int i = 1; i < ids.length; i++) { + out.printf(" tzCanonicalIDMap.put(\"%s\", \"%s\");\n", ids[i], + ids[0]); + } }); out.printf(" }\n }\n\n"); } @@ -333,8 +340,8 @@ if (CLDRConverter.isBaseModule) { out.printf(" @Override\n" + - " public Map tzShortIDs() {\n" + - " return TZShortIDMapHolder.tzShortIDMap;\n" + + " public Map tzCanonicalIDs() {\n" + + " return TZCanonicalIDMapHolder.tzCanonicalIDMap;\n" + " }\n\n"); out.printf(" public Map parentLocales() {\n" + " return parentLocalesMap;\n" + --- old/src/java.base/share/classes/java/text/SimpleDateFormat.java 2018-04-23 15:34:05.389339535 -0700 +++ new/src/java.base/share/classes/java/text/SimpleDateFormat.java 2018-04-23 15:34:05.117334334 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ import sun.util.calendar.CalendarUtils; import sun.util.calendar.ZoneInfoFile; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.TimeZoneNameUtility; /** * SimpleDateFormat is a concrete class for formatting and @@ -1691,6 +1692,12 @@ // Checking long and short zones [1 & 2], // and long and short daylight [3 & 4]. String zoneName = zoneNames[i]; + if (zoneName.isEmpty()) { + // fill in by retrieving single name + zoneName = TimeZoneNameUtility.retrieveDisplayName( + zoneNames[0], i >= 3, i % 2, locale); + zoneNames[i] = zoneName; + } if (text.regionMatches(true, start, zoneName, 0, zoneName.length())) { return i; --- old/src/java.base/share/classes/java/util/Locale.java 2018-04-23 15:34:06.826367012 -0700 +++ new/src/java.base/share/classes/java/util/Locale.java 2018-04-23 15:34:06.556361850 -0700 @@ -2189,9 +2189,9 @@ } break; case "tz": - displayType = TimeZoneNameUtility.retrieveGenericDisplayName( - TimeZoneNameUtility.convertLDMLShortID(type).orElse(type), - TimeZone.LONG, inLocale); + displayType = TimeZoneNameUtility.convertLDMLShortID(type) + .map(id -> TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.LONG, inLocale)) + .orElse(type); break; } ret = MessageFormat.format(lr.getLocaleName("ListKeyTypePattern"), --- old/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java 2018-04-23 15:34:08.322395618 -0700 +++ new/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java 2018-04-23 15:34:07.996389384 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,6 +45,7 @@ import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.CalendarDataProvider; +import java.util.spi.TimeZoneNameProvider; import sun.util.locale.provider.JRELocaleProviderAdapter; import sun.util.locale.provider.LocaleDataMetaInfo; import sun.util.locale.provider.LocaleProviderAdapter; @@ -131,6 +132,24 @@ } @Override + public TimeZoneNameProvider getTimeZoneNameProvider() { + if (timeZoneNameProvider == null) { + TimeZoneNameProvider provider = AccessController.doPrivileged( + (PrivilegedAction) () -> + new CLDRTimeZoneNameProviderImpl( + getAdapterType(), + getLanguageTagSet("TimeZoneNames"))); + + synchronized (this) { + if (timeZoneNameProvider == null) { + timeZoneNameProvider = provider; + } + } + } + return timeZoneNameProvider; + } + + @Override public Locale[] getAvailableLocales() { Set all = createLanguageTagSet("AvailableLocales"); Locale[] locs = new Locale[all.size()]; @@ -246,9 +265,9 @@ } /** - * Returns the time zone ID from an LDML's short ID + * Returns the canonical ID for the given ID */ - public Optional getTimeZoneID(String shortID) { - return Optional.ofNullable(baseMetaInfo.tzShortIDs().get(shortID)); + public Optional canonicalTZID(String id) { + return Optional.ofNullable(baseMetaInfo.tzCanonicalIDs().get(id)); } } --- old/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java 2018-04-23 15:34:09.676421508 -0700 +++ new/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java 2018-04-23 15:34:09.342415121 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,7 +129,7 @@ private volatile CurrencyNameProvider currencyNameProvider; private volatile LocaleNameProvider localeNameProvider; - private volatile TimeZoneNameProvider timeZoneNameProvider; + protected volatile TimeZoneNameProvider timeZoneNameProvider; protected volatile CalendarDataProvider calendarDataProvider; private volatile CalendarNameProvider calendarNameProvider; --- old/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo.java 2018-04-23 15:34:10.999446805 -0700 +++ new/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo.java 2018-04-23 15:34:10.730441661 -0700 @@ -50,11 +50,12 @@ public String availableLanguageTags(String category); /** - * Returns a map for short time zone ids in BCP47 Unicode extension and - * the long time zone ids. - * @return map of short id to long ids, separated by a space. + * Returns a map for time zone ids to their canonical ids. + * The map key is either an LDML's short id, or a valid + * TZDB zone id. + * @return map of ids to their canonical ids. */ - default public Map tzShortIDs() { + default public Map tzCanonicalIDs() { return null; } } --- old/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java 2018-04-23 15:34:12.411473804 -0700 +++ new/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java 2018-04-23 15:34:12.071467303 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import sun.security.action.GetPropertyAction; import sun.util.calendar.ZoneInfo; import sun.util.resources.LocaleData; import sun.util.resources.OpenListResourceBundle; @@ -87,6 +88,9 @@ private static final String NUMBER_PATTERNS_CACHEKEY = "NP"; private static final String DATE_TIME_PATTERN = "DTP."; + // TimeZoneNamesBundle exemplar city prefix + private static final String TZNB_EXCITY_PREFIX = "timezone.excity."; + // null singleton cache value private static final Object NULLOBJECT = new Object(); @@ -254,23 +258,32 @@ return (String) localeName; } - String[] getTimeZoneNames(String key) { - String[] names = null; - String cacheKey = TIME_ZONE_NAMES + '.' + key; + public Object getTimeZoneNames(String key) { + Object val = null; + String cacheKey = TIME_ZONE_NAMES + key; removeEmptyReferences(); ResourceReference data = cache.get(cacheKey); - if (Objects.isNull(data) || Objects.isNull((names = (String[]) data.get()))) { + if (Objects.isNull(data) || Objects.isNull(val = data.get())) { TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale); if (tznb.containsKey(key)) { - names = tznb.getStringArray(key); + if (key.startsWith(TZNB_EXCITY_PREFIX)) { + val = tznb.getString(key); + assert val instanceof String; + trace("tznb: %s key: %s, val: %s\n", tznb, key, val); + } else { + String[] names = tznb.getStringArray(key); + trace("tznb: %s key: %s, names: %s, %s, %s, %s, %s, %s, %s\n", tznb, key, + names[0], names[1], names[2], names[3], names[4], names[5], names[6]); + val = names; + } cache.put(cacheKey, - new ResourceReference(cacheKey, (Object) names, referenceQueue)); + new ResourceReference(cacheKey, val, referenceQueue)); } } - return names; + return val; } @SuppressWarnings("unchecked") @@ -296,7 +309,9 @@ // Use a LinkedHashSet to preseve the order Set value = new LinkedHashSet<>(); for (String key : keyset) { - value.add(rb.getStringArray(key)); + if (!key.startsWith(TZNB_EXCITY_PREFIX)) { + value.add(rb.getStringArray(key)); + } } // Add aliases data for CLDR @@ -514,4 +529,13 @@ return cacheKey; } } + + private static final boolean TRACE_ON = Boolean.valueOf( + GetPropertyAction.privilegedGetProperty("locale.resources.debug", "false")); + + public static void trace(String format, Object... params) { + if (TRACE_ON) { + System.out.format(format, params); + } + } } --- old/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java 2018-04-23 15:34:13.928502810 -0700 +++ new/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java 2018-04-23 15:34:13.544495468 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ import java.util.Set; import java.util.TimeZone; import java.util.spi.TimeZoneNameProvider; -import sun.util.calendar.ZoneInfoFile; /** * Concrete implementation of the @@ -43,9 +42,8 @@ public class TimeZoneNameProviderImpl extends TimeZoneNameProvider { private final LocaleProviderAdapter.Type type; private final Set langtags; - private static final String CLDR_NO_INHERITANCE_MARKER = "\u2205\u2205\u2205"; - TimeZoneNameProviderImpl(LocaleProviderAdapter.Type type, Set langtags) { + protected TimeZoneNameProviderImpl(LocaleProviderAdapter.Type type, Set langtags) { this.type = type; this.langtags = langtags; } @@ -120,41 +118,23 @@ return null; } - private String[] getDisplayNameArray(String id, Locale locale) { + protected String[] getDisplayNameArray(String id, Locale locale) { Objects.requireNonNull(id); Objects.requireNonNull(locale); - String[] ret = - LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id); - - if (Objects.nonNull(ret) && type == LocaleProviderAdapter.Type.CLDR) { - // check for CLDR's "no inheritance marker" - for (int index = 0; index < ret.length; index++) { - TimeZone tz = null; - if (CLDR_NO_INHERITANCE_MARKER.equals(ret[index])) { - if (Objects.isNull(tz)) { - tz = TimeZone.getTimeZone(id); - } - int offset = tz.getRawOffset(); - if (index == 3 || index == 4) { // daylight - offset += tz.getDSTSavings(); - } - ret[index] = ZoneInfoFile.toCustomID(offset); - } - } - } - - return ret; + return (String []) LocaleProviderAdapter.forType(type) + .getLocaleResources(locale) + .getTimeZoneNames(id); } /** * Returns a String[][] as the DateFormatSymbols.getZoneStrings() value for - * the given locale. This method is package private. + * the given locale. * * @param locale a Locale for time zone names * @return an array of time zone names arrays */ - String[][] getZoneStrings(Locale locale) { + protected String[][] getZoneStrings(Locale locale) { return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getZoneStrings(); } } --- old/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java 2018-04-23 15:34:15.274528547 -0700 +++ new/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java 2018-04-23 15:34:15.006523423 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,9 +162,15 @@ * @return the tzdb's time zone ID */ public static Optional convertLDMLShortID(String shortID) { + return canonicalTZID(shortID); + } + + /** + * Returns the canonical ID for the given ID + */ + public static Optional canonicalTZID(String id) { return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR)) - .getTimeZoneID(shortID) - .map(id -> id.replaceAll("\\s.*", "")); + .canonicalTZID(id); } private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { --- old/src/java.base/share/classes/sun/util/resources/LocaleData.java 2018-04-23 15:34:16.682555469 -0700 +++ new/src/java.base/share/classes/sun/util/resources/LocaleData.java 2018-04-23 15:34:16.396550001 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -275,11 +275,6 @@ } } } - // Force fallback to Locale.ENGLISH for CLDR time zone names support - if (locale.getLanguage() != "en" - && type == CLDR && category.equals("TimeZoneNames")) { - candidates.add(candidates.size() - 1, Locale.ENGLISH); - } CANDIDATES_MAP.putIfAbsent(key, candidates); } return candidates; --- old/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java 2018-04-23 15:34:18.027581187 -0700 +++ new/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java 2018-04-23 15:34:17.742575738 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,15 +79,16 @@ */ @Override public Object handleGetObject(String key) { - String[] contents = (String[]) super.handleGetObject(key); - if (Objects.isNull(contents)) { - return null; + Object val = super.handleGetObject(key); + if (val instanceof String[]) { + String[] contents = (String[]) val; + int clen = contents.length; + String[] tmpobj = new String[7]; + tmpobj[0] = key; + System.arraycopy(contents, 0, tmpobj, 1, clen); + return tmpobj; } - int clen = contents.length; - String[] tmpobj = new String[7]; - tmpobj[0] = key; - System.arraycopy(contents, 0, tmpobj, 1, clen); - return tmpobj; + return val; } /** --- old/test/jdk/java/util/TimeZone/Bug8149452.java 2018-04-23 15:34:19.388607210 -0700 +++ new/test/jdk/java/util/TimeZone/Bug8149452.java 2018-04-23 15:34:19.096601627 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ */ /* * @test - * @bug 8149452 8151876 + * @bug 8149452 8151876 8181157 * @summary Check the missing time zone names. */ import java.text.DateFormatSymbols; @@ -34,6 +34,20 @@ public class Bug8149452 { public static void main(String[] args) { + // These zone ids are new in tzdb and yet to be reflected in + // CLDR data. Needs to be excluded from the test. + // This list is as of CLDR version 29, and should be examined + // on the CLDR data upgrade. + List NEW_ZONEIDS = List.of( + "America/Punta_Arenas", + "Asia/Atyrau", + "Asia/Barnaul", + "Asia/Famagusta", + "Asia/Tomsk", + "Europe/Astrakhan", + "Europe/Kirov", + "Europe/Saratov", + "Europe/Ulyanovsk"); List listNotFound = new ArrayList<>(); String[][] zoneStrings = DateFormatSymbols.getInstance() @@ -42,10 +56,9 @@ if (!Arrays.stream(zoneStrings) .anyMatch(zone -> tzID.equalsIgnoreCase(zone[0]))) { // to ignore names for Etc/GMT[+-][0-9]+ which are not supported - // Also ignore the TimeZone DisplayNames with GMT[+-]:hh:mm if (!tzID.startsWith("Etc/GMT") && !tzID.startsWith("GMT") - && !TimeZone.getTimeZone(tzID).getDisplayName().startsWith("GMT")) { + && !NEW_ZONEIDS.contains(tzID)) { listNotFound.add(tzID); } } --- old/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java 2018-04-23 15:34:20.728632832 -0700 +++ new/test/jdk/java/util/TimeZone/CLDRDisplayNamesTest.java 2018-04-23 15:34:20.444627402 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8005471 8008577 8129881 8130845 8136518 + * @bug 8005471 8008577 8129881 8130845 8136518 8181157 * @modules jdk.localedata * @run main/othervm -Djava.locale.providers=CLDR CLDRDisplayNamesTest * @summary Make sure that localized time zone names of CLDR are used @@ -47,27 +47,27 @@ { "ja-JP", "\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u6a19\u6e96\u6642", - "PST", + "GMT-08:00", "\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u590f\u6642\u9593", - "PDT", + "GMT-07:00", //"\u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u6642\u9593", //"PT" }, { "zh-CN", "\u5317\u7f8e\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4", - "PST", + "GMT-08:00", "\u5317\u7f8e\u592a\u5e73\u6d0b\u590f\u4ee4\u65f6\u95f4", - "PDT", + "GMT-07:00", //"\u5317\u7f8e\u592a\u5e73\u6d0b\u65f6\u95f4", //"PT", }, { "de-DE", "Nordamerikanische Westk\u00fcsten-Normalzeit", - "PST", + "GMT-08:00", "Nordamerikanische Westk\u00fcsten-Sommerzeit", - "PDT", + "GMT-07:00", //"Nordamerikanische Westk\u00fcstenzeit", //"PT", }, --- old/test/jdk/java/util/TimeZone/TimeZoneTest.java 2018-04-23 15:34:22.145659926 -0700 +++ new/test/jdk/java/util/TimeZone/TimeZoneTest.java 2018-04-23 15:34:21.849654267 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @bug 4028006 4044013 4096694 4107276 4107570 4112869 4130885 7039469 7126465 7158483 - * 8008577 8077685 8098547 8133321 8138716 8148446 8151876 8159684 8166875 + * 8008577 8077685 8098547 8133321 8138716 8148446 8151876 8159684 8166875 8181157 * @modules java.base/sun.util.resources * @library /java/text/testlib * @summary test TimeZone @@ -364,6 +364,7 @@ } else if (!name.equals("Pacific Standard Time") && !name.equals("\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4") && + !name.equals("\u5317\u7f8e\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4") && !name.equals("GMT-08:00") && !name.equals("GMT-8:00") && !name.equals("GMT-0800") && --- old/test/jdk/sun/text/resources/LocaleData.cldr 2018-04-23 15:34:23.578687326 -0700 +++ new/test/jdk/sun/text/resources/LocaleData.cldr 2018-04-23 15:34:23.252681093 -0700 @@ -5563,7 +5563,7 @@ # bug 6507067 TimeZoneNames/zh_TW/Asia\/Taipei/1=\u53f0\u5317\u6a19\u6e96\u6642\u9593 -TimeZoneNames/zh_TW/Asia\/Taipei/2=CST +TimeZoneNames/zh_TW/Asia\/Taipei/2= # bug 6645271 FormatData/hr_HR/DatePatterns/2=d. MMM y. --- old/test/jdk/sun/text/resources/LocaleDataTest.java 2018-04-23 15:34:25.109716600 -0700 +++ new/test/jdk/sun/text/resources/LocaleDataTest.java 2018-04-23 15:34:24.792710539 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ * 7114053 7074882 7040556 8008577 8013836 8021121 6192407 6931564 8027695 * 8017142 8037343 8055222 8042126 8074791 8075173 8080774 8129361 8134916 * 8145136 8145952 8164784 8037111 8081643 7037368 8178872 8185841 8190918 - * 8187946 8195478 + * 8187946 8195478 8181157 * @summary Verify locale data * @modules java.base/sun.util.resources * @modules jdk.localedata --- /dev/null 2018-04-16 11:08:08.577585051 -0700 +++ new/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java 2018-04-23 15:34:26.224737919 -0700 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.util.cldr; + +import static sun.util.locale.provider.LocaleProviderAdapter.Type; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.TimeZone; +import java.util.stream.Collectors; +import sun.util.calendar.ZoneInfoFile; +import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.LocaleResources; +import sun.util.locale.provider.TimeZoneNameProviderImpl; +import sun.util.locale.provider.TimeZoneNameUtility; + +/** + * Concrete implementation of the + * {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider} class + * for the CLDR LocaleProviderAdapter. + * + * @author Naoto Sato + */ +public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl { + + private static final String NO_INHERITANCE_MARKER = "\u2205\u2205\u2205"; + private static class AVAILABLE_IDS { + static final String[] INSTANCE = + Arrays.stream(ZoneInfoFile.getZoneIds()) + .sorted() + .toArray(String[]::new); + } + + // display name array indexes + private static final int INDEX_TZID = 0; + private static final int INDEX_STD_LONG = 1; + private static final int INDEX_STD_SHORT = 2; + private static final int INDEX_DST_LONG = 3; + private static final int INDEX_DST_SHORT = 4; + private static final int INDEX_GEN_LONG = 5; + private static final int INDEX_GEN_SHORT = 6; + + public CLDRTimeZoneNameProviderImpl(Type type, Set langtags) { + super(type, langtags); + } + + @Override + protected String[] getDisplayNameArray(String id, Locale locale) { + String tzid = TimeZoneNameUtility.canonicalTZID(id).orElse(id); + String[] namesSuper = super.getDisplayNameArray(tzid, locale); + + if (Objects.nonNull(namesSuper)) { + // CLDR's resource bundle has an translated entry for this id. + // Fix up names if needed, either missing or no-inheritance + namesSuper[INDEX_TZID] = id; + + // Check if standard long name exists. If not, try to retrieve the name + // from language only locale resources. E.g., "Europe/London" + // for en-GB only contains DST names + if (!exists(namesSuper, INDEX_STD_LONG) && !locale.getCountry().isEmpty()) { + String[] names = + getDisplayNameArray(id, Locale.forLanguageTag(locale.getLanguage())); + if (exists(names, INDEX_STD_LONG)) { + namesSuper[INDEX_STD_LONG] = names[INDEX_STD_LONG]; + } + } + + for(int i = INDEX_STD_LONG; i < namesSuper.length; i++) { // index 0 is the 'id' itself + switch (namesSuper[i]) { + case "": + // Fill in empty elements + deriveFallbackName(namesSuper, i, locale, + namesSuper[INDEX_DST_LONG].isEmpty()); + break; + case NO_INHERITANCE_MARKER: + // CLDR's "no inheritance marker" + namesSuper[i] = toGMTFormat(id, i == INDEX_DST_LONG || i == INDEX_DST_SHORT, + i % 2 != 0, locale); + break; + default: + break; + } + } + return namesSuper; + } else { + // Derive the names for this id. Validate the id first. + if (Arrays.binarySearch(AVAILABLE_IDS.INSTANCE, id) >= 0) { + String[] names = new String[INDEX_GEN_SHORT + 1]; + names[INDEX_TZID] = id; + deriveFallbackNames(names, locale); + return names; + } + } + + return null; + } + + @Override + protected String[][] getZoneStrings(Locale locale) { + // Use English for the ROOT locale + locale = locale.equals(Locale.ROOT) ? Locale.ENGLISH : locale; + String[][] ret = super.getZoneStrings(locale); + + // Fill in for the empty names. + // English names are prefilled for performance. + if (locale.getLanguage() != "en") { + for (int zoneIndex = 0; zoneIndex < ret.length; zoneIndex++) { + deriveFallbackNames(ret[zoneIndex], locale); + } + } + return ret; + } + + // Derive fallback time zone name according to LDML's logic + private void deriveFallbackNames(String[] names, Locale locale) { + for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { + deriveFallbackName(names, i, locale, false); + } + } + + private void deriveFallbackName(String[] names, int index, Locale locale, boolean noDST) { + if (exists(names, index)) { + return; + } + + // Check if COMPAT can substitute the name + if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) { + String[] compatNames = (String[])LocaleProviderAdapter.forJRE() + .getLocaleResources(locale) + .getTimeZoneNames(names[INDEX_TZID]); + if (compatNames != null) { + for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { + // Assumes COMPAT has no empty slots + if (i == index || !exists(names, i)) { + names[i] = compatNames[i]; + } + } + return; + } + } + + // Type Fallback + if (noDST && typeFallback(names, index)) { + return; + } + + // Region Fallback + if (regionFormatFallback(names, index, locale)) { + return; + } + + // last resort + String id = names[INDEX_TZID].toUpperCase(Locale.ROOT); + if (!id.startsWith("ETC/GMT") && + !id.startsWith("GMT") && + !id.startsWith("UT")) { + names[index] = toGMTFormat(names[INDEX_TZID], + index == INDEX_DST_LONG || index == INDEX_DST_SHORT, + index % 2 != 0, + locale); + } + } + + private boolean exists(String[] names, int index) { + return Objects.nonNull(names) + && Objects.nonNull(names[index]) + && !names[index].isEmpty(); + } + + private boolean typeFallback(String[] names, int index) { + // check generic + int genIndex = INDEX_GEN_SHORT - index % 2; + if (!exists(names, index) && exists(names, genIndex)) { + names[index] = names[genIndex]; + } else { + // check standard + int stdIndex = INDEX_STD_SHORT - index % 2; + if (!exists(names, index) && exists(names, stdIndex)) { + names[index] = names[stdIndex]; + } + } + + return exists(names, index); + } + + private boolean regionFormatFallback(String[] names, int index, Locale l) { + String id = names[INDEX_TZID]; + LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l); + ResourceBundle fd = lr.getJavaTimeFormatData(); + + String rgn = (String) lr.getTimeZoneNames("timezone.excity." + id); + if (rgn == null && !id.startsWith("Etc") && !id.startsWith("SystemV")) { + int slash = id.lastIndexOf('/'); + if (slash > 0) { + rgn = id.substring(slash + 1).replaceAll("_", " "); + } + } + + if (rgn != null) { + String fmt = ""; + switch (index) { + case INDEX_STD_LONG: + fmt = fd.getString("timezone.regionFormat.standard"); + break; + case INDEX_DST_LONG: + fmt = fd.getString("timezone.regionFormat.daylight"); + break; + case INDEX_GEN_LONG: + fmt = fd.getString("timezone.regionFormat"); + break; + } + if (!fmt.isEmpty()) { + names[index] = MessageFormat.format(fmt, rgn); + } + } + + return exists(names, index); + } + + private String toGMTFormat(String id, boolean daylight, boolean isShort, Locale l) { + TimeZone tz = ZoneInfoFile.getZoneInfo(id); + int offset = (tz.getRawOffset() + (daylight ? tz.getDSTSavings() : 0)) / 60000; + LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l); + ResourceBundle fd = lr.getJavaTimeFormatData(); + + if (offset == 0) { + return fd.getString("timezone.gmtZeroFormat"); + } else { + String gmtFormat = fd.getString("timezone.gmtFormat"); + String hourFormat = fd.getString("timezone.hourFormat"); + + if (offset > 0) { + hourFormat = hourFormat.substring(0, hourFormat.indexOf(";")); + } else { + hourFormat = hourFormat.substring(hourFormat.indexOf(";") + 1); + offset = -offset; + } + hourFormat = hourFormat + .replaceFirst("H+", (isShort ? "\\%1\\$d" : "\\%1\\$02d")) + .replaceFirst("m+", "\\%2\\$02d"); + return MessageFormat.format(gmtFormat, + String.format(hourFormat, offset / 60, offset % 60)); + } + } +} --- /dev/null 2018-04-16 11:08:08.577585051 -0700 +++ new/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java 2018-04-23 15:34:27.681765778 -0700 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8181157 + * @modules jdk.localedata + * @summary Checks CLDR time zone names are generated correctly at runtime + * @run testng/othervm -Djava.locale.providers=CLDR TimeZoneNamesTest + */ + +import static org.testng.Assert.assertEquals; + +import java.time.ZoneId; +import java.time.format.TextStyle; +import java.util.Locale; +import java.util.TimeZone; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +@Test +public class TimeZoneNamesTest { + + @DataProvider(name="noResourceTZs") + Object[][] data() { + return new Object[][] { + // tzid, locale, style, expected + + // These zone ids are new in tzdb and yet to be reflected in + // CLDR data. Thus it's assured there is no l10n names for these. + // This list is as of CLDR version 29, and should be examined + // on the CLDR data upgrade. + {"America/Punta_Arenas", Locale.US, "Punta Arenas Standard Time", + "GMT-03:00", + "Punta Arenas Daylight Time", + "GMT-03:00", + "Punta Arenas Time", + "GMT-03:00"}, + {"America/Punta_Arenas", Locale.FRANCE, "Punta Arenas (heure standard)", + "UTC\u221203:00", + "Punta Arenas (heure d\u2019\u00e9t\u00e9)", + "UTC\u221203:00", + "heure : Punta Arenas", + "UTC\u221203:00"}, + {"Asia/Atyrau", Locale.US, "Atyrau Standard Time", + "GMT+05:00", + "Atyrau Daylight Time", + "GMT+05:00", + "Atyrau Time", + "GMT+05:00"}, + {"Asia/Atyrau", Locale.FRANCE, "Atyrau (heure standard)", + "UTC+05:00", + "Atyrau (heure d\u2019\u00e9t\u00e9)", + "UTC+05:00", + "heure : Atyrau", + "UTC+05:00"}, + + // no "metazone" zones + {"Asia/Srednekolymsk", Locale.US, "Srednekolymsk Time", + "SRET", + "Srednekolymsk Daylight Time", + "SREDT", + "Srednekolymsk Time", + "SRET"}, + {"Asia/Srednekolymsk", Locale.FRANCE, "Srednekolymsk (heure standard)", + "UTC+11:00", + "Srednekolymsk (heure standard)", + "UTC+11:00", + "heure : Srednekolymsk", + "UTC+11:00"}, + {"Pacific/Bougainville", Locale.US, "Bougainville Standard Time", + "BST", + "Bougainville Daylight Time", + "BST", + "Bougainville Time", + "BT"}, + {"Pacific/Bougainville", Locale.FRANCE, "Bougainville (heure standard)", + "UTC+11:00", + "Bougainville (heure standard)", + "UTC+11:00", + "heure : Bougainville", + "UTC+11:00"}, + + }; + } + + + @Test(dataProvider="noResourceTZs") + public void test_tzNames(String tzid, Locale locale, String lstd, String sstd, String ldst, String sdst, String lgen, String sgen) { + // Standard time + assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.LONG, locale), lstd); + assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.SHORT, locale), sstd); + + // daylight saving time + assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.LONG, locale), ldst); + assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.SHORT, locale), sdst); + + // generic name + assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.FULL, locale), lgen); + assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.SHORT, locale), sgen); + } +}