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) 2011-2016, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.text; 10 11 import java.io.Serializable; 12 import java.util.Collection; 13 import java.util.Collections; 14 import java.util.EnumSet; 15 import java.util.Locale; 16 import java.util.Set; 17 18 import com.ibm.icu.impl.ICUConfig; 19 import com.ibm.icu.impl.SoftCache; 20 import com.ibm.icu.impl.TZDBTimeZoneNames; 21 import com.ibm.icu.impl.TimeZoneNamesImpl; 22 import com.ibm.icu.util.TimeZone; 23 import com.ibm.icu.util.ULocale; 24 25 /** 26 * <code>TimeZoneNames</code> is an abstract class representing the time zone display name data model defined 27 * by <a href="http://www.unicode.org/reports/tr35/">UTS#35 Unicode Locale Data Markup Language (LDML)</a>. 28 * The model defines meta zone, which is used for storing a set of display names. A meta zone can be shared 29 * by multiple time zones. Also a time zone may have multiple meta zone historic mappings. 30 * <p> 31 * For example, people in the United States refer the zone used by the east part of North America as "Eastern Time". 32 * The tz database contains multiple time zones "America/New_York", "America/Detroit", "America/Montreal" and some 33 * others that belong to "Eastern Time". However, assigning different display names to these time zones does not make 34 * much sense for most of people. 35 * <p> 36 * In <a href="http://cldr.unicode.org/">CLDR</a> (which uses LDML for representing locale data), the display name 37 * "Eastern Time" is stored as long generic display name of a meta zone identified by the ID "America_Eastern". 38 * Then, there is another table maintaining the historic mapping to meta zones for each time zone. The time zones in 39 * the above example ("America/New_York", "America/Detroit"...) are mapped to the meta zone "America_Eastern". 40 * <p> 41 * Sometimes, a time zone is mapped to a different time zone in the past. For example, "America/Indiana/Knox" 42 * had been moving "Eastern Time" and "Central Time" back and forth. Therefore, it is necessary that time zone 43 * to meta zones mapping data are stored by date range. 44 * 45 * <p><b>Note:</b> 46 * <p> 47 * {@link TimeZoneFormat} assumes an instance of <code>TimeZoneNames</code> is immutable. If you want to provide 48 * your own <code>TimeZoneNames</code> implementation and use it with {@link TimeZoneFormat}, you must follow 49 * the contract. 50 * <p> 51 * The methods in this class assume that time zone IDs are already canonicalized. For example, you may not get proper 52 * result returned by a method with time zone ID "America/Indiana/Indianapolis", because it's not a canonical time zone 53 * ID (the canonical time zone ID for the time zone is "America/Indianapolis". See 54 * {@link TimeZone#getCanonicalID(String)} about ICU canonical time zone IDs. 55 * 56 * <p> 57 * In CLDR, most of time zone display names except location names are provided through meta zones. But a time zone may 58 * have a specific name that is not shared with other time zones. 59 * 60 * For example, time zone "Europe/London" has English long name for standard time "Greenwich Mean Time", which is also 61 * shared with other time zones. However, the long name for daylight saving time is "British Summer Time", which is only 62 * used for "Europe/London". 63 * 64 * <p> 65 * {@link #getTimeZoneDisplayName(String, NameType)} is designed for accessing a name only used by a single time zone. 66 * But is not necessarily mean that a subclass implementation use the same model with CLDR. A subclass implementation 67 * may provide time zone names only through {@link #getTimeZoneDisplayName(String, NameType)}, or only through 68 * {@link #getMetaZoneDisplayName(String, NameType)}, or both. 69 * 70 * <p> 71 * The default <code>TimeZoneNames</code> implementation returned by {@link #getInstance(ULocale)} uses the locale data 72 * imported from CLDR. In CLDR, set of meta zone IDs and mappings between zone IDs and meta zone IDs are shared by all 73 * locales. Therefore, the behavior of {@link #getAvailableMetaZoneIDs()}, {@link #getAvailableMetaZoneIDs(String)}, 74 * {@link #getMetaZoneID(String, long)}, and {@link #getReferenceZoneID(String, String)} won't be changed no matter 75 * what locale is used for getting an instance of <code>TimeZoneNames</code>. 76 * 77 * @stable ICU 49 78 */ 79 public abstract class TimeZoneNames implements Serializable { 80 81 private static final long serialVersionUID = -9180227029248969153L; 82 83 /** 84 * Time zone display name types 85 * 86 * @stable ICU 49 87 */ 88 public enum NameType { 89 /** 90 * Long display name, such as "Eastern Time". 91 * 92 * @stable ICU 49 93 */ 94 LONG_GENERIC, 95 /** 96 * Long display name for standard time, such as "Eastern Standard Time". 97 * 98 * @stable ICU 49 99 */ 100 LONG_STANDARD, 101 /** 102 * Long display name for daylight saving time, such as "Eastern Daylight Time". 103 * 104 * @stable ICU 49 105 */ 106 LONG_DAYLIGHT, 107 /** 108 * Short display name, such as "ET". 109 * 110 * @stable ICU 49 111 */ 112 SHORT_GENERIC, 113 /** 114 * Short display name for standard time, such as "EST". 115 * 116 * @stable ICU 49 117 */ 118 SHORT_STANDARD, 119 /** 120 * Short display name for daylight saving time, such as "EDT". 121 * 122 * @stable ICU 49 123 */ 124 SHORT_DAYLIGHT, 125 /** 126 * Exemplar location name, such as "Los Angeles". 127 * 128 * @stable ICU 51 129 */ 130 EXEMPLAR_LOCATION, 131 } 132 133 private static Cache TZNAMES_CACHE = new Cache(); 134 135 private static final Factory TZNAMES_FACTORY; 136 private static final String FACTORY_NAME_PROP = "com.ibm.icu.text.TimeZoneNames.Factory.impl"; 137 private static final String DEFAULT_FACTORY_CLASS = "com.ibm.icu.impl.TimeZoneNamesFactoryImpl"; 138 139 static { 140 Factory factory = null; 141 String classname = ICUConfig.get(FACTORY_NAME_PROP, DEFAULT_FACTORY_CLASS); 142 while (true) { 143 try { 144 factory = (Factory) Class.forName(classname).newInstance(); 145 break; 146 } catch (ClassNotFoundException cnfe) { 147 // fall through 148 } catch (IllegalAccessException iae) { 149 // fall through 150 } catch (InstantiationException ie) { 151 // fall through 152 } 153 if (classname.equals(DEFAULT_FACTORY_CLASS)) { 154 break; 155 } 156 classname = DEFAULT_FACTORY_CLASS; 157 } 158 159 if (factory == null) { 160 factory = new DefaultTimeZoneNames.FactoryImpl(); 161 } 162 TZNAMES_FACTORY = factory; 163 } 164 165 /** 166 * Returns an instance of <code>TimeZoneNames</code> for the specified locale. 167 * 168 * @param locale 169 * The locale. 170 * @return An instance of <code>TimeZoneNames</code> 171 * @stable ICU 49 172 */ getInstance(ULocale locale)173 public static TimeZoneNames getInstance(ULocale locale) { 174 String key = locale.getBaseName(); 175 return TZNAMES_CACHE.getInstance(key, locale); 176 } 177 178 /** 179 * Returns an instance of <code>TimeZoneNames</code> for the specified 180 * {@link java.util.Locale}. 181 * 182 * @param locale 183 * The {@link java.util.Locale}. 184 * @return An instance of <code>TimeZoneDisplayNames</code> 185 * @stable ICU 54 186 */ getInstance(Locale locale)187 public static TimeZoneNames getInstance(Locale locale) { 188 return getInstance(ULocale.forLocale(locale)); 189 } 190 191 /** 192 * Returns an instance of <code>TimeZoneNames</code> containing only short specific 193 * zone names ({@link NameType#SHORT_STANDARD} and {@link NameType#SHORT_DAYLIGHT}), 194 * compatible with the IANA tz database's zone abbreviations (not localized). 195 * <br> 196 * Note: The input locale is used for resolving ambiguous names (e.g. "IST" is parsed 197 * as Israel Standard Time for Israel, while it is parsed as India Standard Time for 198 * all other regions). The zone names returned by this instance are not localized. 199 * 200 * @stable ICU 54 201 */ getTZDBInstance(ULocale locale)202 public static TimeZoneNames getTZDBInstance(ULocale locale) { 203 return new TZDBTimeZoneNames(locale); 204 } 205 206 /** 207 * Returns an immutable set of all available meta zone IDs. 208 * @return An immutable set of all available meta zone IDs. 209 * @stable ICU 49 210 */ getAvailableMetaZoneIDs()211 public abstract Set<String> getAvailableMetaZoneIDs(); 212 213 /** 214 * Returns an immutable set of all available meta zone IDs used by the given time zone. 215 * 216 * @param tzID 217 * The canonical time zone ID. 218 * @return An immutable set of all available meta zone IDs used by the given time zone. 219 * @stable ICU 49 220 */ getAvailableMetaZoneIDs(String tzID)221 public abstract Set<String> getAvailableMetaZoneIDs(String tzID); 222 223 /** 224 * Returns the meta zone ID for the given canonical time zone ID at the given date. 225 * 226 * @param tzID 227 * The canonical time zone ID. 228 * @param date 229 * The date. 230 * @return The meta zone ID for the given time zone ID at the given date. If the time zone does not have a 231 * corresponding meta zone at the given date or the implementation does not support meta zones, null is 232 * returned. 233 * @stable ICU 49 234 */ getMetaZoneID(String tzID, long date)235 public abstract String getMetaZoneID(String tzID, long date); 236 237 /** 238 * Returns the reference zone ID for the given meta zone ID for the region. 239 * 240 * Note: Each meta zone must have a reference zone associated with a special region "001" (world). 241 * Some meta zones may have region specific reference zone IDs other than the special region 242 * "001". When a meta zone does not have any region specific reference zone IDs, this method 243 * return the reference zone ID for the special region "001" (world). 244 * 245 * @param mzID 246 * The meta zone ID. 247 * @param region 248 * The region. 249 * @return The reference zone ID ("golden zone" in the LDML specification) for the given time zone ID for the 250 * region. If the meta zone is unknown or the implementation does not support meta zones, null is returned. 251 * @stable ICU 49 252 */ getReferenceZoneID(String mzID, String region)253 public abstract String getReferenceZoneID(String mzID, String region); 254 255 /** 256 * Returns the display name of the meta zone. 257 * 258 * @param mzID 259 * The meta zone ID. 260 * @param type 261 * The display name type. See {@link TimeZoneNames.NameType}. 262 * @return The display name of the meta zone. When this object does not have a localized display name for the given 263 * meta zone with the specified type or the implementation does not provide any display names associated 264 * with meta zones, null is returned. 265 * @stable ICU 49 266 */ getMetaZoneDisplayName(String mzID, NameType type)267 public abstract String getMetaZoneDisplayName(String mzID, NameType type); 268 269 /** 270 * Returns the display name of the time zone at the given date. 271 * 272 * <p> 273 * <b>Note:</b> This method calls the subclass's {@link #getTimeZoneDisplayName(String, NameType)} first. When the 274 * result is null, this method calls {@link #getMetaZoneID(String, long)} to get the meta zone ID mapped from the 275 * time zone, then calls {@link #getMetaZoneDisplayName(String, NameType)}. 276 * 277 * @param tzID 278 * The canonical time zone ID. 279 * @param type 280 * The display name type. See {@link TimeZoneNames.NameType}. 281 * @param date 282 * The date 283 * @return The display name for the time zone at the given date. When this object does not have a localized display 284 * name for the time zone with the specified type and date, null is returned. 285 * @stable ICU 49 286 */ getDisplayName(String tzID, NameType type, long date)287 public final String getDisplayName(String tzID, NameType type, long date) { 288 String name = getTimeZoneDisplayName(tzID, type); 289 if (name == null) { 290 String mzID = getMetaZoneID(tzID, date); 291 name = getMetaZoneDisplayName(mzID, type); 292 } 293 return name; 294 } 295 296 /** 297 * Returns the display name of the time zone. Unlike {@link #getDisplayName(String, NameType, long)}, 298 * this method does not get a name from a meta zone used by the time zone. 299 * 300 * @param tzID 301 * The canonical time zone ID. 302 * @param type 303 * The display name type. See {@link TimeZoneNames.NameType}. 304 * @return The display name for the time zone. When this object does not have a localized display name for the given 305 * time zone with the specified type, null is returned. 306 * @stable ICU 49 307 */ getTimeZoneDisplayName(String tzID, NameType type)308 public abstract String getTimeZoneDisplayName(String tzID, NameType type); 309 310 /** 311 * Returns the exemplar location name for the given time zone. When this object does not have a localized location 312 * name, the default implementation may still returns a programmatically generated name with the logic described 313 * below. 314 * <ol> 315 * <li>Check if the ID contains "/". If not, return null. 316 * <li>Check if the ID does not start with "Etc/" or "SystemV/". If it does, return null. 317 * <li>Extract a substring after the last occurrence of "/". 318 * <li>Replace "_" with " ". 319 * </ol> 320 * For example, "New York" is returned for the time zone ID "America/New_York" when this object does not have the 321 * localized location name. 322 * 323 * @param tzID 324 * The canonical time zone ID 325 * @return The exemplar location name for the given time zone, or null when a localized location name is not 326 * available and the fallback logic described above cannot extract location from the ID. 327 * @stable ICU 49 328 */ getExemplarLocationName(String tzID)329 public String getExemplarLocationName(String tzID) { 330 return TimeZoneNamesImpl.getDefaultExemplarLocationName(tzID); 331 } 332 333 /** 334 * Finds time zone name prefix matches for the input text at the 335 * given offset and returns a collection of the matches. 336 * 337 * @param text the text. 338 * @param start the starting offset within the text. 339 * @param types the set of name types, or <code>null</code> for all name types. 340 * @return A collection of matches. 341 * @see NameType 342 * @see MatchInfo 343 * @draft ICU 49 (Retain) 344 * @provisional This API might change or be removed in a future release. 345 */ find(CharSequence text, int start, EnumSet<NameType> types)346 public Collection<MatchInfo> find(CharSequence text, int start, EnumSet<NameType> types) { 347 throw new UnsupportedOperationException("The method is not implemented in TimeZoneNames base class."); 348 } 349 350 /** 351 * A <code>MatchInfo</code> represents a time zone name match used by 352 * {@link TimeZoneNames#find(CharSequence, int, EnumSet)}. 353 * @draft ICU 49 (Retain) 354 * @provisional This API might change or be removed in a future release. 355 */ 356 public static class MatchInfo { 357 private NameType _nameType; 358 private String _tzID; 359 private String _mzID; 360 private int _matchLength; 361 362 /** 363 * Constructing a <code>MatchInfo</code>. 364 * 365 * @param nameType the name type enum. 366 * @param tzID the time zone ID, or null 367 * @param mzID the meta zone ID, or null 368 * @param matchLength the match length. 369 * @throws IllegalArgumentException when 1) <code>nameType</code> is <code>null</code>, 370 * or 2) both <code>tzID</code> and <code>mzID</code> are <code>null</code>, 371 * or 3) <code>matchLength</code> is 0 or smaller. 372 * @see NameType 373 * @draft ICU 49 (Retain) 374 * @provisional This API might change or be removed in a future release. 375 */ MatchInfo(NameType nameType, String tzID, String mzID, int matchLength)376 public MatchInfo(NameType nameType, String tzID, String mzID, int matchLength) { 377 if (nameType == null) { 378 throw new IllegalArgumentException("nameType is null"); 379 } 380 if (tzID == null && mzID == null) { 381 throw new IllegalArgumentException("Either tzID or mzID must be available"); 382 } 383 if (matchLength <= 0) { 384 throw new IllegalArgumentException("matchLength must be positive value"); 385 } 386 _nameType = nameType; 387 _tzID = tzID; 388 _mzID = mzID; 389 _matchLength = matchLength; 390 } 391 392 /** 393 * Returns the time zone ID, or <code>null</code> if not available. 394 * 395 * <p><b>Note</b>: A <code>MatchInfo</code> must have either a time zone ID 396 * or a meta zone ID. 397 * 398 * @return the time zone ID, or <code>null</code>. 399 * @see #mzID() 400 * @draft ICU 49 (Retain) 401 * @provisional This API might change or be removed in a future release. 402 */ tzID()403 public String tzID() { 404 return _tzID; 405 } 406 407 /** 408 * Returns the meta zone ID, or <code>null</code> if not available. 409 * 410 * <p><b>Note</b>: A <code>MatchInfo</code> must have either a time zone ID 411 * or a meta zone ID. 412 * 413 * @return the meta zone ID, or <code>null</code>. 414 * @see #tzID() 415 * @draft ICU 49 (Retain) 416 * @provisional This API might change or be removed in a future release. 417 */ mzID()418 public String mzID() { 419 return _mzID; 420 } 421 422 /** 423 * Returns the time zone name type. 424 * @return the time zone name type enum. 425 * @see NameType 426 * @draft ICU 49 (Retain) 427 * @provisional This API might change or be removed in a future release. 428 */ nameType()429 public NameType nameType() { 430 return _nameType; 431 } 432 433 /** 434 * Returns the match length. 435 * @return the match length. 436 * @draft ICU 49 (Retain) 437 * @provisional This API might change or be removed in a future release. 438 */ matchLength()439 public int matchLength() { 440 return _matchLength; 441 } 442 } 443 444 /** 445 * @internal For specific users only until proposed publicly. 446 * @deprecated This API is ICU internal only. 447 */ 448 @Deprecated loadAllDisplayNames()449 public void loadAllDisplayNames() {} 450 451 /** 452 * @internal For specific users only until proposed publicly. 453 * @deprecated This API is ICU internal only. 454 */ 455 @Deprecated getDisplayNames(String tzID, NameType[] types, long date, String[] dest, int destOffset)456 public void getDisplayNames(String tzID, NameType[] types, long date, 457 String[] dest, int destOffset) { 458 if (tzID == null || tzID.length() == 0) { 459 return; 460 } 461 String mzID = null; 462 for (int i = 0; i < types.length; ++i) { 463 NameType type = types[i]; 464 String name = getTimeZoneDisplayName(tzID, type); 465 if (name == null) { 466 if (mzID == null) { 467 mzID = getMetaZoneID(tzID, date); 468 } 469 name = getMetaZoneDisplayName(mzID, type); 470 } 471 dest[destOffset + i] = name; 472 } 473 } 474 475 /** 476 * Sole constructor for invocation by subclass constructors. 477 * 478 * @draft ICU 49 (Retain) 479 * @provisional This API might change or be removed in a future release. 480 */ TimeZoneNames()481 protected TimeZoneNames() { 482 } 483 484 /** 485 * The super class of <code>TimeZoneNames</code> service factory classes. 486 * 487 * @internal 488 * @deprecated This API is ICU internal only. 489 */ 490 @Deprecated 491 public static abstract class Factory { 492 /** 493 * The factory method of <code>TimeZoneNames</code>. 494 * 495 * @param locale 496 * The display locale 497 * @return An instance of <code>TimeZoneNames</code>. 498 * @internal 499 * @deprecated This API is ICU internal only. 500 */ 501 @Deprecated getTimeZoneNames(ULocale locale)502 public abstract TimeZoneNames getTimeZoneNames(ULocale locale); 503 504 /** 505 * Sole constructor 506 * @internal 507 * @deprecated This API is ICU internal only. 508 */ 509 @Deprecated Factory()510 protected Factory() { 511 } 512 } 513 514 /** 515 * TimeZoneNames cache used by {@link TimeZoneNames#getInstance(ULocale)} 516 */ 517 private static class Cache extends SoftCache<String, TimeZoneNames, ULocale> { 518 519 /* 520 * (non-Javadoc) 521 * 522 * @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object) 523 */ 524 @Override createInstance(String key, ULocale data)525 protected TimeZoneNames createInstance(String key, ULocale data) { 526 return TZNAMES_FACTORY.getTimeZoneNames(data); 527 } 528 529 } 530 531 ///CLOVER:OFF 532 /** 533 * The default implementation of <code>TimeZoneNames</code> used by {@link TimeZoneNames#getInstance(ULocale)} when 534 * the ICU4J tznamedata component is not available. 535 */ 536 private static class DefaultTimeZoneNames extends TimeZoneNames { 537 538 private static final long serialVersionUID = -995672072494349071L; 539 540 public static final DefaultTimeZoneNames INSTANCE = new DefaultTimeZoneNames(); 541 542 /* (non-Javadoc) 543 * @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs() 544 */ 545 @Override getAvailableMetaZoneIDs()546 public Set<String> getAvailableMetaZoneIDs() { 547 return Collections.emptySet(); 548 } 549 550 /* (non-Javadoc) 551 * @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs(java.lang.String) 552 */ 553 @Override getAvailableMetaZoneIDs(String tzID)554 public Set<String> getAvailableMetaZoneIDs(String tzID) { 555 return Collections.emptySet(); 556 } 557 558 /* 559 * (non-Javadoc) 560 * 561 * @see com.ibm.icu.text.TimeZoneNames#getMetaZoneID (java.lang.String, long) 562 */ 563 @Override getMetaZoneID(String tzID, long date)564 public String getMetaZoneID(String tzID, long date) { 565 return null; 566 } 567 568 /* 569 * (non-Javadoc) 570 * 571 * @see com.ibm.icu.text.TimeZoneNames#getReferenceZoneID(java.lang.String, java.lang.String) 572 */ 573 @Override getReferenceZoneID(String mzID, String region)574 public String getReferenceZoneID(String mzID, String region) { 575 return null; 576 } 577 578 /* 579 * (non-Javadoc) 580 * @see com.ibm.icu.text.TimeZoneNames#getMetaZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType) 581 */ 582 @Override getMetaZoneDisplayName(String mzID, NameType type)583 public String getMetaZoneDisplayName(String mzID, NameType type) { 584 return null; 585 } 586 587 /* 588 * (non-Javadoc) 589 * @see com.ibm.icu.text.TimeZoneNames#getTimeZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType) 590 */ 591 @Override getTimeZoneDisplayName(String tzID, NameType type)592 public String getTimeZoneDisplayName(String tzID, NameType type) { 593 return null; 594 } 595 596 /* (non-Javadoc) 597 * @see com.ibm.icu.text.TimeZoneNames#find(java.lang.CharSequence, int, com.ibm.icu.text.TimeZoneNames.NameType[]) 598 */ 599 @Override find(CharSequence text, int start, EnumSet<NameType> nameTypes)600 public Collection<MatchInfo> find(CharSequence text, int start, EnumSet<NameType> nameTypes) { 601 return Collections.emptyList(); 602 } 603 604 /** 605 * The default <code>TimeZoneNames</code> factory called from {@link TimeZoneNames#getInstance(ULocale)} when 606 * the ICU4J tznamedata component is not available. 607 */ 608 public static class FactoryImpl extends Factory { 609 610 /* 611 * (non-Javadoc) 612 * 613 * @see com.ibm.icu.text.TimeZoneNames.Factory#getTimeZoneNames (com.ibm.icu.util.ULocale) 614 */ 615 @Override getTimeZoneNames(ULocale locale)616 public TimeZoneNames getTimeZoneNames(ULocale locale) { 617 return DefaultTimeZoneNames.INSTANCE; 618 } 619 } 620 } 621 ///CLOVER:ON 622 } 623