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