1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2008-2015, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.dev.test.localespi; 10 11 import java.util.Collections; 12 import java.util.HashSet; 13 import java.util.Locale; 14 import java.util.Set; 15 import java.util.TimeZone; 16 17 import org.junit.Test; 18 19 import com.ibm.icu.dev.test.TestFmwk; 20 import com.ibm.icu.text.TimeZoneNames; 21 import com.ibm.icu.text.TimeZoneNames.NameType; 22 import com.ibm.icu.util.ULocale; 23 24 public class TimeZoneNameTest extends TestFmwk { 25 26 private static final Set<String> ProblematicZones = new HashSet<String>(); 27 static { 28 // Since tzdata2013e, Pacific/Johnston is defined as below: 29 // 30 // Link Pacific/Honolulu Pacific/Johnston 31 // 32 // JDK TimeZone.getDisplayName no longer passes Pacific/Johnston to a 33 // TimeZoneNameProvider implementation. As of CLDR 25M1, Pacific/Johnston 34 // has a different set of names from Pacific/Honolulu. This test case 35 // expects JRE calls a TimeZoneNameProvider without such normalization 36 // (and I believe it's a JDK bug). For now, we ignore the test failure 37 // caused by Pacific/Johnston with this the JDK problem. 38 ProblematicZones.add("Pacific/Johnston"); 39 } 40 41 @Test 42 public void TestTimeZoneNames() { 43 Locale[] locales = Locale.getAvailableLocales(); 44 String[] tzids = TimeZone.getAvailableIDs(); 45 46 for (Locale loc : locales) { 47 boolean warningOnly = false; 48 if (TestUtil.isExcluded(loc)) { 49 warningOnly = true; 50 } 51 52 for (String tzid : tzids) { 53 // Java has a problem when a provider does not supply all 4 names 54 // for a zone. For this reason, ICU TimeZoneName provider does not return 55 // localized names unless these 4 names are available. 56 57 String icuStdLong = getIcuDisplayName(tzid, false, TimeZone.LONG, loc); 58 String icuDstLong = getIcuDisplayName(tzid, true, TimeZone.LONG, loc); 59 String icuStdShort = getIcuDisplayName(tzid, false, TimeZone.SHORT, loc); 60 String icuDstShort = getIcuDisplayName(tzid, true, TimeZone.SHORT, loc); 61 62 if (icuStdLong != null && icuDstLong != null && icuStdShort != null && icuDstShort != null) { 63 checkDisplayNamePair(TimeZone.SHORT, tzid, loc, warningOnly || ProblematicZones.contains(tzid)); 64 checkDisplayNamePair(TimeZone.LONG, tzid, loc, warningOnly || ProblematicZones.contains(tzid)); 65 } else { 66 logln("Localized long standard name is not available for " 67 + tzid + " in locale " + loc + " in ICU"); 68 } 69 } 70 } 71 } 72 73 private void checkDisplayNamePair(int style, String tzid, Locale loc, boolean warnOnly) { 74 /* Note: There are two problems here. 75 * 76 * It looks Java 6 requires a TimeZoneNameProvider to return both standard name and daylight name 77 * for a zone. If the provider implementation only returns either of them, Java 6 also ignore 78 * the other. In ICU, there are zones which do not have daylight names, especially zones which 79 * do not use daylight time. This test case does not check a standard name if its daylight name 80 * is not available because of the Java 6 implementation problem. 81 * 82 * Another problem is that ICU always use a standard name for a zone which does not use daylight 83 * saving time even daylight name is requested. 84 */ 85 86 String icuStdName = getIcuDisplayName(tzid, false, style, loc); 87 String icuDstName = getIcuDisplayName(tzid, true, style, loc); 88 if (icuStdName != null && icuDstName != null && !icuStdName.equals(icuDstName)) { 89 checkDisplayName(false, style, tzid, loc, icuStdName, warnOnly); 90 checkDisplayName(true, style, tzid, loc, icuDstName, warnOnly); 91 } 92 } 93 94 private String getIcuDisplayName(String tzid, boolean daylight, int style, Locale loc) { 95 String icuName = null; 96 boolean[] isSystemID = new boolean[1]; 97 String canonicalID = com.ibm.icu.util.TimeZone.getCanonicalID(tzid, isSystemID); 98 if (isSystemID[0]) { 99 long date = System.currentTimeMillis(); 100 TimeZoneNames tznames = TimeZoneNames.getInstance(ULocale.forLocale(loc)); 101 switch (style) { 102 case TimeZone.LONG: 103 icuName = daylight ? 104 tznames.getDisplayName(canonicalID, NameType.LONG_DAYLIGHT, date) : 105 tznames.getDisplayName(canonicalID, NameType.LONG_STANDARD, date); 106 break; 107 case TimeZone.SHORT: 108 icuName = daylight ? 109 tznames.getDisplayName(canonicalID, NameType.SHORT_DAYLIGHT, date) : 110 tznames.getDisplayName(canonicalID, NameType.SHORT_STANDARD, date); 111 break; 112 } 113 } 114 return icuName; 115 } 116 117 private void checkDisplayName(boolean daylight, int style, String tzid, Locale loc, String icuname, boolean warnOnly) { 118 String styleStr = (style == TimeZone.SHORT) ? "SHORT" : "LONG"; 119 TimeZone tz = TimeZone.getTimeZone(tzid); 120 String name = tz.getDisplayName(daylight, style, loc); 121 122 if (TestUtil.isICUExtendedLocale(loc)) { 123 // The name should be taken from ICU 124 if (!name.equals(icuname)) { 125 if (warnOnly) { 126 logln("WARNING: TimeZone name by ICU is " + icuname + ", but got " + name 127 + " for time zone " + tz.getID() + " in locale " + loc 128 + " (daylight=" + daylight + ", style=" + styleStr + ")"); 129 130 } else { 131 errln("FAIL: TimeZone name by ICU is " + icuname + ", but got " + name 132 + " for time zone " + tz.getID() + " in locale " + loc 133 + " (daylight=" + daylight + ", style=" + styleStr + ")"); 134 } 135 } 136 } else { 137 if (!name.equals(icuname)) { 138 logln("INFO: TimeZone name by ICU is " + icuname + ", but got " + name 139 + " for time zone " + tz.getID() + " in locale " + loc 140 + " (daylight=" + daylight + ", style=" + styleStr + ")"); 141 } 142 // Try explicit ICU locale (xx_yy_ICU) 143 Locale icuLoc = TestUtil.toICUExtendedLocale(loc); 144 name = tz.getDisplayName(daylight, style, icuLoc); 145 if (!name.equals(icuname)) { 146 if (warnOnly) { 147 logln("WARNING: TimeZone name by ICU is " + icuname + ", but got " + name 148 + " for time zone " + tz.getID() + " in locale " + icuLoc 149 + " (daylight=" + daylight + ", style=" + styleStr + ")"); 150 } else { 151 errln("FAIL: TimeZone name by ICU is " + icuname + ", but got " + name 152 + " for time zone " + tz.getID() + " in locale " + icuLoc 153 + " (daylight=" + daylight + ", style=" + styleStr + ")"); 154 } 155 } 156 } 157 } 158 159 @Test 160 public void testGetInstance_Locale() { 161 TimeZoneNames uLocaleInstance = TimeZoneNames.getInstance(ULocale.CANADA); 162 TimeZoneNames localeInstance = TimeZoneNames.getInstance(Locale.CANADA); 163 164 Set<String> uLocaleAvailableIds = uLocaleInstance.getAvailableMetaZoneIDs(); 165 Set<String> localeAvailableIds = localeInstance.getAvailableMetaZoneIDs(); 166 assertEquals("Available ids", uLocaleAvailableIds, localeAvailableIds); 167 168 for (String availableId : uLocaleAvailableIds) { 169 long date = 1458385200000L; 170 TimeZoneNames.NameType nameType = TimeZoneNames.NameType.SHORT_GENERIC; 171 String uLocaleName = uLocaleInstance.getDisplayName(availableId, nameType, date); 172 String localeName = localeInstance.getDisplayName(availableId, nameType, date); 173 assertEquals("Id: " + availableId, uLocaleName, localeName); 174 } 175 } 176 177 @Test 178 public void testGetAvailableMetaZoneIDs() { 179 TimeZoneNames japaneseNames = TimeZoneNames.getInstance(ULocale.JAPANESE); 180 Set<String> allJapan = japaneseNames.getAvailableMetaZoneIDs(); 181 182 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(ULocale.CHINESE); 183 Set<String> tzdbAll = tzdbNames.getAvailableMetaZoneIDs(); 184 185 // The data is the same in the current implementation. 186 assertEquals("MetaZone IDs different between locales", allJapan, tzdbAll); 187 188 // Make sure that there is something. 189 assertTrue("count of zone ids is less than 100", allJapan.size() >= 180); 190 } 191 192 @Test 193 public void testGetAvailableMetaZoneIDs_String() { 194 TimeZoneNames japaneseNames = TimeZoneNames.getInstance(ULocale.JAPANESE); 195 assertEquals("Timezone name mismatch", Collections.singleton("America_Pacific"), 196 japaneseNames.getAvailableMetaZoneIDs("America/Los_Angeles")); 197 198 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(ULocale.CHINESE); 199 assertEquals("Timezone name mismatch", Collections.singleton("Taipei"), 200 tzdbNames.getAvailableMetaZoneIDs("Asia/Taipei")); 201 } 202 203 @Test 204 public void testGetMetaZoneDisplayName() { 205 TimeZoneNames usNames = TimeZoneNames.getInstance(ULocale.US); 206 207 String europeanCentralName = usNames.getMetaZoneDisplayName("Europe_Central", 208 TimeZoneNames.NameType.LONG_STANDARD); 209 assertEquals("Timezone name mismatch", "Central European Standard Time", 210 europeanCentralName); 211 212 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(ULocale.CHINESE); 213 String americaPacificName = tzdbNames.getMetaZoneDisplayName("America_Pacific", 214 TimeZoneNames.NameType.SHORT_DAYLIGHT); 215 assertEquals("Timezone name mismatch", "PDT", americaPacificName); 216 } 217 218 @Test 219 public void testGetMetaZoneID() { 220 TimeZoneNames usNames = TimeZoneNames.getInstance(ULocale.US); 221 222 String europeanCentralName = usNames.getMetaZoneID("Europe/Paris", 0); 223 assertEquals("Timezone name mismatch", "Europe_Central", europeanCentralName); 224 225 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(ULocale.KOREAN); 226 String seoulName = tzdbNames.getMetaZoneID("Asia/Seoul", 0); 227 assertEquals("Timezone name mismatch", "Korea", seoulName); 228 229 // Now try Jan 1st 1945 GMT 230 seoulName = tzdbNames.getMetaZoneID("Asia/Seoul", -786240000000L); 231 assertNull("Timezone name mismatch", seoulName); 232 } 233 234 @Test 235 public void testGetTimeZoneDisplayName() { 236 TimeZoneNames frenchNames = TimeZoneNames.getInstance(ULocale.FRENCH); 237 String dublinName = frenchNames.getTimeZoneDisplayName("Europe/Dublin", 238 TimeZoneNames.NameType.LONG_DAYLIGHT); 239 assertEquals("Timezone name mismatch", "heure dt irlandaise", dublinName); 240 241 String dublinLocation = frenchNames.getTimeZoneDisplayName("Europe/Dublin", 242 TimeZoneNames.NameType.EXEMPLAR_LOCATION); 243 assertEquals("Timezone name mismatch", "Dublin", dublinLocation); 244 245 // All the names returned by this are null. 246 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(ULocale.KOREAN); 247 for (String tzId : TimeZone.getAvailableIDs()) { 248 for (TimeZoneNames.NameType nameType : TimeZoneNames.NameType.values()) { 249 String name = tzdbNames.getTimeZoneDisplayName(tzId, nameType); 250 assertNull("TZ:" + tzId + ", NameType: " + nameType + ", value: " + name, name); 251 } 252 } 253 } 254 } 255