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