1 /* 2 * @(#)TimeZone.java 1.51 00/01/19 3 * 4 * Copyright (C) 1996-2015, International Business Machines 5 * Corporation and others. All Rights Reserved. 6 */ 7 8 package com.ibm.icu.util; 9 10 import java.io.Serializable; 11 import java.util.Date; 12 import java.util.Locale; 13 import java.util.MissingResourceException; 14 import java.util.Set; 15 import java.util.logging.Logger; 16 17 import com.ibm.icu.impl.Grego; 18 import com.ibm.icu.impl.ICUConfig; 19 import com.ibm.icu.impl.ICUResourceBundle; 20 import com.ibm.icu.impl.JavaTimeZone; 21 import com.ibm.icu.impl.TimeZoneAdapter; 22 import com.ibm.icu.impl.ZoneMeta; 23 import com.ibm.icu.text.TimeZoneFormat; 24 import com.ibm.icu.text.TimeZoneFormat.Style; 25 import com.ibm.icu.text.TimeZoneFormat.TimeType; 26 import com.ibm.icu.text.TimeZoneNames; 27 import com.ibm.icu.text.TimeZoneNames.NameType; 28 import com.ibm.icu.util.ULocale.Category; 29 30 /** 31 * {@icuenhanced java.util.TimeZone}.{@icu _usage_} 32 * 33 * <p><code>TimeZone</code> represents a time zone offset, and also computes daylight 34 * savings. 35 * 36 * <p>Typically, you get a <code>TimeZone</code> using {@link #getDefault()} 37 * which creates a <code>TimeZone</code> based on the time zone where the program 38 * is running. For example, for a program running in Japan, <code>getDefault</code> 39 * creates a <code>TimeZone</code> object based on Japanese Standard Time. 40 * 41 * <p>You can also get a <code>TimeZone</code> using {@link #getTimeZone(String)} 42 * along with a time zone ID. For instance, the time zone ID for the 43 * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a 44 * U.S. Pacific Time <code>TimeZone</code> object with: 45 * 46 * <blockquote> 47 * <pre> 48 * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); 49 * </pre> 50 * </blockquote> 51 * You can use the {@link #getAvailableIDs()} method to iterate through 52 * all the supported time zone IDs, or getCanonicalID method to check 53 * if a time zone ID is supported or not. You can then choose a 54 * supported ID to get a <code>TimeZone</code>. 55 * If the time zone you want is not represented by one of the 56 * supported IDs, then you can create a custom time zone ID with 57 * the following syntax: 58 * 59 * <blockquote> 60 * <pre> 61 * GMT[+|-]hh[[:]mm] 62 * </pre> 63 * </blockquote> 64 * 65 * For example, you might specify GMT+14:00 as a custom 66 * time zone ID. The <code>TimeZone</code> that is returned 67 * when you specify a custom time zone ID uses the specified 68 * offset from GMT(=UTC) and does not observe daylight saving 69 * time. For example, you might specify GMT+14:00 as a custom 70 * time zone ID to create a TimeZone representing 14 hours ahead 71 * of GMT (with no daylight saving time). In addition, 72 * <code>getCanonicalID</code> can also be used to 73 * normalize a custom time zone ID. 74 * 75 * <p>For compatibility with JDK 1.1.x, some other three-letter time zone IDs 76 * (such as "PST", "CTT", "AST") are also supported. However, <strong>their 77 * use is deprecated</strong> because the same abbreviation is often used 78 * for multiple time zones (for example, "CST" could be U.S. "Central Standard 79 * Time" and "China Standard Time"), and the Java platform can then only 80 * recognize one of them. 81 * 82 * <p><strong>Note:</strong> Starting from ICU4J 4.0, you can optionally choose 83 * JDK <code>TimeZone</code> as the time zone implementation. The TimeZone factory 84 * method <code>getTimeZone</code> creates an instance of ICU's own <code>TimeZone</code> 85 * subclass by default. If you want to use the JDK implementation always, you can 86 * set the default time zone implementation type by the new method 87 * <code>setDefaultTimeZoneType</code>. Alternatively, you can change the initial 88 * default implementation type by setting a property below. 89 * 90 * <blockquote> 91 * <pre> 92 * # 93 * # The default TimeZone implementation type used by the ICU TimeZone 94 * # factory method. [ ICU | JDK ] 95 * # 96 * com.ibm.icu.util.TimeZone.DefaultTimeZoneType = ICU 97 * </pre> 98 * </blockquote> 99 * 100 * <p>This property is included in ICUConfig.properties in com.ibm.icu package. When the 101 * <code>TimeZone</code> class is loaded, the initialization code checks if the property 102 * <code>com.ibm.icu.util.TimeZone.DefaultTimeZoneType=xxx</code> is defined by the system 103 * properties. If not available, then it loads ICUConfig.properties to get the default 104 * time zone implementation type. The property setting is only used for the initial 105 * default value and you can change the default type by calling 106 * <code>setDefaultTimeZoneType</code> at runtime. 107 * 108 * @see Calendar 109 * @see GregorianCalendar 110 * @see SimpleTimeZone 111 * @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu 112 * @stable ICU 2.0 113 */ 114 abstract public class TimeZone implements Serializable, Cloneable, Freezable<TimeZone> { 115 /** 116 * Logger instance for this class 117 */ 118 private static final Logger LOGGER = Logger.getLogger("com.ibm.icu.util.TimeZone"); 119 120 // using serialver from jdk1.4.2_05 121 private static final long serialVersionUID = -744942128318337471L; 122 123 /** 124 * Default constructor. (For invocation by subclass constructors, 125 * typically implicit.) 126 * @stable ICU 2.8 127 */ TimeZone()128 public TimeZone() { 129 } 130 131 /** 132 * Constructing a TimeZone with the given time zone ID. 133 * @param ID the time zone ID. 134 * @internal 135 * @deprecated This API is ICU internal only. 136 */ 137 @Deprecated TimeZone(String ID)138 protected TimeZone(String ID) { 139 if (ID == null) { 140 throw new NullPointerException(); 141 } 142 this.ID = ID; 143 } 144 145 /** 146 * {@icu} A time zone implementation type indicating ICU's own TimeZone used by 147 * <code>getTimeZone</code>, <code>setDefaultTimeZoneType</code> 148 * and <code>getDefaultTimeZoneType</code>. 149 * @stable ICU 4.0 150 */ 151 public static final int TIMEZONE_ICU = 0; 152 /** 153 * {@icu} A time zone implementation type indicating JDK TimeZone used by 154 * <code>getTimeZone</code>, <code>setDefaultTimeZoneType</code> 155 * and <code>getDefaultTimeZoneType</code>. 156 * @stable ICU 4.0 157 */ 158 public static final int TIMEZONE_JDK = 1; 159 160 /** 161 * A style specifier for <code>getDisplayName()</code> indicating 162 * a short name, such as "PST." 163 * @see #LONG 164 * @stable ICU 2.0 165 */ 166 public static final int SHORT = 0; 167 168 /** 169 * A style specifier for <code>getDisplayName()</code> indicating 170 * a long name, such as "Pacific Standard Time." 171 * @see #SHORT 172 * @stable ICU 2.0 173 */ 174 public static final int LONG = 1; 175 176 /** 177 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 178 * a short generic name, such as "PT." 179 * @see #LONG_GENERIC 180 * @stable ICU 4.4 181 */ 182 public static final int SHORT_GENERIC = 2; 183 184 /** 185 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 186 * a long generic name, such as "Pacific Time." 187 * @see #SHORT_GENERIC 188 * @stable ICU 4.4 189 */ 190 public static final int LONG_GENERIC = 3; 191 192 /** 193 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 194 * a short name derived from the timezone's offset, such as "-0800." 195 * @see #LONG_GMT 196 * @stable ICU 4.4 197 */ 198 public static final int SHORT_GMT = 4; 199 200 /** 201 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 202 * a long name derived from the timezone's offset, such as "GMT-08:00." 203 * @see #SHORT_GMT 204 * @stable ICU 4.4 205 */ 206 public static final int LONG_GMT = 5; 207 208 /** 209 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 210 * a short name derived from the timezone's short standard or daylight 211 * timezone name ignoring commonlyUsed, such as "PDT." 212 * @stable ICU 4.4 213 */ 214 215 public static final int SHORT_COMMONLY_USED = 6; 216 217 /** 218 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 219 * a long name derived from the timezone's fallback name, such as 220 * "United States (Los Angeles)." 221 * @stable ICU 4.4 222 */ 223 public static final int GENERIC_LOCATION = 7; 224 225 /** 226 * {@icu} The time zone ID reserved for unknown time zone. 227 * @see #getTimeZone(String) 228 * 229 * @stable ICU 4.8 230 */ 231 public static final String UNKNOWN_ZONE_ID = "Etc/Unknown"; 232 233 /** 234 * The canonical ID for GMT(UTC) time zone. 235 */ 236 static final String GMT_ZONE_ID = "Etc/GMT"; 237 238 /** 239 * {@icu} The immutable (frozen) "unknown" time zone. 240 * It behaves like the GMT/UTC time zone but has the UNKNOWN_ZONE_ID = "Etc/Unknown". 241 * {@link TimeZone#getTimeZone(String)} returns a mutable clone of this 242 * time zone if the input ID is not recognized. 243 * 244 * @see #UNKNOWN_ZONE_ID 245 * @see #getTimeZone(String) 246 * 247 * @stable ICU 49 248 */ 249 public static final TimeZone UNKNOWN_ZONE = new ConstantZone(0, UNKNOWN_ZONE_ID).freeze(); 250 251 /** 252 * {@icu} The immutable GMT (=UTC) time zone. Its ID is "Etc/GMT". 253 * 254 * @stable ICU 49 255 */ 256 public static final TimeZone GMT_ZONE = new ConstantZone(0, GMT_ZONE_ID).freeze(); 257 258 /** 259 * {@icu} System time zone type constants used by filtering zones in 260 * {@link TimeZone#getAvailableIDs(SystemTimeZoneType, String, Integer)} 261 * 262 * @stable ICU 4.8 263 */ 264 public enum SystemTimeZoneType { 265 /** 266 * Any system zones. 267 * @stable ICU 4.8 268 * @provisional This API might change or be removed in a future release. 269 */ 270 ANY, 271 272 /** 273 * Canonical system zones. 274 * @stable ICU 4.8 275 * @provisional This API might change or be removed in a future release. 276 */ 277 CANONICAL, 278 279 /** 280 * Canonical system zones associated with actual locations. 281 * @stable ICU 4.8 282 * @provisional This API might change or be removed in a future release. 283 */ 284 CANONICAL_LOCATION, 285 } 286 287 /** 288 * Gets the time zone offset, for current date, modified in case of 289 * daylight savings. This is the offset to add *to* UTC to get local time. 290 * @param era the era of the given date. 291 * @param year the year in the given date. 292 * @param month the month in the given date. 293 * Month is 0-based. e.g., 0 for January. 294 * @param day the day-in-month of the given date. 295 * @param dayOfWeek the day-of-week of the given date. 296 * @param milliseconds the millis in day in <em>standard</em> local time. 297 * @return the offset to add *to* GMT to get local time. 298 * @stable ICU 2.0 299 */ getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds)300 abstract public int getOffset(int era, int year, int month, int day, 301 int dayOfWeek, int milliseconds); 302 303 304 /** 305 * Returns the offset of this time zone from UTC at the specified 306 * date. If Daylight Saving Time is in effect at the specified 307 * date, the offset value is adjusted with the amount of daylight 308 * saving. 309 * 310 * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT 311 * @return the amount of time in milliseconds to add to UTC to get local time. 312 * 313 * @see Calendar#ZONE_OFFSET 314 * @see Calendar#DST_OFFSET 315 * @see #getOffset(long, boolean, int[]) 316 * @stable ICU 2.8 317 */ getOffset(long date)318 public int getOffset(long date) { 319 int[] result = new int[2]; 320 getOffset(date, false, result); 321 return result[0]+result[1]; 322 } 323 324 /** 325 * Returns the time zone raw and GMT offset for the given moment 326 * in time. Upon return, local-millis = GMT-millis + rawOffset + 327 * dstOffset. All computations are performed in the proleptic 328 * Gregorian calendar. The default implementation in the TimeZone 329 * class delegates to the 8-argument getOffset(). 330 * 331 * @param date moment in time for which to return offsets, in 332 * units of milliseconds from January 1, 1970 0:00 GMT, either GMT 333 * time or local wall time, depending on `local'. 334 * @param local if true, `date' is local wall time; otherwise it 335 * is in GMT time. 336 * @param offsets output parameter to receive the raw offset, that 337 * is, the offset not including DST adjustments, in offsets[0], 338 * and the DST offset, that is, the offset to be added to 339 * `rawOffset' to obtain the total offset between local and GMT 340 * time, in offsets[1]. If DST is not in effect, the DST offset is 341 * zero; otherwise it is a positive value, typically one hour. 342 * 343 * @stable ICU 2.8 344 */ getOffset(long date, boolean local, int[] offsets)345 public void getOffset(long date, boolean local, int[] offsets) { 346 offsets[0] = getRawOffset(); 347 if (!local) { 348 date += offsets[0]; // now in local standard millis 349 } 350 351 // When local == true, date might not be in local standard 352 // millis. getOffset taking 6 parameters used here assume 353 // the given time in day is local standard time. 354 // At STD->DST transition, there is a range of time which 355 // does not exist. When 'date' is in this time range 356 // (and local == true), this method interprets the specified 357 // local time as DST. At DST->STD transition, there is a 358 // range of time which occurs twice. In this case, this 359 // method interprets the specified local time as STD. 360 // To support the behavior above, we need to call getOffset 361 // (with 6 args) twice when local == true and DST is 362 // detected in the initial call. 363 int fields[] = new int[6]; 364 for (int pass = 0; ; pass++) { 365 Grego.timeToFields(date, fields); 366 offsets[1] = getOffset(GregorianCalendar.AD, 367 fields[0], fields[1], fields[2], 368 fields[3], fields[5]) - offsets[0]; 369 370 if (pass != 0 || !local || offsets[1] == 0) { 371 break; 372 } 373 // adjust to local standard millis 374 date -= offsets[1]; 375 } 376 } 377 378 /** 379 * Sets the base time zone offset to GMT. 380 * This is the offset to add *to* UTC to get local time. 381 * @param offsetMillis the given base time zone offset to GMT. 382 * @stable ICU 2.0 383 */ setRawOffset(int offsetMillis)384 abstract public void setRawOffset(int offsetMillis); 385 386 /** 387 * Gets unmodified offset, NOT modified in case of daylight savings. 388 * This is the offset to add *to* UTC to get local time. 389 * @return the unmodified offset to add *to* UTC to get local time. 390 * @stable ICU 2.0 391 */ getRawOffset()392 abstract public int getRawOffset(); 393 394 /** 395 * Gets the ID of this time zone. 396 * @return the ID of this time zone. 397 * @stable ICU 2.0 398 */ getID()399 public String getID() { 400 return ID; 401 } 402 403 /** 404 * Sets the time zone ID. This does not change any other data in 405 * the time zone object. 406 * @param ID the new time zone ID. 407 * @stable ICU 2.0 408 */ setID(String ID)409 public void setID(String ID) { 410 if (ID == null) { 411 throw new NullPointerException(); 412 } 413 if (isFrozen()) { 414 throw new UnsupportedOperationException("Attempt to modify a frozen TimeZone instance."); 415 } 416 this.ID = ID; 417 } 418 419 /** 420 * Returns a name of this time zone suitable for presentation to the user 421 * in the default <code>DISPLAY</code> locale. 422 * This method returns the long generic name. 423 * If the display name is not available for the locale, 424 * a fallback based on the country, city, or time zone id will be used. 425 * @return the human-readable name of this time zone in the default locale. 426 * @see Category#DISPLAY 427 * @stable ICU 2.0 428 */ getDisplayName()429 public final String getDisplayName() { 430 return _getDisplayName(LONG_GENERIC, false, ULocale.getDefault(Category.DISPLAY)); 431 } 432 433 /** 434 * Returns a name of this time zone suitable for presentation to the user 435 * in the specified locale. 436 * This method returns the long generic name. 437 * If the display name is not available for the locale, 438 * a fallback based on the country, city, or time zone id will be used. 439 * @param locale the locale in which to supply the display name. 440 * @return the human-readable name of this time zone in the given locale 441 * or in the default locale if the given locale is not recognized. 442 * @stable ICU 2.0 443 */ getDisplayName(Locale locale)444 public final String getDisplayName(Locale locale) { 445 return _getDisplayName(LONG_GENERIC, false, ULocale.forLocale(locale)); 446 } 447 448 /** 449 * Returns a name of this time zone suitable for presentation to the user 450 * in the specified locale. 451 * This method returns the long name, not including daylight savings. 452 * If the display name is not available for the locale, 453 * a fallback based on the country, city, or time zone id will be used. 454 * @param locale the ulocale in which to supply the display name. 455 * @return the human-readable name of this time zone in the given locale 456 * or in the default ulocale if the given ulocale is not recognized. 457 * @stable ICU 3.2 458 */ getDisplayName(ULocale locale)459 public final String getDisplayName(ULocale locale) { 460 return _getDisplayName(LONG_GENERIC, false, locale); 461 } 462 463 /** 464 * Returns a name of this time zone suitable for presentation to the user 465 * in the default <code>DISPLAY</code> locale. 466 * If the display name is not available for the locale, 467 * then this method returns a string in the localized GMT offset format 468 * such as <code>GMT[+-]HH:mm</code>. 469 * @param daylight if true, return the daylight savings name. 470 * @param style the output style of the display name. Valid styles are 471 * <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>, 472 * <code>LONG_GENERIC</code>, <code>SHORT_GMT</code>, <code>LONG_GMT</code>, 473 * <code>SHORT_COMMONLY_USED</code> or <code>GENERIC_LOCATION</code>. 474 * @return the human-readable name of this time zone in the default locale. 475 * @see Category#DISPLAY 476 * @stable ICU 2.0 477 */ getDisplayName(boolean daylight, int style)478 public final String getDisplayName(boolean daylight, int style) { 479 return getDisplayName(daylight, style, ULocale.getDefault(Category.DISPLAY)); 480 } 481 482 /** 483 * Returns a name of this time zone suitable for presentation to the user 484 * in the specified locale. 485 * If the display name is not available for the locale, 486 * then this method returns a string in the localized GMT offset format 487 * such as <code>GMT[+-]HH:mm</code>. 488 * @param daylight if true, return the daylight savings name. 489 * @param style the output style of the display name. Valid styles are 490 * <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>, 491 * <code>LONG_GENERIC</code>, <code>SHORT_GMT</code>, <code>LONG_GMT</code>, 492 * <code>SHORT_COMMONLY_USED</code> or <code>GENERIC_LOCATION</code>. 493 * @param locale the locale in which to supply the display name. 494 * @return the human-readable name of this time zone in the given locale 495 * or in the default locale if the given locale is not recognized. 496 * @exception IllegalArgumentException style is invalid. 497 * @stable ICU 2.0 498 */ getDisplayName(boolean daylight, int style, Locale locale)499 public String getDisplayName(boolean daylight, int style, Locale locale) { 500 return getDisplayName(daylight, style, ULocale.forLocale(locale)); 501 } 502 503 /** 504 * Returns a name of this time zone suitable for presentation to the user 505 * in the specified locale. 506 * If the display name is not available for the locale, 507 * then this method returns a string in the localized GMT offset format 508 * such as <code>GMT[+-]HH:mm</code>. 509 * @param daylight if true, return the daylight savings name. 510 * @param style the output style of the display name. Valid styles are 511 * <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>, 512 * <code>LONG_GENERIC</code>, <code>SHORT_GMT</code>, <code>LONG_GMT</code>, 513 * <code>SHORT_COMMONLY_USED</code> or <code>GENERIC_LOCATION</code>. 514 * @param locale the locale in which to supply the display name. 515 * @return the human-readable name of this time zone in the given locale 516 * or in the default locale if the given locale is not recognized. 517 * @exception IllegalArgumentException style is invalid. 518 * @stable ICU 3.2 519 */ getDisplayName(boolean daylight, int style, ULocale locale)520 public String getDisplayName(boolean daylight, int style, ULocale locale) { 521 if (style < SHORT || style > GENERIC_LOCATION) { 522 throw new IllegalArgumentException("Illegal style: " + style); 523 } 524 525 return _getDisplayName(style, daylight, locale); 526 } 527 528 /** 529 * internal version (which is called by public APIs) accepts 530 * SHORT, LONG, SHORT_GENERIC, LONG_GENERIC, SHORT_GMT, LONG_GMT, 531 * SHORT_COMMONLY_USED and GENERIC_LOCATION. 532 */ _getDisplayName(int style, boolean daylight, ULocale locale)533 private String _getDisplayName(int style, boolean daylight, ULocale locale) { 534 if (locale == null) { 535 throw new NullPointerException("locale is null"); 536 } 537 538 String result = null; 539 540 if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) { 541 // Generic format 542 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale); 543 long date = System.currentTimeMillis(); 544 Output<TimeType> timeType = new Output<TimeType>(TimeType.UNKNOWN); 545 546 switch (style) { 547 case GENERIC_LOCATION: 548 result = tzfmt.format(Style.GENERIC_LOCATION, this, date, timeType); 549 break; 550 case LONG_GENERIC: 551 result = tzfmt.format(Style.GENERIC_LONG, this, date, timeType); 552 break; 553 case SHORT_GENERIC: 554 result = tzfmt.format(Style.GENERIC_SHORT, this, date, timeType); 555 break; 556 } 557 558 // Generic format many use Localized GMT as the final fallback. 559 // When Localized GMT format is used, the result might not be 560 // appropriate for the requested daylight value. 561 if (daylight && timeType.value == TimeType.STANDARD || 562 !daylight && timeType.value == TimeType.DAYLIGHT) { 563 int offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset(); 564 result = (style == SHORT_GENERIC) ? 565 tzfmt.formatOffsetShortLocalizedGMT(offset) : tzfmt.formatOffsetLocalizedGMT(offset); 566 } 567 568 } else if (style == LONG_GMT || style == SHORT_GMT) { 569 // Offset format 570 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale); 571 int offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 572 switch (style) { 573 case LONG_GMT: 574 result = tzfmt.formatOffsetLocalizedGMT(offset); 575 break; 576 case SHORT_GMT: 577 result = tzfmt.formatOffsetISO8601Basic(offset, false, false, false); 578 break; 579 } 580 } else { 581 // Specific format 582 assert(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED); 583 584 // Gets the name directly from TimeZoneNames 585 long date = System.currentTimeMillis(); 586 TimeZoneNames tznames = TimeZoneNames.getInstance(locale); 587 NameType nameType = null; 588 switch (style) { 589 case LONG: 590 nameType = daylight ? NameType.LONG_DAYLIGHT : NameType.LONG_STANDARD; 591 break; 592 case SHORT: 593 case SHORT_COMMONLY_USED: 594 nameType = daylight ? NameType.SHORT_DAYLIGHT : NameType.SHORT_STANDARD; 595 break; 596 } 597 result = tznames.getDisplayName(ZoneMeta.getCanonicalCLDRID(this), nameType, date); 598 if (result == null) { 599 // Fallback to localized GMT 600 TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale); 601 int offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset(); 602 result = (style == LONG) ? 603 tzfmt.formatOffsetLocalizedGMT(offset) : tzfmt.formatOffsetShortLocalizedGMT(offset); 604 } 605 } 606 assert(result != null); 607 608 return result; 609 } 610 611 /** 612 * Returns the amount of time to be added to local standard time 613 * to get local wall clock time. 614 * <p> 615 * The default implementation always returns 3600000 milliseconds 616 * (i.e., one hour) if this time zone observes Daylight Saving 617 * Time. Otherwise, 0 (zero) is returned. 618 * <p> 619 * If an underlying TimeZone implementation subclass supports 620 * historical Daylight Saving Time changes, this method returns 621 * the known latest daylight saving value. 622 * 623 * @return the amount of saving time in milliseconds 624 * @stable ICU 2.8 625 */ getDSTSavings()626 public int getDSTSavings() { 627 if (useDaylightTime()) { 628 return 3600000; 629 } 630 return 0; 631 } 632 633 /** 634 * Queries if this time zone uses daylight savings time. 635 * @return true if this time zone uses daylight savings time, 636 * false, otherwise. 637 * <p><strong>Note:</strong>The default implementation of 638 * ICU TimeZone uses the tz database, which supports historic 639 * rule changes, for system time zones. With the implementation, 640 * there are time zones that used daylight savings time in the 641 * past, but no longer used currently. For example, Asia/Tokyo has 642 * never used daylight savings time since 1951. Most clients would 643 * expect that this method to return <code>false</code> for such case. 644 * The default implementation of this method returns <code>true</code> 645 * when the time zone uses daylight savings time in the current 646 * (Gregorian) calendar year. 647 * @stable ICU 2.0 648 */ useDaylightTime()649 abstract public boolean useDaylightTime(); 650 651 /** 652 * Queries if this time zone is in daylight saving time or will observe 653 * daylight saving time at any future time. 654 * <p>The default implementation in this class returns <code>true</code> if {@link #useDaylightTime()} 655 * or {@link #inDaylightTime(Date) inDaylightTime(new Date())} returns <code>true</code>. 656 * <p> 657 * <strong>Note:</strong> This method was added for JDK compatibility support. 658 * The JDK's <code>useDaylightTime()</code> only checks the last known rule(s), therefore 659 * it may return false even the zone observes daylight saving time currently. JDK added 660 * <code>observesDaylightTime()</code> to resolve the issue. In ICU, {@link #useDaylightTime()} 661 * works differently. The ICU implementation checks if the zone uses daylight saving time 662 * in the current calendar year. Therefore, it will never return <code>false</code> if 663 * daylight saving time is currently used. 664 * <p> 665 * ICU's TimeZone subclass implementations override this method to support the same behavior 666 * with JDK's <code>observesDaylightSavingTime()</code>. Unlike {@link #useDaylightTime()}, 667 * the implementation does not take past daylight saving time into account, so 668 * that this method may return <code>false</code> even when {@link #useDaylightTime()} returns 669 * <code>true</code>. 670 * 671 * @return <code>true</code> if this time zone is in daylight saving time or will observe 672 * daylight saving time at any future time. 673 * @see #useDaylightTime 674 * @stable ICU 49 675 */ observesDaylightTime()676 public boolean observesDaylightTime() { 677 return useDaylightTime() || inDaylightTime(new Date()); 678 } 679 680 /** 681 * Queries if the given date is in daylight savings time in 682 * this time zone. 683 * @param date the given Date. 684 * @return true if the given date is in daylight savings time, 685 * false, otherwise. 686 * @stable ICU 2.0 687 */ inDaylightTime(Date date)688 abstract public boolean inDaylightTime(Date date); 689 690 /** 691 * Gets the <code>TimeZone</code> for the given ID. 692 * 693 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", 694 * or a custom ID such as "GMT-8:00". Note that the support of abbreviations, 695 * such as "PST", is for JDK 1.1.x compatibility only and full names should be used. 696 * 697 * @return the specified <code>TimeZone</code>, or a mutable clone of the UNKNOWN_ZONE 698 * if the given ID cannot be understood or if the given ID is "Etc/Unknown". 699 * @see #UNKNOWN_ZONE 700 * @stable ICU 2.0 701 */ getTimeZone(String ID)702 public static TimeZone getTimeZone(String ID) { 703 return getTimeZone(ID, TZ_IMPL, false); 704 } 705 706 /** 707 * Gets the <code>TimeZone</code> for the given ID. The instance of <code>TimeZone</code> 708 * returned by this method is immutable. Any methods mutate the instance({@link #setID(String)}, 709 * {@link #setRawOffset(int)}) will throw <code>UnsupportedOperationException</code> upon its 710 * invocation. 711 * 712 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", 713 * or a custom ID such as "GMT-8:00". Note that the support of abbreviations, 714 * such as "PST", is for JDK 1.1.x compatibility only and full names should be used. 715 * 716 * @return the specified <code>TimeZone</code>, or the UNKNOWN_ZONE 717 * if the given ID cannot be understood. 718 * @see #UNKNOWN_ZONE 719 * @stable ICU 49 720 */ getFrozenTimeZone(String ID)721 public static TimeZone getFrozenTimeZone(String ID) { 722 return getTimeZone(ID, TZ_IMPL, true); 723 } 724 725 /** 726 * Gets the <code>TimeZone</code> for the given ID and the timezone type. 727 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", or a 728 * custom ID such as "GMT-8:00". Note that the support of abbreviations, such as 729 * "PST", is for JDK 1.1.x compatibility only and full names should be used. 730 * @param type Time zone type, either <code>TIMEZONE_ICU</code> or 731 * <code>TIMEZONE_JDK</code>. 732 * @return the specified <code>TimeZone</code>, or a mutable clone of the UNKNOWN_ZONE if the given ID 733 * cannot be understood or if the given ID is "Etc/Unknown". 734 * @see #UNKNOWN_ZONE 735 * @stable ICU 4.0 736 */ getTimeZone(String ID, int type)737 public static TimeZone getTimeZone(String ID, int type) { 738 return getTimeZone(ID, type, false); 739 } 740 741 /** 742 * Gets the <code>TimeZone</code> for the given ID and the timezone type. 743 * @param ID time zone ID 744 * @param type time zone implementation type, TIMEZONE_JDK or TIMEZONE_ICU 745 * @param frozen specify if the returned object can be frozen 746 * @return the specified <code>TimeZone</code> or UNKNOWN_ZONE if the given ID 747 * cannot be understood. 748 */ getTimeZone(String ID, int type, boolean frozen)749 private static TimeZone getTimeZone(String ID, int type, boolean frozen) { 750 TimeZone result; 751 if (type == TIMEZONE_JDK) { 752 result = JavaTimeZone.createTimeZone(ID); 753 if (result != null) { 754 return frozen ? result.freeze() : result; 755 } 756 } else { 757 /* We first try to lookup the zone ID in our system list. If this 758 * fails, we try to parse it as a custom string GMT[+-]HH:mm. If 759 * all else fails, we return GMT, which is probably not what the 760 * user wants, but at least is a functioning TimeZone object. 761 * 762 * We cannot return NULL, because that would break compatibility 763 * with the JDK. 764 */ 765 if(ID==null){ 766 throw new NullPointerException(); 767 } 768 result = ZoneMeta.getSystemTimeZone(ID); 769 } 770 771 if (result == null) { 772 result = ZoneMeta.getCustomTimeZone(ID); 773 } 774 775 if (result == null) { 776 LOGGER.fine("\"" +ID + "\" is a bogus id so timezone is falling back to Etc/Unknown(GMT)."); 777 result = UNKNOWN_ZONE; 778 } 779 780 return frozen ? result : result.cloneAsThawed(); 781 } 782 783 /** 784 * Sets the default time zone type used by <code>getTimeZone</code>. 785 * @param type time zone type, either <code>TIMEZONE_ICU</code> or 786 * <code>TIMEZONE_JDK</code>. 787 * @stable ICU 4.0 788 */ setDefaultTimeZoneType(int type)789 public static synchronized void setDefaultTimeZoneType(int type) { 790 if (type != TIMEZONE_ICU && type != TIMEZONE_JDK) { 791 throw new IllegalArgumentException("Invalid timezone type"); 792 } 793 TZ_IMPL = type; 794 } 795 796 /** 797 * {@icu} Returns the default time zone type currently used. 798 * @return The default time zone type, either <code>TIMEZONE_ICU</code> or 799 * <code>TIMEZONE_JDK</code>. 800 * @stable ICU 4.0 801 */ getDefaultTimeZoneType()802 public static int getDefaultTimeZoneType() { 803 return TZ_IMPL; 804 } 805 806 /** 807 * {@icu} Returns a set of time zone ID strings with the given filter conditions. 808 * <p><b>Note:</b>A <code>Set</code> returned by this method is 809 * immutable. 810 * @param zoneType The system time zone type. 811 * @param region The ISO 3166 two-letter country code or UN M.49 three-digit area code. 812 * When null, no filtering done by region. 813 * @param rawOffset An offset from GMT in milliseconds, ignoring the effect of daylight savings 814 * time, if any. When null, no filtering done by zone offset. 815 * @return an immutable set of system time zone IDs. 816 * @see SystemTimeZoneType 817 * 818 * @stable ICU 4.8 819 */ getAvailableIDs(SystemTimeZoneType zoneType, String region, Integer rawOffset)820 public static Set<String> getAvailableIDs(SystemTimeZoneType zoneType, 821 String region, Integer rawOffset) { 822 return ZoneMeta.getAvailableIDs(zoneType, region, rawOffset); 823 } 824 825 /** 826 * Return a new String array containing all system TimeZone IDs 827 * with the given raw offset from GMT. These IDs may be passed to 828 * <code>get()</code> to construct the corresponding TimeZone 829 * object. 830 * @param rawOffset the offset in milliseconds from GMT 831 * @return an array of IDs for system TimeZones with the given 832 * raw offset. If there are none, return a zero-length array. 833 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 834 * 835 * @stable ICU 2.0 836 */ getAvailableIDs(int rawOffset)837 public static String[] getAvailableIDs(int rawOffset) { 838 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, null, Integer.valueOf(rawOffset)); 839 return ids.toArray(new String[0]); 840 } 841 842 /** 843 * Return a new String array containing all system TimeZone IDs 844 * associated with the given country. These IDs may be passed to 845 * <code>get()</code> to construct the corresponding TimeZone 846 * object. 847 * @param country a two-letter ISO 3166 country code, or <code>null</code> 848 * to return zones not associated with any country 849 * @return an array of IDs for system TimeZones in the given 850 * country. If there are none, return a zero-length array. 851 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 852 * 853 * @stable ICU 2.0 854 */ getAvailableIDs(String country)855 public static String[] getAvailableIDs(String country) { 856 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, country, null); 857 return ids.toArray(new String[0]); 858 } 859 860 /** 861 * Return a new String array containing all system TimeZone IDs. 862 * These IDs (and only these IDs) may be passed to 863 * <code>get()</code> to construct the corresponding TimeZone 864 * object. 865 * @return an array of all system TimeZone IDs 866 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 867 * 868 * @stable ICU 2.0 869 */ getAvailableIDs()870 public static String[] getAvailableIDs() { 871 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, null, null); 872 return ids.toArray(new String[0]); 873 } 874 875 /** 876 * {@icu} Returns the number of IDs in the equivalency group that 877 * includes the given ID. An equivalency group contains zones 878 * that have the same GMT offset and rules. 879 * 880 * <p>The returned count includes the given ID; it is always >= 1 881 * for valid IDs. The given ID must be a system time zone. If it 882 * is not, returns zero. 883 * @param id a system time zone ID 884 * @return the number of zones in the equivalency group containing 885 * 'id', or zero if 'id' is not a valid system ID 886 * @see #getEquivalentID 887 * @stable ICU 2.0 888 */ countEquivalentIDs(String id)889 public static int countEquivalentIDs(String id) { 890 return ZoneMeta.countEquivalentIDs(id); 891 } 892 893 /** 894 * Returns an ID in the equivalency group that 895 * includes the given ID. An equivalency group contains zones 896 * that have the same GMT offset and rules. 897 * 898 * <p>The given index must be in the range 0..n-1, where n is the 899 * value returned by <code>countEquivalentIDs(id)</code>. For 900 * some value of 'index', the returned value will be equal to the 901 * given id. If the given id is not a valid system time zone, or 902 * if 'index' is out of range, then returns an empty string. 903 * @param id a system time zone ID 904 * @param index a value from 0 to n-1, where n is the value 905 * returned by <code>countEquivalentIDs(id)</code> 906 * @return the ID of the index-th zone in the equivalency group 907 * containing 'id', or an empty string if 'id' is not a valid 908 * system ID or 'index' is out of range 909 * @see #countEquivalentIDs 910 * @stable ICU 2.0 911 */ getEquivalentID(String id, int index)912 public static String getEquivalentID(String id, int index) { 913 return ZoneMeta.getEquivalentID(id, index); 914 } 915 916 /** 917 * Gets the default <code>TimeZone</code> for this host. 918 * The source of the default <code>TimeZone</code> 919 * may vary with implementation. 920 * @return a default <code>TimeZone</code>. 921 * @stable ICU 2.0 922 */ getDefault()923 public static TimeZone getDefault() { 924 if (defaultZone == null) { 925 synchronized(TimeZone.class) { 926 if (defaultZone == null) { 927 if (TZ_IMPL == TIMEZONE_JDK) { 928 defaultZone = new JavaTimeZone(); 929 } else { 930 java.util.TimeZone temp = java.util.TimeZone.getDefault(); 931 defaultZone = getFrozenTimeZone(temp.getID()); 932 } 933 } 934 } 935 } 936 return defaultZone.cloneAsThawed(); 937 } 938 939 /** 940 * Sets the <code>TimeZone</code> that is 941 * returned by the <code>getDefault</code> method. If <code>zone</code> 942 * is null, reset the default to the value it had originally when the 943 * VM first started. 944 * @param tz the new default time zone 945 * @stable ICU 2.0 946 */ setDefault(TimeZone tz)947 public static synchronized void setDefault(TimeZone tz) { 948 defaultZone = tz; 949 java.util.TimeZone jdkZone = null; 950 if (defaultZone instanceof JavaTimeZone) { 951 jdkZone = ((JavaTimeZone)defaultZone).unwrap(); 952 } else { 953 // Keep java.util.TimeZone default in sync so java.util.Date 954 // can interoperate with com.ibm.icu.util classes. 955 956 if (tz != null) { 957 if (tz instanceof com.ibm.icu.impl.OlsonTimeZone) { 958 // Because of the lack of APIs supporting historic 959 // zone offset/dst saving in JDK TimeZone, 960 // wrapping ICU TimeZone with JDK TimeZone will 961 // cause historic offset calculation in Calendar/Date. 962 // JDK calendar implementation calls getRawOffset() and 963 // getDSTSavings() when the instance of JDK TimeZone 964 // is not an instance of JDK internal TimeZone subclass 965 // (sun.util.calendar.ZoneInfo). Ticket#6459 966 String icuID = tz.getID(); 967 jdkZone = java.util.TimeZone.getTimeZone(icuID); 968 if (!icuID.equals(jdkZone.getID())) { 969 // If the ID was unknown, retry with the canonicalized 970 // ID instead. This will ensure that JDK 1.1.x 971 // compatibility IDs supported by ICU (but not 972 // necessarily supported by the platform) work. 973 // Ticket#11483 974 icuID = getCanonicalID(icuID); 975 jdkZone = java.util.TimeZone.getTimeZone(icuID); 976 if (!icuID.equals(jdkZone.getID())) { 977 // JDK does not know the ID.. 978 jdkZone = null; 979 } 980 } 981 } 982 if (jdkZone == null) { 983 jdkZone = TimeZoneAdapter.wrap(tz); 984 } 985 } 986 } 987 java.util.TimeZone.setDefault(jdkZone); 988 } 989 990 /** 991 * Returns true if this zone has the same rule and offset as another zone. 992 * That is, if this zone differs only in ID, if at all. Returns false 993 * if the other zone is null. 994 * @param other the <code>TimeZone</code> object to be compared with 995 * @return true if the other zone is not null and is the same as this one, 996 * with the possible exception of the ID 997 * @stable ICU 2.0 998 */ hasSameRules(TimeZone other)999 public boolean hasSameRules(TimeZone other) { 1000 return other != null && 1001 getRawOffset() == other.getRawOffset() && 1002 useDaylightTime() == other.useDaylightTime(); 1003 } 1004 1005 /** 1006 * Overrides clone. 1007 * @stable ICU 2.0 1008 */ clone()1009 public Object clone() { 1010 if (isFrozen()) { 1011 return this; 1012 } 1013 return cloneAsThawed(); 1014 } 1015 1016 /** 1017 * Overrides equals. 1018 * @stable ICU 3.6 1019 */ equals(Object obj)1020 public boolean equals(Object obj){ 1021 if (this == obj) return true; 1022 if (obj == null || getClass() != obj.getClass()) return false; 1023 return (ID.equals(((TimeZone)obj).ID)); 1024 } 1025 1026 /** 1027 * Overrides hashCode. 1028 * @stable ICU 3.6 1029 */ hashCode()1030 public int hashCode(){ 1031 return ID.hashCode(); 1032 } 1033 1034 /** 1035 * {@icu} Returns the time zone data version currently used by ICU. 1036 * 1037 * @return the version string, such as "2007f" 1038 * @throws MissingResourceException if ICU time zone resource bundle 1039 * is missing or the version information is not available. 1040 * 1041 * @stable ICU 3.8 1042 */ getTZDataVersion()1043 public static String getTZDataVersion() { 1044 // The implementation had been moved to VersionInfo. 1045 return VersionInfo.getTZDataVersion(); 1046 } 1047 1048 /** 1049 * {@icu} Returns the canonical system time zone ID or the normalized 1050 * custom time zone ID for the given time zone ID. 1051 * @param id The input time zone ID to be canonicalized. 1052 * @return The canonical system time zone ID or the custom time zone ID 1053 * in normalized format for the given time zone ID. When the given time zone ID 1054 * is neither a known system time zone ID nor a valid custom time zone ID, 1055 * null is returned. 1056 * @stable ICU 4.0 1057 */ getCanonicalID(String id)1058 public static String getCanonicalID(String id) { 1059 return getCanonicalID(id, null); 1060 } 1061 1062 /** 1063 * {@icu} Returns the canonical system time zone ID or the normalized 1064 * custom time zone ID for the given time zone ID. 1065 * @param id The input time zone ID to be canonicalized. 1066 * @param isSystemID When non-null boolean array is specified and 1067 * the given ID is a known system time zone ID, true is set to <code>isSystemID[0]</code> 1068 * @return The canonical system time zone ID or the custom time zone ID 1069 * in normalized format for the given time zone ID. When the given time zone ID 1070 * is neither a known system time zone ID nor a valid custom time zone ID, 1071 * null is returned. 1072 * @stable ICU 4.0 1073 */ getCanonicalID(String id, boolean[] isSystemID)1074 public static String getCanonicalID(String id, boolean[] isSystemID) { 1075 String canonicalID = null; 1076 boolean systemTzid = false; 1077 if (id != null && id.length() != 0) { 1078 if (id.equals(TimeZone.UNKNOWN_ZONE_ID)) { 1079 // special case - Etc/Unknown is a canonical ID, but not system ID 1080 canonicalID = TimeZone.UNKNOWN_ZONE_ID; 1081 systemTzid = false; 1082 } else { 1083 canonicalID = ZoneMeta.getCanonicalCLDRID(id); 1084 if (canonicalID != null) { 1085 systemTzid = true; 1086 } else { 1087 canonicalID = ZoneMeta.getCustomID(id); 1088 } 1089 } 1090 } 1091 if (isSystemID != null) { 1092 isSystemID[0] = systemTzid; 1093 } 1094 return canonicalID; 1095 } 1096 1097 /** 1098 * {@icu} Returns the region code associated with the given 1099 * system time zone ID. The region code is either ISO 3166 1100 * 2-letter country code or UN M.49 3-digit area code. 1101 * When the time zone is not associated with a specific location, 1102 * for example - "Etc/UTC", "EST5EDT", then this method returns 1103 * "001" (UN M.49 area code for World). 1104 * @param id the system time zone ID. 1105 * @return the region code associated with the given 1106 * system time zone ID. 1107 * @throws IllegalArgumentException if <code>id</code> is not a known system ID. 1108 * @see #getAvailableIDs(String) 1109 * 1110 * @stable ICU 4.8 1111 */ getRegion(String id)1112 public static String getRegion(String id) { 1113 String region = null; 1114 // "Etc/Unknown" is not a system time zone ID, 1115 // but in the zone data. 1116 if (!id.equals(UNKNOWN_ZONE_ID)) { 1117 region = ZoneMeta.getRegion(id); 1118 } 1119 if (region == null) { 1120 // unknown id 1121 throw new IllegalArgumentException("Unknown system zone id: " + id); 1122 } 1123 return region; 1124 } 1125 1126 /** 1127 * {@icu} Converts a system time zone ID to an equivalent Windows time zone ID. For example, 1128 * Windows time zone ID "Pacific Standard Time" is returned for input "America/Los_Angeles". 1129 * 1130 * <p>There are system time zones that cannot be mapped to Windows zones. When the input 1131 * system time zone ID is unknown or unmappable to a Windows time zone, then this 1132 * method returns <code>null</code>. 1133 * 1134 * <p>This implementation utilizes <a href="http://unicode.org/cldr/charts/supplemental/zone_tzid.html"> 1135 * Zone-Tzid mapping data<a>. The mapping data is updated time to time. To get the latest changes, 1136 * please read the ICU user guide section <a href="http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data"> 1137 * Updating the Time Zone Data</a>. 1138 * 1139 * @param id A system time zone ID 1140 * @return A Windows time zone ID mapped from the input system time zone ID, 1141 * or <code>null</code> when the input ID is unknown or unmappable. 1142 * @see #getIDForWindowsID(String, String) 1143 * 1144 * @stable ICU 52 1145 */ getWindowsID(String id)1146 public static String getWindowsID(String id) { 1147 // canonicalize the input ID 1148 boolean[] isSystemID = {false}; 1149 id = getCanonicalID(id, isSystemID); 1150 if (!isSystemID[0]) { 1151 // mapping data is only applicable to tz database IDs 1152 return null; 1153 } 1154 1155 UResourceBundle top = UResourceBundle.getBundleInstance( 1156 ICUResourceBundle.ICU_BASE_NAME, "windowsZones", ICUResourceBundle.ICU_DATA_CLASS_LOADER); 1157 UResourceBundle mapTimezones = top.get("mapTimezones"); 1158 1159 UResourceBundleIterator resitr = mapTimezones.getIterator(); 1160 while (resitr.hasNext()) { 1161 UResourceBundle winzone = resitr.next(); 1162 if (winzone.getType() != UResourceBundle.TABLE) { 1163 continue; 1164 } 1165 UResourceBundleIterator rgitr = winzone.getIterator(); 1166 while (rgitr.hasNext()) { 1167 UResourceBundle regionalData = rgitr.next(); 1168 if (regionalData.getType() != UResourceBundle.STRING) { 1169 continue; 1170 } 1171 String[] tzids = regionalData.getString().split(" "); 1172 for (String tzid : tzids) { 1173 if (tzid.equals(id)) { 1174 return winzone.getKey(); 1175 } 1176 } 1177 } 1178 } 1179 1180 return null; 1181 } 1182 1183 /** 1184 * {@icu} Converts a Windows time zone ID to an equivalent system time zone ID 1185 * for a region. For example, system time zone ID "America/Los_Angeles" is returned 1186 * for input Windows ID "Pacific Standard Time" and region "US" (or <code>null</code>), 1187 * "America/Vancouver" is returned for the same Windows ID "Pacific Standard Time" and 1188 * region "CA". 1189 * 1190 * <p>Not all Windows time zones can be mapped to system time zones. When the input 1191 * Windows time zone ID is unknown or unmappable to a system time zone, then this 1192 * method returns <code>null</code>. 1193 * 1194 * <p>This implementation utilizes <a href="http://unicode.org/cldr/charts/supplemental/zone_tzid.html"> 1195 * Zone-Tzid mapping data<a>. The mapping data is updated time to time. To get the latest changes, 1196 * please read the ICU user guide section <a href="http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data"> 1197 * Updating the Time Zone Data</a>. 1198 * 1199 * @param winid A Windows time zone ID 1200 * @param region A region code, or <code>null</code> if no regional preference. 1201 * @return A system time zone ID mapped from the input Windows time zone ID, 1202 * or <code>null</code> when the input ID is unknown or unmappable. 1203 * @see #getWindowsID(String) 1204 * 1205 * @stable ICU 52 1206 */ getIDForWindowsID(String winid, String region)1207 public static String getIDForWindowsID(String winid, String region) { 1208 String id = null; 1209 1210 UResourceBundle top = UResourceBundle.getBundleInstance( 1211 ICUResourceBundle.ICU_BASE_NAME, "windowsZones", ICUResourceBundle.ICU_DATA_CLASS_LOADER); 1212 UResourceBundle mapTimezones = top.get("mapTimezones"); 1213 1214 try { 1215 UResourceBundle zones = mapTimezones.get(winid); 1216 if (region != null) { 1217 try { 1218 id = zones.getString(region); 1219 if (id != null) { 1220 // first ID delimited by space is the default one 1221 int endIdx = id.indexOf(' '); 1222 if (endIdx > 0) { 1223 id = id.substring(0, endIdx); 1224 } 1225 } 1226 } catch (MissingResourceException e) { 1227 // no explicit region mapping found 1228 } 1229 } 1230 if (id == null) { 1231 id = zones.getString("001"); 1232 } 1233 } catch (MissingResourceException e) { 1234 // no mapping data found 1235 } 1236 1237 return id; 1238 } 1239 1240 // Freezable stuffs 1241 1242 /** 1243 * {@inheritDoc} 1244 * @stable ICU 49 1245 */ isFrozen()1246 public boolean isFrozen() { 1247 return false; 1248 } 1249 1250 /** 1251 * {@inheritDoc} 1252 * @stable ICU 49 1253 */ freeze()1254 public TimeZone freeze() { 1255 throw new UnsupportedOperationException("Needs to be implemented by the subclass."); 1256 } 1257 1258 /** 1259 * {@inheritDoc} 1260 * @stable ICU 49 1261 */ cloneAsThawed()1262 public TimeZone cloneAsThawed() { 1263 try { 1264 TimeZone other = (TimeZone) super.clone(); 1265 return other; 1266 } catch (CloneNotSupportedException e) { 1267 throw new ICUCloneNotSupportedException(e); 1268 } 1269 } 1270 1271 // =======================privates=============================== 1272 1273 /** 1274 * The string identifier of this <code>TimeZone</code>. This is a 1275 * programmatic identifier used internally to look up <code>TimeZone</code> 1276 * objects from the system table and also to map them to their localized 1277 * display names. <code>ID</code> values are unique in the system 1278 * table but may not be for dynamically created zones. 1279 * @serial 1280 */ 1281 private String ID; 1282 1283 /** 1284 * The default time zone, or null if not set. 1285 */ 1286 private static volatile TimeZone defaultZone = null; 1287 1288 /** 1289 * TimeZone implementation type 1290 */ 1291 private static int TZ_IMPL = TIMEZONE_ICU; 1292 1293 /** 1294 * TimeZone implementation type initialization 1295 */ 1296 private static final String TZIMPL_CONFIG_KEY = "com.ibm.icu.util.TimeZone.DefaultTimeZoneType"; 1297 private static final String TZIMPL_CONFIG_ICU = "ICU"; 1298 private static final String TZIMPL_CONFIG_JDK = "JDK"; 1299 1300 static { 1301 String type = ICUConfig.get(TZIMPL_CONFIG_KEY, TZIMPL_CONFIG_ICU); 1302 if (type.equalsIgnoreCase(TZIMPL_CONFIG_JDK)) { 1303 TZ_IMPL = TIMEZONE_JDK; 1304 } 1305 } 1306 1307 /* 1308 * ConstantZone is a private TimeZone subclass dedicated for the two TimeZone class 1309 * constants - TimeZone.GMT_ZONE and TimeZone.UNKNOWN_ZONE. Previously, these zones 1310 * are instances of SimpleTimeZone. However, when the SimpleTimeZone constructor and 1311 * TimeZone's static methods (such as TimeZone.getDefault()) are called from multiple 1312 * threads at the same time, it causes a deadlock by TimeZone's static initializer 1313 * and SimpleTimeZone's static initializer. To avoid this issue, these TimeZone 1314 * constants (GMT/UNKNOWN) must be implemented by a class not visible from users. 1315 * See ticket#11343. 1316 */ 1317 private static final class ConstantZone extends TimeZone { 1318 private static final long serialVersionUID = 1L; 1319 1320 private int rawOffset; 1321 ConstantZone(int rawOffset, String ID)1322 private ConstantZone(int rawOffset, String ID) { 1323 super(ID); 1324 this.rawOffset = rawOffset; 1325 } 1326 1327 @Override getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds)1328 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) { 1329 return rawOffset; 1330 } 1331 1332 @Override setRawOffset(int offsetMillis)1333 public void setRawOffset(int offsetMillis) { 1334 if (isFrozen()) { 1335 throw new UnsupportedOperationException("Attempt to modify a frozen TimeZone instance."); 1336 } 1337 rawOffset = offsetMillis; 1338 } 1339 1340 @Override getRawOffset()1341 public int getRawOffset() { 1342 return rawOffset; 1343 } 1344 1345 @Override useDaylightTime()1346 public boolean useDaylightTime() { 1347 return false; 1348 } 1349 1350 @Override inDaylightTime(Date date)1351 public boolean inDaylightTime(Date date) { 1352 return false; 1353 } 1354 1355 private volatile transient boolean isFrozen = false; 1356 1357 @Override isFrozen()1358 public boolean isFrozen() { 1359 return isFrozen; 1360 } 1361 1362 @Override freeze()1363 public TimeZone freeze() { 1364 isFrozen = true; 1365 return this; 1366 } 1367 1368 @Override cloneAsThawed()1369 public TimeZone cloneAsThawed() { 1370 ConstantZone tz = (ConstantZone)super.cloneAsThawed(); 1371 tz.isFrozen = false; 1372 return tz; 1373 } 1374 } 1375 } 1376 1377 //eof 1378