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