1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************** 6 * Copyright (C) 2007-2016, Google, International Business Machines Corporation 7 * and others. All Rights Reserved. 8 ******************************************************************************** 9 */ 10 11 package ohos.global.icu.dev.test.format; 12 13 import java.text.FieldPosition; 14 import java.text.ParseException; 15 import java.text.ParsePosition; 16 import java.util.ArrayList; 17 import java.util.Arrays; 18 import java.util.Collections; 19 import java.util.Date; 20 import java.util.EnumSet; 21 import java.util.List; 22 import java.util.Locale; 23 import java.util.Random; 24 import java.util.Set; 25 import java.util.TreeSet; 26 import java.util.concurrent.atomic.AtomicInteger; 27 import java.util.regex.Pattern; 28 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 import org.junit.runners.JUnit4; 32 33 import ohos.global.icu.dev.test.TestFmwk; 34 import ohos.global.icu.impl.TZDBTimeZoneNames; 35 import ohos.global.icu.impl.ZoneMeta; 36 import ohos.global.icu.lang.UCharacter; 37 import ohos.global.icu.text.DateFormat; 38 import ohos.global.icu.text.SimpleDateFormat; 39 import ohos.global.icu.text.TimeZoneFormat; 40 import ohos.global.icu.text.TimeZoneFormat.GMTOffsetPatternType; 41 import ohos.global.icu.text.TimeZoneFormat.ParseOption; 42 import ohos.global.icu.text.TimeZoneFormat.Style; 43 import ohos.global.icu.text.TimeZoneFormat.TimeType; 44 import ohos.global.icu.text.TimeZoneNames; 45 import ohos.global.icu.text.TimeZoneNames.Factory; 46 import ohos.global.icu.text.TimeZoneNames.NameType; 47 import ohos.global.icu.util.BasicTimeZone; 48 import ohos.global.icu.util.Calendar; 49 import ohos.global.icu.util.Output; 50 import ohos.global.icu.util.SimpleTimeZone; 51 import ohos.global.icu.util.TimeZone; 52 import ohos.global.icu.util.TimeZone.SystemTimeZoneType; 53 import ohos.global.icu.util.TimeZoneTransition; 54 import ohos.global.icu.util.ULocale; 55 56 57 58 @RunWith(JUnit4.class) 59 public class TimeZoneFormatTest extends TestFmwk { 60 61 private static boolean JDKTZ = (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_JDK); 62 private static final Pattern EXCL_TZ_PATTERN = Pattern.compile(".*/Riyadh8[7-9]"); 63 64 private static final String[] PATTERNS = { 65 "z", 66 "zzzz", 67 "Z", // equivalent to "xxxx" 68 "ZZZZ", // equivalent to "OOOO" 69 "v", 70 "vvvv", 71 "O", 72 "OOOO", 73 "X", 74 "XX", 75 "XXX", 76 "XXXX", 77 "XXXXX", 78 "x", 79 "xx", 80 "xxx", 81 "xxxx", 82 "xxxxx", 83 "V", 84 "VV", 85 "VVV", 86 "VVVV" 87 }; 88 boolean REALLY_VERBOSE_LOG = false; 89 90 /* 91 * Test case for checking if a TimeZone is properly set in the result calendar 92 * and if the result TimeZone has the expected behavior. 93 */ 94 @Test TestTimeZoneRoundTrip()95 public void TestTimeZoneRoundTrip() { 96 boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false); 97 98 TimeZone unknownZone = new SimpleTimeZone(-31415, "Etc/Unknown"); 99 int badDstOffset = -1234; 100 int badZoneOffset = -2345; 101 102 int[][] testDateData = { 103 {2007, 1, 15}, 104 {2007, 6, 15}, 105 {1990, 1, 15}, 106 {1990, 6, 15}, 107 {1960, 1, 15}, 108 {1960, 6, 15}, 109 }; 110 111 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 112 cal.clear(); 113 114 // Set up rule equivalency test range 115 long low, high; 116 cal.set(1900, 0, 1); 117 low = cal.getTimeInMillis(); 118 cal.set(2040, 0, 1); 119 high = cal.getTimeInMillis(); 120 121 // Set up test dates 122 Date[] DATES = new Date[testDateData.length]; 123 cal.clear(); 124 for (int i = 0; i < DATES.length; i++) { 125 cal.set(testDateData[i][0], testDateData[i][1], testDateData[i][2]); 126 DATES[i] = cal.getTime(); 127 } 128 129 // Set up test locales 130 ULocale[] LOCALES = null; 131 if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) { 132 LOCALES = ULocale.getAvailableLocales(); 133 } else { 134 LOCALES = new ULocale[] {new ULocale("en"), new ULocale("en_CA"), new ULocale("fr"), 135 new ULocale("zh_Hant"), new ULocale("fa"), new ULocale("ccp")}; 136 } 137 138 String[] tzids; 139 if (JDKTZ) { 140 tzids = java.util.TimeZone.getAvailableIDs(); 141 } else { 142 tzids = TimeZone.getAvailableIDs(); 143 } 144 int[] inOffsets = new int[2]; 145 int[] outOffsets = new int[2]; 146 147 // Run the roundtrip test 148 for (int locidx = 0; locidx < LOCALES.length; locidx++) { 149 logln("Locale: " + LOCALES[locidx].toString()); 150 151 String localGMTString = TimeZoneFormat.getInstance(LOCALES[locidx]).formatOffsetLocalizedGMT(0); 152 153 for (int patidx = 0; patidx < PATTERNS.length; patidx++) { 154 logln(" pattern: " + PATTERNS[patidx]); 155 SimpleDateFormat sdf = new SimpleDateFormat(PATTERNS[patidx], LOCALES[locidx]); 156 157 for (int tzidx = 0; tzidx < tzids.length; tzidx++) { 158 if (EXCL_TZ_PATTERN.matcher(tzids[tzidx]).matches()) { 159 continue; 160 } 161 TimeZone tz = TimeZone.getTimeZone(tzids[tzidx]); 162 163 for (int datidx = 0; datidx < DATES.length; datidx++) { 164 // Format 165 sdf.setTimeZone(tz); 166 String tzstr = sdf.format(DATES[datidx]); 167 168 // Before parse, set unknown zone to SimpleDateFormat instance 169 // just for making sure that it does not depends on the time zone 170 // originally set. 171 sdf.setTimeZone(unknownZone); 172 173 // Parse 174 ParsePosition pos = new ParsePosition(0); 175 Calendar outcal = Calendar.getInstance(unknownZone); 176 outcal.set(Calendar.DST_OFFSET, badDstOffset); 177 outcal.set(Calendar.ZONE_OFFSET, badZoneOffset); 178 179 sdf.parse(tzstr, outcal, pos); 180 181 // Check the result 182 TimeZone outtz = outcal.getTimeZone(); 183 184 tz.getOffset(DATES[datidx].getTime(), false, inOffsets); 185 outtz.getOffset(DATES[datidx].getTime(), false, outOffsets); 186 187 if (PATTERNS[patidx].equals("V")) { 188 // Short zone ID - should support roundtrip for canonical CLDR IDs 189 String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]); 190 if (!outtz.getID().equals(canonicalID)) { 191 if (outtz.getID().equals("Etc/Unknown")) { 192 // Note that some zones like Asia/Riyadh87 does not have 193 // short zone ID and "unk" is used as the fallback 194 if (REALLY_VERBOSE_LOG) { 195 logln("Canonical round trip failed (probably as expected); tz=" + tzids[tzidx] 196 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 197 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 198 + ", outtz=" + outtz.getID()); 199 } 200 } else { 201 errln("Canonical round trip failed; tz=" + tzids[tzidx] 202 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 203 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 204 + ", outtz=" + outtz.getID()); 205 } 206 } 207 } else if (PATTERNS[patidx].equals("VV")) { 208 // Zone ID - full roundtrip support 209 if (!outtz.getID().equals(tzids[tzidx])) { 210 errln("Zone ID round trip failed; tz=" + tzids[tzidx] 211 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 212 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 213 + ", outtz=" + outtz.getID()); 214 } 215 } else if (PATTERNS[patidx].equals("VVV") || PATTERNS[patidx].equals("VVVV")) { 216 // Location: time zone rule must be preserved except 217 // zones not actually associated with a specific location. 218 String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]); 219 if (canonicalID != null && !outtz.getID().equals(canonicalID)) { 220 // Canonical ID did not match - check the rules 221 boolean bFailure = false; 222 if ((tz instanceof BasicTimeZone) && (outtz instanceof BasicTimeZone)) { 223 boolean hasNoLocation = TimeZone.getRegion(tzids[tzidx]).equals("001"); 224 bFailure = !hasNoLocation 225 && !((BasicTimeZone)outtz).hasEquivalentTransitions(tz, low, high); 226 } 227 if (bFailure) { 228 errln("Canonical round trip failed; tz=" + tzids[tzidx] 229 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 230 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 231 + ", outtz=" + outtz.getID()); 232 } else if (REALLY_VERBOSE_LOG) { 233 logln("Canonical round trip failed (as expected); tz=" + tzids[tzidx] 234 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 235 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 236 + ", outtz=" + outtz.getID()); 237 } 238 } 239 } else { 240 boolean isOffsetFormat = (PATTERNS[patidx].charAt(0) == 'Z' 241 || PATTERNS[patidx].charAt(0) == 'O' 242 || PATTERNS[patidx].charAt(0) == 'X' 243 || PATTERNS[patidx].charAt(0) == 'x'); 244 boolean minutesOffset = false; 245 if (PATTERNS[patidx].charAt(0) == 'X' || PATTERNS[patidx].charAt(0) == 'x') { 246 minutesOffset = PATTERNS[patidx].length() <= 3; 247 } 248 249 if (!isOffsetFormat) { 250 // Check if localized GMT format is used as a fallback of name styles 251 int numDigits = 0; 252 int idx = 0; 253 while (idx < tzstr.length()) { 254 int cp = tzstr.codePointAt(idx); 255 if (UCharacter.isDigit(cp)) { 256 numDigits++; 257 } 258 idx += UCharacter.charCount(cp); 259 } 260 isOffsetFormat = (numDigits > 0); 261 } 262 263 if (isOffsetFormat || tzstr.equals(localGMTString)) { 264 // Localized GMT or ISO: total offset (raw + dst) must be preserved. 265 int inOffset = inOffsets[0] + inOffsets[1]; 266 int outOffset = outOffsets[0] + outOffsets[1]; 267 int diff = outOffset - inOffset; 268 if (minutesOffset) { 269 diff = (diff / 60000) * 60000; 270 } 271 if (diff != 0) { 272 errln("Offset round trip failed; tz=" + tzids[tzidx] 273 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 274 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 275 + ", inOffset=" + inOffset + ", outOffset=" + outOffset); 276 } 277 } else { 278 // Specific or generic: raw offset must be preserved. 279 if (inOffsets[0] != outOffsets[0]) { 280 if (JDKTZ && tzids[tzidx].startsWith("SystemV/")) { 281 // JDK uses rule SystemV for these zones while 282 // ICU handles these zones as aliases of existing time zones 283 if (REALLY_VERBOSE_LOG) { 284 logln("Raw offset round trip failed; tz=" + tzids[tzidx] 285 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 286 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 287 + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]); 288 } 289 290 } else { 291 errln("Raw offset round trip failed; tz=" + tzids[tzidx] 292 + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] 293 + ", time=" + DATES[datidx].getTime() + ", str=" + tzstr 294 + ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]); 295 } 296 } 297 } 298 } 299 } 300 } 301 } 302 } 303 304 } 305 306 /* 307 * Test case of round trip time and text. This test case detects every canonical TimeZone's 308 * rule transition since 1900 until 2020, then check if time around each transition can 309 * round trip as expected. 310 */ 311 @Test TestTimeRoundTrip()312 public void TestTimeRoundTrip() { 313 314 boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false); 315 316 int startYear, endYear; 317 318 if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) { 319 startYear = 1900; 320 } else { 321 startYear = 1990; 322 } 323 324 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 325 endYear = cal.get(Calendar.YEAR) + 3; 326 327 cal.set(startYear, Calendar.JANUARY, 1); 328 final long START_TIME = cal.getTimeInMillis(); 329 330 cal.set(endYear, Calendar.JANUARY, 1); 331 final long END_TIME = cal.getTimeInMillis(); 332 333 // These patterns are ambiguous at DST->STD local time overlap 334 List<String> AMBIGUOUS_DST_DECESSION = Arrays.asList("v", "vvvv", "V", "VV", "VVV", "VVVV"); 335 336 // These patterns are ambiguous at STD->STD/DST->DST local time overlap 337 List<String> AMBIGUOUS_NEGATIVE_SHIFT = Arrays.asList("z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV"); 338 339 // These patterns only support integer minutes offset 340 List<String> MINUTES_OFFSET = Arrays.asList("X", "XX", "XXX", "x", "xx", "xxx"); 341 342 // Regex pattern used for filtering zone IDs without exemplar location 343 final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]"); 344 345 final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS"; 346 347 ULocale[] LOCALES = null; 348 349 // timer for performance analysis 350 long[] times = new long[PATTERNS.length]; 351 long timer; 352 353 if (TEST_ALL) { 354 // It may take about an hour for testing all locales 355 LOCALES = ULocale.getAvailableLocales(); 356 } else if (TestFmwk.getExhaustiveness() > 5) { 357 LOCALES = new ULocale[] { 358 new ULocale("ar_EG"), new ULocale("bg_BG"), new ULocale("ca_ES"), new ULocale("da_DK"), new ULocale("de"), 359 new ULocale("de_DE"), new ULocale("el_GR"), new ULocale("en"), new ULocale("en_AU"), new ULocale("en_CA"), 360 new ULocale("en_US"), new ULocale("es"), new ULocale("es_ES"), new ULocale("es_MX"), new ULocale("fi_FI"), 361 new ULocale("fr"), new ULocale("fr_CA"), new ULocale("fr_FR"), new ULocale("he_IL"), new ULocale("hu_HU"), 362 new ULocale("it"), new ULocale("it_IT"), new ULocale("ja"), new ULocale("ja_JP"), new ULocale("ko"), 363 new ULocale("ko_KR"), new ULocale("nb_NO"), new ULocale("nl_NL"), new ULocale("nn_NO"), new ULocale("pl_PL"), 364 new ULocale("pt"), new ULocale("pt_BR"), new ULocale("pt_PT"), new ULocale("ru_RU"), new ULocale("sv_SE"), 365 new ULocale("th_TH"), new ULocale("tr_TR"), new ULocale("zh"), new ULocale("zh_Hans"), new ULocale("zh_Hans_CN"), 366 new ULocale("zh_Hant"), new ULocale("zh_Hant_HK"), new ULocale("zh_Hant_TW"), new ULocale("ccp"), new ULocale("fa") 367 }; 368 } else { 369 LOCALES = new ULocale[] { 370 new ULocale("en"), 371 }; 372 } 373 374 SimpleDateFormat sdfGMT = new SimpleDateFormat(BASEPATTERN); 375 sdfGMT.setTimeZone(TimeZone.getTimeZone("Etc/GMT")); 376 377 long testCounts = 0; 378 long[] testTimes = new long[4]; 379 boolean[] expectedRoundTrip = new boolean[4]; 380 int testLen = 0; 381 for (int locidx = 0; locidx < LOCALES.length; locidx++) { 382 logln("Locale: " + LOCALES[locidx].toString()); 383 for (int patidx = 0; patidx < PATTERNS.length; patidx++) { 384 logln(" pattern: " + PATTERNS[patidx]); 385 String pattern = BASEPATTERN + " " + PATTERNS[patidx]; 386 SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]); 387 boolean minutesOffset = MINUTES_OFFSET.contains(PATTERNS[patidx]); 388 389 Set<String> ids = null; 390 if (JDKTZ) { 391 ids = new TreeSet<String>(); 392 String[] jdkIDs = java.util.TimeZone.getAvailableIDs(); 393 for (String jdkID : jdkIDs) { 394 if (EXCL_TZ_PATTERN.matcher(jdkID).matches()) { 395 continue; 396 } 397 String tmpID = TimeZone.getCanonicalID(jdkID); 398 if (tmpID != null) { 399 ids.add(tmpID); 400 } 401 } 402 } else { 403 ids = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null); 404 } 405 406 for (String id : ids) { 407 if (PATTERNS[patidx].equals("V")) { 408 // Some zones do not have short ID assigned, such as Asia/Riyadh87. 409 // The time roundtrip will fail for such zones with pattern "V" (short zone ID). 410 // This is expected behavior. 411 String shortZoneID = ZoneMeta.getShortID(id); 412 if (shortZoneID == null) { 413 continue; 414 } 415 } else if (PATTERNS[patidx].equals("VVV")) { 416 // Some zones are not associated with any region, such as Etc/GMT+8. 417 // The time roundtrip will fail for such zones with pattern "VVV" (exemplar location). 418 // This is expected behavior. 419 if (id.indexOf('/') < 0 || LOC_EXCLUSION_PATTERN.matcher(id).matches()) { 420 continue; 421 } 422 } 423 424 if ((id.equals("Pacific/Apia") || id.equals("Pacific/Midway") || id.equals("Pacific/Pago_Pago")) 425 && PATTERNS[patidx].equals("vvvv") 426 && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) { 427 continue; 428 } 429 430 BasicTimeZone btz = (BasicTimeZone)TimeZone.getTimeZone(id, TimeZone.TIMEZONE_ICU); 431 TimeZone tz = TimeZone.getTimeZone(id); 432 sdf.setTimeZone(tz); 433 434 long t = START_TIME; 435 TimeZoneTransition tzt = null; 436 boolean middle = true; 437 boolean last = false; 438 while (t < END_TIME) { 439 if (tzt == null) { 440 testTimes[0] = t; 441 expectedRoundTrip[0] = true; 442 testLen = 1; 443 } else { 444 int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings(); 445 int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings(); 446 int delta = toOffset - fromOffset; 447 if (delta < 0) { 448 boolean isDstDecession = tzt.getFrom().getDSTSavings() > 0 && tzt.getTo().getDSTSavings() == 0; 449 testTimes[0] = t + delta - 1; 450 expectedRoundTrip[0] = true; 451 testTimes[1] = t + delta; 452 expectedRoundTrip[1] = isDstDecession ? 453 !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : 454 !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]); 455 testTimes[2] = t - 1; 456 expectedRoundTrip[2] = isDstDecession ? 457 !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : 458 !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]); 459 testTimes[3] = t; 460 expectedRoundTrip[3] = true; 461 testLen = 4; 462 } else { 463 testTimes[0] = t - 1; 464 expectedRoundTrip[0] = true; 465 testTimes[1] = t; 466 expectedRoundTrip[1] = true; 467 testLen = 2; 468 } 469 } 470 for (int testidx = 0; testidx < testLen; testidx++) { 471 testCounts++; 472 timer = System.currentTimeMillis(); 473 String text = sdf.format(new Date(testTimes[testidx])); 474 try { 475 Date parsedDate = sdf.parse(text); 476 long restime = parsedDate.getTime(); 477 long timeDiff = restime - testTimes[testidx]; 478 boolean bTimeMatch = minutesOffset ? 479 (timeDiff/60000)*60000 == 0 : timeDiff == 0; 480 if (!bTimeMatch) { 481 StringBuffer msg = new StringBuffer(); 482 msg.append("Time round trip failed for ") 483 .append("tzid=").append(id) 484 .append(", locale=").append(LOCALES[locidx]) 485 .append(", pattern=").append(PATTERNS[patidx]) 486 .append(", text=").append(text) 487 .append(", gmt=").append(sdfGMT.format(new Date(testTimes[testidx]))) 488 .append(", time=").append(testTimes[testidx]) 489 .append(", restime=").append(restime) 490 .append(", diff=").append(timeDiff); 491 if (expectedRoundTrip[testidx] 492 && !isSpecialTimeRoundTripCase(LOCALES[locidx], id, PATTERNS[patidx], testTimes[testidx])) { 493 errln("FAIL: " + msg.toString()); 494 } else if (REALLY_VERBOSE_LOG) { 495 logln(msg.toString()); 496 } 497 } 498 } catch (ParseException pe) { 499 errln("FAIL: " + pe.getMessage() + " tzid=" + id + ", locale=" + LOCALES[locidx] + 500 ", pattern=" + PATTERNS[patidx] + ", text=" + text); 501 } 502 times[patidx] += System.currentTimeMillis() - timer; 503 } 504 505 if (last) { 506 break; 507 } 508 509 tzt = btz.getNextTransition(t, false); 510 if (tzt == null) { 511 last = true; 512 t = END_TIME - 1; 513 } else if (middle) { 514 // Test the date in the middle of two transitions. 515 t += (tzt.getTime() - t)/2; 516 middle = false; 517 tzt = null; 518 } else { 519 t = tzt.getTime(); 520 } 521 } 522 } 523 } 524 } 525 526 long total = 0; 527 logln("### Elapsed time by patterns ###"); 528 for (int i = 0; i < PATTERNS.length; i++) { 529 logln(times[i] + "ms (" + PATTERNS[i] + ")"); 530 total += times[i]; 531 } 532 logln("Total: " + total + "ms"); 533 logln("Iteration: " + testCounts); 534 } 535 536 // Special exclusions in TestTimeZoneRoundTrip. 537 // These special cases do not round trip time as designed. isSpecialTimeRoundTripCase(ULocale loc, String id, String pattern, long time)538 private boolean isSpecialTimeRoundTripCase(ULocale loc, String id, String pattern, long time) { 539 final Object[][] EXCLUSIONS = { 540 {null, "Asia/Chita", "zzzz", Long.valueOf(1414252800000L)}, 541 {null, "Asia/Chita", "vvvv", Long.valueOf(1414252800000L)}, 542 {null, "Asia/Srednekolymsk", "zzzz", Long.valueOf(1414241999999L)}, 543 {null, "Asia/Srednekolymsk", "vvvv", Long.valueOf(1414241999999L)}, 544 }; 545 boolean isExcluded = false; 546 for (Object[] excl : EXCLUSIONS) { 547 if (excl[0] == null || loc.equals(excl[0])) { 548 if (id.equals(excl[1])) { 549 if (excl[2] == null || pattern.equals(excl[2])) { 550 if (excl[3] == null || ((Long)excl[3]).compareTo(time) == 0) { 551 isExcluded = true; 552 break; 553 } 554 } 555 } 556 } 557 } 558 return isExcluded; 559 } 560 561 @Test TestParse()562 public void TestParse() { 563 final Object[][] DATA = { 564 // text inpos locale style 565 // parseOptions expected outpos time type 566 {"Z", 0, "en_US", Style.ISO_EXTENDED_FULL, 567 null, "Etc/GMT", 1, TimeType.UNKNOWN}, 568 569 {"Z", 0, "en_US", Style.SPECIFIC_LONG, 570 null, "Etc/GMT", 1, TimeType.UNKNOWN}, 571 572 {"Zambia time", 0, "en_US", Style.ISO_EXTENDED_FULL, 573 EnumSet.of(ParseOption.ALL_STYLES), "Etc/GMT", 1, TimeType.UNKNOWN}, 574 575 {"Zambia time", 0, "en_US", Style.GENERIC_LOCATION, 576 null, "Africa/Lusaka", 11, TimeType.UNKNOWN}, 577 578 {"Zambia time", 0, "en_US", Style.ISO_BASIC_LOCAL_FULL, 579 EnumSet.of(ParseOption.ALL_STYLES), "Africa/Lusaka", 11, TimeType.UNKNOWN}, 580 581 {"+00:00", 0, "en_US", Style.ISO_EXTENDED_FULL, 582 null, "Etc/GMT", 6, TimeType.UNKNOWN}, 583 584 {"-01:30:45", 0, "en_US", Style.ISO_EXTENDED_FULL, 585 null, "GMT-01:30:45", 9, TimeType.UNKNOWN}, 586 587 {"-7", 0, "en_US", Style.ISO_BASIC_LOCAL_FULL, 588 null, "GMT-07:00", 2, TimeType.UNKNOWN}, 589 590 {"-2222", 0, "en_US", Style.ISO_BASIC_LOCAL_FULL, 591 null, "GMT-22:22", 5, TimeType.UNKNOWN}, 592 593 {"-3333", 0, "en_US", Style.ISO_BASIC_LOCAL_FULL, 594 null, "GMT-03:33", 4, TimeType.UNKNOWN}, 595 596 {"XXX+01:30YYY", 3, "en_US", Style.LOCALIZED_GMT, 597 null, "GMT+01:30", 9, TimeType.UNKNOWN}, 598 599 {"GMT0", 0, "en_US", Style.SPECIFIC_SHORT, 600 null, "Etc/GMT", 3, TimeType.UNKNOWN}, 601 602 {"EST", 0, "en_US", Style.SPECIFIC_SHORT, 603 null, "America/New_York", 3, TimeType.STANDARD}, 604 605 {"ESTx", 0, "en_US", Style.SPECIFIC_SHORT, 606 null, "America/New_York", 3, TimeType.STANDARD}, 607 608 {"EDTx", 0, "en_US", Style.SPECIFIC_SHORT, 609 null, "America/New_York", 3, TimeType.DAYLIGHT}, 610 611 {"EST", 0, "en_US", Style.SPECIFIC_LONG, 612 null, null, 0, TimeType.UNKNOWN}, 613 614 {"EST", 0, "en_US", Style.SPECIFIC_LONG, 615 EnumSet.of(ParseOption.ALL_STYLES), "America/New_York", 3, TimeType.STANDARD}, 616 617 {"EST", 0, "en_CA", Style.SPECIFIC_SHORT, 618 null, "America/Toronto", 3, TimeType.STANDARD}, 619 620 {"CST", 0, "en_US", Style.SPECIFIC_SHORT, 621 null, "America/Chicago", 3, TimeType.STANDARD}, 622 623 {"CST", 0, "en_GB", Style.SPECIFIC_SHORT, 624 null, null, 0, TimeType.UNKNOWN}, 625 626 {"CST", 0, "en_GB", Style.SPECIFIC_SHORT, 627 EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS), "America/Chicago", 3, TimeType.STANDARD}, 628 629 {"--CST--", 2, "en_GB", Style.SPECIFIC_SHORT, 630 EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS), "America/Chicago", 5, TimeType.STANDARD}, 631 632 {"CST", 0, "zh_CN", Style.SPECIFIC_SHORT, 633 EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS), "Asia/Shanghai", 3, TimeType.STANDARD}, 634 635 {"AEST", 0, "en_AU", Style.SPECIFIC_SHORT, 636 EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS), "Australia/Sydney", 4, TimeType.STANDARD}, 637 638 {"AST", 0, "ar_SA", Style.SPECIFIC_SHORT, 639 EnumSet.of(ParseOption.TZ_DATABASE_ABBREVIATIONS), "Asia/Riyadh", 3, TimeType.STANDARD}, 640 641 {"AQTST", 0, "en", Style.SPECIFIC_LONG, 642 null, null, 0, TimeType.UNKNOWN}, 643 644 {"AQTST", 0, "en", Style.SPECIFIC_LONG, 645 EnumSet.of(ParseOption.ALL_STYLES), null, 0, TimeType.UNKNOWN}, 646 647 {"AQTST", 0, "en", Style.SPECIFIC_LONG, 648 EnumSet.of(ParseOption.ALL_STYLES, ParseOption.TZ_DATABASE_ABBREVIATIONS), "Asia/Aqtobe", 5, TimeType.DAYLIGHT}, 649 650 {"hora de verano británica", 0, "es", Style.SPECIFIC_LONG, 651 null, "Europe/London", 24, TimeType.DAYLIGHT}, 652 }; 653 654 for (Object[] test : DATA) { 655 String text = (String)test[0]; 656 int inPos = (Integer)test[1]; 657 ULocale loc = new ULocale((String)test[2]); 658 Style style = (Style)test[3]; 659 EnumSet<ParseOption> options = (EnumSet<ParseOption>)test[4]; 660 String expID = (String)test[5]; 661 int expPos = (Integer)test[6]; 662 TimeType expType = (TimeType)test[7]; 663 664 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(loc); 665 Output<TimeType> timeType = new Output<TimeType>(TimeType.UNKNOWN); 666 ParsePosition pos = new ParsePosition(inPos); 667 TimeZone tz = tzfmt.parse(style, text, pos, options, timeType); 668 669 String errMsg = null; 670 if (tz == null) { 671 if (expID != null) { 672 errMsg = "Parse failure - expected: " + expID; 673 } 674 } else if (!tz.getID().equals(expID)) { 675 errMsg = "Time zone ID: " + tz.getID() + " - expected: " + expID; 676 } else if (pos.getIndex() != expPos) { 677 errMsg = "Parsed pos: " + pos.getIndex() + " - expected: " + expPos; 678 } else if (timeType.value != expType) { 679 errMsg = "Time type: " + timeType + " - expected: " + expType; 680 } 681 682 if (errMsg != null) { 683 errln("Fail: " + errMsg + 684 " [text=" + text + ", pos=" + inPos + 685 ", locale=" + loc + ", style=" + style + "]"); 686 } 687 } 688 } 689 690 // Coverage tests for other versions of the parse() method. All of them end up 691 // calling the full parse() method tested on the TestParse() test. 692 @Test TestParseCoverage()693 public void TestParseCoverage() { 694 TimeZone expectedTZ = TimeZone.getTimeZone("America/Los_Angeles"); 695 TimeZoneFormat fmt = TimeZoneFormat.getInstance(ULocale.ENGLISH); 696 697 // Test parse(String) 698 try { 699 TimeZone tz1 = fmt.parse("America/Los_Angeles"); 700 if (tz1 == null) { 701 errln("Parse failure using parse(String) - expected: " + expectedTZ.getID()); 702 } else if (!expectedTZ.equals(tz1)) { 703 errln("Parsed TimeZone: '" + tz1.getID() + "' using parse(String) - expected: " 704 + expectedTZ.getID()); 705 } 706 } catch (ParseException e) { 707 errln("Parse failure using parse(String) - expected: " + expectedTZ.getID() 708 + " exception: " + e.getMessage()); 709 } 710 711 // Test parse(String, ParsePosition) 712 TimeZone tz2 = fmt.parse("++America/Los_Angeles", new ParsePosition(2)); 713 if (tz2 == null) { 714 errln("Parse failure using parse(String, ParsePosition) - expected: " 715 + expectedTZ.getID()); 716 } else if (!expectedTZ.equals(tz2)) { 717 errln("Parsed TimeZone: '" + tz2.getID() + "' using parse(String, ParsePosition) - expected: " 718 + expectedTZ.getID()); 719 } 720 721 // Test parseObject(String, ParsePosition) 722 Object tz3 = fmt.parseObject("++America/Los_Angeles", new ParsePosition(2)); 723 if (tz3 == null) { 724 errln("Parse failure using parseObject(String, ParsePosition) - expected: " 725 + expectedTZ.getID()); 726 } else if (!expectedTZ.equals(tz3)) { 727 errln("Parsed TimeZone: '" + ((TimeZone)tz3).getID() 728 + "' using parseObject(String, ParsePosition) - expected: " 729 + expectedTZ.getID()); 730 } 731 } 732 733 @Test TestISOFormat()734 public void TestISOFormat() { 735 final int[] OFFSET = { 736 0, // 0 737 999, // 0.999s 738 -59999, // -59.999s 739 60000, // 1m 740 -77777, // -1m 17.777s 741 1800000, // 30m 742 -3600000, // -1h 743 36000000, // 10h 744 -37800000, // -10h 30m 745 -37845000, // -10h 30m 45s 746 108000000, // 30h 747 }; 748 749 final String[][] ISO_STR = { 750 // 0 751 { 752 "Z", "Z", "Z", "Z", "Z", 753 "+00", "+0000", "+00:00", "+0000", "+00:00", 754 "+0000" 755 }, 756 // 999 757 { 758 "Z", "Z", "Z", "Z", "Z", 759 "+00", "+0000", "+00:00", "+0000", "+00:00", 760 "+0000" 761 }, 762 // -59999 763 { 764 "Z", "Z", "Z", "-000059", "-00:00:59", 765 "+00", "+0000", "+00:00", "-000059", "-00:00:59", 766 "-000059" 767 }, 768 // 60000 769 { 770 "+0001", "+0001", "+00:01", "+0001", "+00:01", 771 "+0001", "+0001", "+00:01", "+0001", "+00:01", 772 "+0001" 773 }, 774 // -77777 775 { 776 "-0001", "-0001", "-00:01", "-000117", "-00:01:17", 777 "-0001", "-0001", "-00:01", "-000117", "-00:01:17", 778 "-000117" 779 }, 780 // 1800000 781 { 782 "+0030", "+0030", "+00:30", "+0030", "+00:30", 783 "+0030", "+0030", "+00:30", "+0030", "+00:30", 784 "+0030" 785 }, 786 // -3600000 787 { 788 "-01", "-0100", "-01:00", "-0100", "-01:00", 789 "-01", "-0100", "-01:00", "-0100", "-01:00", 790 "-0100" 791 }, 792 // 36000000 793 { 794 "+10", "+1000", "+10:00", "+1000", "+10:00", 795 "+10", "+1000", "+10:00", "+1000", "+10:00", 796 "+1000" 797 }, 798 // -37800000 799 { 800 "-1030", "-1030", "-10:30", "-1030", "-10:30", 801 "-1030", "-1030", "-10:30", "-1030", "-10:30", 802 "-1030" 803 }, 804 // -37845000 805 { 806 "-1030", "-1030", "-10:30", "-103045", "-10:30:45", 807 "-1030", "-1030", "-10:30", "-103045", "-10:30:45", 808 "-103045" 809 }, 810 // 108000000 811 { 812 null, null, null, null, null, 813 null, null, null, null, null, 814 null 815 } 816 }; 817 818 final String[] PATTERN = { 819 "X", "XX", "XXX", "XXXX", "XXXXX", "x", "xx", "xxx", "xxxx", "xxxxx", 820 "Z", // equivalent to "xxxx" 821 }; 822 823 final int[] MIN_OFFSET_UNIT = { 824 60000, 60000, 60000, 1000, 1000, 60000, 60000, 60000, 1000, 1000, 825 1000, 826 }; 827 828 // Formatting 829 SimpleDateFormat sdf = new SimpleDateFormat(); 830 Date d = new Date(); 831 832 for (int i = 0; i < OFFSET.length; i++) { 833 SimpleTimeZone tz = new SimpleTimeZone(OFFSET[i], "Zone Offset:" + String.valueOf(OFFSET[i]) + "ms"); 834 sdf.setTimeZone(tz); 835 for (int j = 0; j < PATTERN.length; j++) { 836 sdf.applyPattern(PATTERN[j]); 837 try { 838 String result = sdf.format(d); 839 if (!result.equals(ISO_STR[i][j])) { 840 errln("FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> " 841 + result + " (expected: " + ISO_STR[i][j] + ")"); 842 } 843 } catch (IllegalArgumentException e) { 844 if (ISO_STR[i][j] != null) { 845 errln("FAIL: IAE thrown for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] 846 + " (expected: " + ISO_STR[i][j] + ")"); 847 } 848 } 849 } 850 } 851 852 // Parsing 853 SimpleTimeZone bogusTZ = new SimpleTimeZone(-1, "Zone Offset: -1ms"); 854 for (int i = 0; i < ISO_STR.length; i++) { 855 for (int j = 0; j < ISO_STR[i].length; j++) { 856 if (ISO_STR[i][j] == null) { 857 continue; 858 } 859 ParsePosition pos = new ParsePosition(0); 860 Calendar outcal = Calendar.getInstance(bogusTZ); 861 sdf.applyPattern(PATTERN[j]); 862 863 sdf.parse(ISO_STR[i][j], outcal, pos); 864 865 if (pos.getIndex() != ISO_STR[i][j].length()) { 866 errln("FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]); 867 continue; 868 } 869 870 TimeZone outtz = outcal.getTimeZone(); 871 int outOffset = outtz.getRawOffset(); 872 int adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j]; 873 874 if (outOffset != adjustedOffset) { 875 errln("FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j] 876 + " (expected:" + adjustedOffset + "ms)"); 877 } 878 } 879 } 880 } 881 882 @Test TestFormat()883 public void TestFormat() { 884 final Date dateJan = new Date(1358208000000L); // 2013-01-15T00:00:00Z 885 final Date dateJul = new Date(1373846400000L); // 2013-07-15T00:00:00Z 886 887 final Object[][] TESTDATA = { 888 { 889 "en", 890 "America/Los_Angeles", 891 dateJan, 892 Style.GENERIC_LOCATION, 893 "Los Angeles Time", 894 TimeType.UNKNOWN 895 }, 896 { 897 "en", 898 "America/Los_Angeles", 899 dateJan, 900 Style.GENERIC_LONG, 901 "Pacific Time", 902 TimeType.UNKNOWN 903 }, 904 { 905 "en", 906 "America/Los_Angeles", 907 dateJan, 908 Style.SPECIFIC_LONG, 909 "Pacific Standard Time", 910 TimeType.STANDARD 911 }, 912 { 913 "en", 914 "America/Los_Angeles", 915 dateJul, 916 Style.SPECIFIC_LONG, 917 "Pacific Daylight Time", 918 TimeType.DAYLIGHT 919 }, 920 { 921 "ja", 922 "America/Los_Angeles", 923 dateJan, 924 Style.ZONE_ID, 925 "America/Los_Angeles", 926 TimeType.UNKNOWN 927 }, 928 { 929 "fr", 930 "America/Los_Angeles", 931 dateJul, 932 Style.ZONE_ID_SHORT, 933 "uslax", 934 TimeType.UNKNOWN 935 }, 936 { 937 "en", 938 "America/Los_Angeles", 939 dateJan, 940 Style.EXEMPLAR_LOCATION, 941 "Los Angeles", 942 TimeType.UNKNOWN 943 }, 944 { 945 "ja", 946 "Asia/Tokyo", 947 dateJan, 948 Style.GENERIC_LONG, 949 "\u65E5\u672C\u6A19\u6E96\u6642", // "日本標準時" 950 TimeType.UNKNOWN 951 }, 952 }; 953 954 for (Object[] testCase : TESTDATA) { 955 TimeZone tz = TimeZone.getTimeZone((String)testCase[1]); 956 Output<TimeType> timeType = new Output<TimeType>(); 957 958 ULocale uloc = new ULocale((String)testCase[0]); 959 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(uloc); 960 String out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType); 961 962 if (!out.equals(testCase[4]) || timeType.value != testCase[5]) { 963 errln("Format result for [locale=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2] 964 + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5] 965 + "]; actual [output=" + out + ",type=" + timeType.value + "]"); 966 } 967 968 // with equivalent Java Locale 969 Locale loc = uloc.toLocale(); 970 tzfmt = TimeZoneFormat.getInstance(loc); 971 out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType); 972 973 if (!out.equals(testCase[4]) || timeType.value != testCase[5]) { 974 errln("Format result for [locale(Java)=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2] 975 + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5] 976 + "]; actual [output=" + out + ",type=" + timeType.value + "]"); 977 } 978 } 979 } 980 981 @Test TestFormatTZDBNames()982 public void TestFormatTZDBNames() { 983 final Date dateJan = new Date(1358208000000L); // 2013-01-15T00:00:00Z 984 final Date dateJul = new Date(1373846400000L); // 2013-07-15T00:00:00Z 985 986 final Object[][] TESTDATA = { 987 { 988 "en", 989 "America/Chicago", 990 dateJan, 991 Style.SPECIFIC_SHORT, 992 "CST", 993 TimeType.STANDARD 994 }, 995 { 996 "en", 997 "Asia/Shanghai", 998 dateJan, 999 Style.SPECIFIC_SHORT, 1000 "CST", 1001 TimeType.STANDARD 1002 }, 1003 { 1004 "zh_Hans", 1005 "Asia/Shanghai", 1006 dateJan, 1007 Style.SPECIFIC_SHORT, 1008 "CST", 1009 TimeType.STANDARD 1010 }, 1011 { 1012 "en", 1013 "America/Los_Angeles", 1014 dateJul, 1015 Style.SPECIFIC_LONG, 1016 "GMT-07:00", // No long display names 1017 TimeType.DAYLIGHT 1018 }, 1019 { 1020 "ja", 1021 "America/Los_Angeles", 1022 dateJul, 1023 Style.SPECIFIC_SHORT, 1024 "PDT", 1025 TimeType.DAYLIGHT 1026 }, 1027 { 1028 "en", 1029 "Australia/Sydney", 1030 dateJan, 1031 Style.SPECIFIC_SHORT, 1032 "AEDT", 1033 TimeType.DAYLIGHT 1034 }, 1035 { 1036 "en", 1037 "Australia/Sydney", 1038 dateJul, 1039 Style.SPECIFIC_SHORT, 1040 "AEST", 1041 TimeType.STANDARD 1042 }, 1043 }; 1044 1045 for (Object[] testCase : TESTDATA) { 1046 ULocale loc = new ULocale((String)testCase[0]); 1047 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(loc).cloneAsThawed(); 1048 TimeZoneNames tzdbNames = TimeZoneNames.getTZDBInstance(loc); 1049 tzfmt.setTimeZoneNames(tzdbNames); 1050 1051 TimeZone tz = TimeZone.getTimeZone((String)testCase[1]); 1052 Output<TimeType> timeType = new Output<TimeType>(); 1053 String out = tzfmt.format((Style)testCase[3], tz, ((Date)testCase[2]).getTime(), timeType); 1054 1055 if (!out.equals(testCase[4]) || timeType.value != testCase[5]) { 1056 errln("Format result for [locale=" + testCase[0] + ",tzid=" + testCase[1] + ",date=" + testCase[2] 1057 + ",style=" + testCase[3] + "]: expected [output=" + testCase[4] + ",type=" + testCase[5] 1058 + "]; actual [output=" + out + ",type=" + timeType.value + "]"); 1059 } 1060 } 1061 } 1062 1063 // Tests format(Object, StringBuffer, FieldPosition):StringBuffer method 1064 // inherited from Format class 1065 @Test TestInheritedFormat()1066 public void TestInheritedFormat() { 1067 TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); 1068 Calendar cal = Calendar.getInstance(tz); 1069 cal.setTimeInMillis(1459187377690L); // Mar 28, 2016 1070 1071 StringBuffer sb = new StringBuffer(); 1072 FieldPosition fp = new FieldPosition(DateFormat.Field.TIME_ZONE); 1073 1074 TimeZoneFormat fmt = TimeZoneFormat.getInstance(ULocale.ENGLISH); 1075 1076 // Test formatting a non-timezone related object 1077 try { 1078 fmt.format(new Object(), sb, fp); 1079 errln("ERROR: format non-timezone related object failed"); 1080 } catch (IllegalArgumentException e) { /* Expected */ } 1081 1082 // Test formatting a TimeZone object 1083 sb = new StringBuffer(); 1084 fmt.format(tz, sb, fp); 1085 // When formatting a TimeZone object the formatter uses the current date. 1086 String fmtOutput = tz.inDaylightTime(new Date()) ? "GMT-07:00" : "GMT-08:00"; 1087 if (!sb.toString().equals(fmtOutput)) { 1088 errln("ERROR: format TimerZone object failed. Expected: " + fmtOutput + ", actual: " + sb); 1089 } 1090 1091 // Test formatting a Calendar object 1092 sb = new StringBuffer(); 1093 fmt.format(cal, sb, fp); 1094 if (!sb.toString().equals("GMT-07:00")) { 1095 errln("ERROR: format Calendar object failed. Expected: GMT-07:00, actual: " + sb); 1096 } 1097 } 1098 1099 // This is a test case of Ticket#11487. 1100 // Because the problem is reproduced for the very first time, 1101 // the reported problem cannot be reproduced with regular test 1102 // execution. Run this test alone reproduced the problem before 1103 // the fix was merged. 1104 @Test TestTZDBNamesThreading()1105 public void TestTZDBNamesThreading() { 1106 final TZDBTimeZoneNames names = new TZDBTimeZoneNames(ULocale.ENGLISH); 1107 final AtomicInteger found = new AtomicInteger(); 1108 List<Thread> threads = new ArrayList<Thread>(); 1109 final int numIteration = 1000; 1110 1111 try { 1112 for (int i = 0; i < numIteration; i++) { 1113 Thread thread = new Thread() { 1114 @Override 1115 public void run() { 1116 int resultSize = names.find("GMT", 0, EnumSet.allOf(NameType.class)).size(); 1117 if (resultSize > 0) { 1118 found.incrementAndGet(); 1119 } 1120 } 1121 }; 1122 thread.start(); 1123 threads.add(thread); 1124 } 1125 1126 for(Thread thread: threads) { 1127 thread.join(); 1128 } 1129 } catch (Throwable t) { 1130 errln(t.toString()); 1131 } 1132 1133 if (found.intValue() != numIteration) { 1134 errln("Incorrect count: " + found.toString() + ", expected: " + numIteration); 1135 } 1136 } 1137 1138 @Test TestGetDisplayNames()1139 public void TestGetDisplayNames() { 1140 long date = System.currentTimeMillis(); 1141 NameType[] types = new NameType[]{ 1142 NameType.LONG_STANDARD, NameType.LONG_DAYLIGHT, 1143 NameType.SHORT_STANDARD, NameType.SHORT_DAYLIGHT 1144 }; 1145 Set<String> zones = ZoneMeta.getAvailableIDs(SystemTimeZoneType.ANY, null, null); 1146 1147 int casesTested = 0; 1148 Random rnd = new Random(2016); 1149 for (ULocale uloc : ULocale.getAvailableLocales()) { 1150 if (rnd.nextDouble() > 0.01) { continue; } 1151 for (String zone : zones) { 1152 if (rnd.nextDouble() > 0.01) { continue; } 1153 casesTested++; 1154 1155 // Test default TimeZoneNames (uses an overridden getDisplayNames) 1156 { 1157 TimeZoneNames tznames = TimeZoneNames.getInstance(uloc); 1158 tznames.loadAllDisplayNames(); 1159 String[] result = new String[types.length]; 1160 tznames.getDisplayNames(zone, types, date, result, 0); 1161 for (int i=0; i<types.length; i++) { 1162 NameType type = types[i]; 1163 String expected = result[i]; 1164 String actual = tznames.getDisplayName(zone, type, date); 1165 assertEquals("TimeZoneNames: getDisplayNames() returns different result than getDisplayName()" 1166 + " for " + zone + " in locale " + uloc, expected, actual); 1167 } 1168 // Coverage for empty call to getDisplayNames 1169 tznames.getDisplayNames(null, null, 0, null, 0); 1170 } 1171 1172 // Test TZDBTimeZoneNames (uses getDisplayNames from abstract class) 1173 { 1174 TimeZoneNames tznames = new TZDBTimeZoneNames(uloc); 1175 tznames.loadAllDisplayNames(); 1176 String[] result = new String[types.length]; 1177 tznames.getDisplayNames(zone, types, date, result, 0); 1178 for (int i=0; i<types.length; i++) { 1179 NameType type = types[i]; 1180 String expected = result[i]; 1181 String actual = tznames.getDisplayName(zone, type, date); 1182 assertEquals("TZDBTimeZoneNames: getDisplayNames() returns different result than getDisplayName()" 1183 + " for " + zone + " in locale " + uloc, expected, actual); 1184 } 1185 // Coverage for empty call to getDisplayNames 1186 tznames.getDisplayNames(null, null, 0, null, 0); 1187 } 1188 } 1189 } 1190 1191 assertTrue("No cases were tested", casesTested > 0); 1192 } 1193 1194 class TimeZoneNamesInheriter extends TimeZoneNames { 1195 private static final long serialVersionUID = 1L; 1196 1197 @Override getAvailableMetaZoneIDs()1198 public Set<String> getAvailableMetaZoneIDs() { 1199 return null; 1200 } 1201 1202 @Override getAvailableMetaZoneIDs(String tzID)1203 public Set<String> getAvailableMetaZoneIDs(String tzID) { 1204 return null; 1205 } 1206 1207 @Override getMetaZoneID(String tzID, long date)1208 public String getMetaZoneID(String tzID, long date) { 1209 return null; 1210 } 1211 1212 @Override getReferenceZoneID(String mzID, String region)1213 public String getReferenceZoneID(String mzID, String region) { 1214 return null; 1215 } 1216 1217 @Override getMetaZoneDisplayName(String mzID, NameType type)1218 public String getMetaZoneDisplayName(String mzID, NameType type) { 1219 return null; 1220 } 1221 1222 @Override getTimeZoneDisplayName(String tzID, NameType type)1223 public String getTimeZoneDisplayName(String tzID, NameType type) { 1224 return null; 1225 } 1226 } 1227 1228 // Coverage for default implementation and abstract methods in base class. 1229 @Test TestDefaultTimeZoneNames()1230 public void TestDefaultTimeZoneNames() { 1231 long date = System.currentTimeMillis(); 1232 TimeZoneNames.Factory factory; 1233 try { 1234 Class cls = Class.forName("ohos.global.icu.text.TimeZoneNames$DefaultTimeZoneNames$FactoryImpl"); 1235 factory = (Factory) cls.newInstance(); 1236 } catch (Exception e) { 1237 errln("Could not create class DefaultTimeZoneNames.FactoryImpl: " + e.getClass() + ": " + e.getMessage()); 1238 return; 1239 } 1240 TimeZoneNames tzn = factory.getTimeZoneNames(ULocale.ENGLISH); 1241 assertEquals("Abstract: getAvailableMetaZoneIDs()", 1242 tzn.getAvailableMetaZoneIDs(), Collections.emptySet()); 1243 assertEquals("Abstract: getAvailableMetaZoneIDs(String tzID)", 1244 tzn.getAvailableMetaZoneIDs("America/Chicago"), Collections.emptySet()); 1245 assertEquals("Abstract: getMetaZoneID(String tzID, long date)", 1246 tzn.getMetaZoneID("America/Chicago", date), null); 1247 assertEquals("Abstract: getReferenceZoneID(String mzID, String region)", 1248 tzn.getReferenceZoneID("America_Central", "IT"), null); 1249 assertEquals("Abstract: getMetaZoneDisplayName(String mzID, NameType type)", 1250 tzn.getMetaZoneDisplayName("America_Central", NameType.LONG_DAYLIGHT), null); 1251 assertEquals("Abstract: getTimeZoneDisplayName(String mzID, NameType type)", 1252 tzn.getTimeZoneDisplayName("America/Chicago", NameType.LONG_DAYLIGHT), null); 1253 assertEquals("Abstract: find(CharSequence text, int start, EnumSet<NameType> nameTypes)", 1254 tzn.find("foo", 0, EnumSet.noneOf(NameType.class)), Collections.emptyList()); 1255 1256 // Other abstract-class methods that aren't covered 1257 tzn = new TimeZoneNamesInheriter(); 1258 try { 1259 tzn.find(null, 0, null); 1260 } catch (UnsupportedOperationException e) { 1261 assertEquals("find() exception", "The method is not implemented in TimeZoneNames base class.", e.getMessage()); 1262 } 1263 } 1264 1265 // Basic get/set test for methods not being called otherwise. 1266 @Test TestAPI()1267 public void TestAPI() { 1268 TimeZoneFormat tzfmtEn = TimeZoneFormat.getInstance(ULocale.ENGLISH); 1269 TimeZoneFormat tzfmtAr = TimeZoneFormat.getInstance(new ULocale("ar")).cloneAsThawed(); 1270 TimeZoneNames tzn = TimeZoneNames.getInstance(Locale.ENGLISH); 1271 1272 String digits = tzfmtEn.getGMTOffsetDigits(); 1273 tzfmtAr.setGMTOffsetDigits(digits); 1274 if (!digits.equals(tzfmtAr.getGMTOffsetDigits())) { 1275 errln("ERROR: get/set GMTOffsetDigits failed"); 1276 } 1277 1278 String pattern = tzfmtEn.getGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H); 1279 tzfmtAr.setGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H, pattern); 1280 if (!pattern.equals(tzfmtAr.getGMTOffsetPattern(GMTOffsetPatternType.POSITIVE_H))) { 1281 errln("ERROR: get/set GMTOffsetPattern failed"); 1282 } 1283 1284 String zeroFmt = tzfmtEn.getGMTZeroFormat(); 1285 tzfmtAr.setGMTZeroFormat(zeroFmt); 1286 if (!zeroFmt.equals(tzfmtAr.getGMTZeroFormat())) { 1287 errln("ERROR: get/set GMTZeroFormat failed"); 1288 } 1289 1290 Set<String> allAvailableMZIDs = tzn.getAvailableMetaZoneIDs(); 1291 if (allAvailableMZIDs.size() < 150 || !allAvailableMZIDs.contains("America_Central")) { 1292 errln("ERROR: getAvailableMetaZoneIDs() did not return expected value"); 1293 } 1294 1295 Set<String> kinshasaAvailableMZIDs = tzn.getAvailableMetaZoneIDs("Africa/Kinshasa"); 1296 if (!kinshasaAvailableMZIDs.contains("Africa_Western") || kinshasaAvailableMZIDs.contains("America_Central")) { 1297 errln("ERROR: getAvailableMetaZoneIDs('Africa/Kinshasa') did not return expected value"); 1298 } 1299 1300 try { 1301 new TimeZoneNames.MatchInfo(null, null, null, -1); 1302 assertTrue("MatchInfo doesn't throw IllegalArgumentException", false); 1303 } catch (IllegalArgumentException e) { 1304 assertEquals("MatchInfo constructor exception", "nameType is null", e.getMessage()); 1305 } 1306 1307 try { 1308 new TimeZoneNames.MatchInfo(NameType.LONG_GENERIC, null, null, -1); 1309 assertTrue("MatchInfo doesn't throw IllegalArgumentException", false); 1310 } catch (IllegalArgumentException e) { 1311 assertEquals("MatchInfo constructor exception", "Either tzID or mzID must be available", e.getMessage()); 1312 } 1313 1314 try { 1315 new TimeZoneNames.MatchInfo(NameType.LONG_GENERIC, "America/Chicago", null, -1); 1316 assertTrue("MatchInfo doesn't throw IllegalArgumentException", false); 1317 } catch (IllegalArgumentException e) { 1318 assertEquals("MatchInfo constructor exception", "matchLength must be positive value", e.getMessage()); 1319 } 1320 } 1321 } 1322