1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3/* 4 * @(#)TimeZone.java 1.51 00/01/19 5 * 6 * Copyright (C) 1996-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 */ 9 10package com.ibm.icu.util; 11 12import java.io.Serializable; 13import java.util.Date; 14import java.util.Locale; 15import java.util.MissingResourceException; 16import java.util.Set; 17import java.util.logging.Logger; 18 19import com.ibm.icu.impl.Grego; 20import com.ibm.icu.impl.ICUConfig; 21import com.ibm.icu.impl.ICUData; 22import com.ibm.icu.impl.ICUResourceBundle; 23import com.ibm.icu.impl.JavaTimeZone; 24import com.ibm.icu.impl.TimeZoneAdapter; 25import com.ibm.icu.impl.ZoneMeta; 26import com.ibm.icu.text.TimeZoneFormat; 27import com.ibm.icu.text.TimeZoneFormat.Style; 28import com.ibm.icu.text.TimeZoneFormat.TimeType; 29import com.ibm.icu.text.TimeZoneNames; 30import com.ibm.icu.text.TimeZoneNames.NameType; 31import com.ibm.icu.util.ULocale.Category; 32 33/** 34 * {@icuenhanced java.util.TimeZone}.{@icu _usage_} 35 * 36 * <p><code>TimeZone</code> represents a time zone offset, and also computes daylight 37 * savings. 38 * 39 * <p>Typically, you get a <code>TimeZone</code> using {@link #getDefault()} 40 * which creates a <code>TimeZone</code> based on the time zone where the program 41 * is running. For example, for a program running in Japan, <code>getDefault</code> 42 * creates a <code>TimeZone</code> object based on Japanese Standard Time. 43 * 44 * <p>You can also get a <code>TimeZone</code> using {@link #getTimeZone(String)} 45 * along with a time zone ID. For instance, the time zone ID for the 46 * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a 47 * U.S. Pacific Time <code>TimeZone</code> object with: 48 * 49 * <blockquote> 50 * <pre> 51 * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); 52 * </pre> 53 * </blockquote> 54 * You can use the {@link #getAvailableIDs()} method to iterate through 55 * all the supported time zone IDs, or getCanonicalID method to check 56 * if a time zone ID is supported or not. You can then choose a 57 * supported ID to get a <code>TimeZone</code>. 58 * If the time zone you want is not represented by one of the 59 * supported IDs, then you can create a custom time zone ID with 60 * the following syntax: 61 * 62 * <blockquote> 63 * <pre> 64 * GMT[+|-]hh[[:]mm] 65 * </pre> 66 * </blockquote> 67 * 68 * For example, you might specify GMT+14:00 as a custom 69 * time zone ID. The <code>TimeZone</code> that is returned 70 * when you specify a custom time zone ID uses the specified 71 * offset from GMT(=UTC) and does not observe daylight saving 72 * time. For example, you might specify GMT+14:00 as a custom 73 * time zone ID to create a TimeZone representing 14 hours ahead 74 * of GMT (with no daylight saving time). In addition, 75 * <code>getCanonicalID</code> can also be used to 76 * normalize a custom time zone ID. 77 * 78 * <p>For compatibility with JDK 1.1.x, some other three-letter time zone IDs 79 * (such as "PST", "CTT", "AST") are also supported. However, <strong>their 80 * use is deprecated</strong> because the same abbreviation is often used 81 * for multiple time zones (for example, "CST" could be U.S. "Central Standard 82 * Time" and "China Standard Time"), and the Java platform can then only 83 * recognize one of them. 84 * 85 * <p><strong>Note:</strong> Starting from ICU4J 4.0, you can optionally choose 86 * JDK <code>TimeZone</code> as the time zone implementation. The TimeZone factory 87 * method <code>getTimeZone</code> creates an instance of ICU's own <code>TimeZone</code> 88 * subclass by default. If you want to use the JDK implementation always, you can 89 * set the default time zone implementation type by the new method 90 * <code>setDefaultTimeZoneType</code>. Alternatively, you can change the initial 91 * default implementation type by setting a property below. 92 * 93 * <blockquote> 94 * <pre> 95 * # 96 * # The default TimeZone implementation type used by the ICU TimeZone 97 * # factory method. [ ICU | JDK ] 98 * # 99 * com.ibm.icu.util.TimeZone.DefaultTimeZoneType = ICU 100 * </pre> 101 * </blockquote> 102 * 103 * <p>This property is included in ICUConfig.properties in com.ibm.icu package. When the 104 * <code>TimeZone</code> class is loaded, the initialization code checks if the property 105 * <code>com.ibm.icu.util.TimeZone.DefaultTimeZoneType=xxx</code> is defined by the system 106 * properties. If not available, then it loads ICUConfig.properties to get the default 107 * time zone implementation type. The property setting is only used for the initial 108 * default value and you can change the default type by calling 109 * <code>setDefaultTimeZoneType</code> at runtime. 110 * 111 * @see Calendar 112 * @see GregorianCalendar 113 * @see SimpleTimeZone 114 * @author Mark Davis, Deborah Goldsmith, Chen-Lieh Huang, Alan Liu 115 * @stable ICU 2.0 116 */ 117abstract public class TimeZone implements Serializable, Cloneable, Freezable<TimeZone> { 118 /** 119 * Logger instance for this class 120 */ 121 private static final Logger LOGGER = Logger.getLogger("com.ibm.icu.util.TimeZone"); 122 123 // using serialver from jdk1.4.2_05 124 private static final long serialVersionUID = -744942128318337471L; 125 126 /** 127 * Default constructor. (For invocation by subclass constructors, 128 * typically implicit.) 129 * @stable ICU 2.8 130 */ 131 public TimeZone() { 132 } 133 134 /** 135 * Constructing a TimeZone with the given time zone ID. 136 * @param ID the time zone ID. 137 * @internal 138 * @deprecated This API is ICU internal only. 139 */ 140 @Deprecated 141 protected TimeZone(String ID) { 142 if (ID == null) { 143 throw new NullPointerException(); 144 } 145 this.ID = ID; 146 } 147 148 /** 149 * {@icu} A time zone implementation type indicating ICU's own TimeZone used by 150 * <code>getTimeZone</code>, <code>setDefaultTimeZoneType</code> 151 * and <code>getDefaultTimeZoneType</code>. 152 * @stable ICU 4.0 153 */ 154 public static final int TIMEZONE_ICU = 0; 155 /** 156 * {@icu} A time zone implementation type indicating the {@link java.util.TimeZone} 157 * used by <code>getTimeZone</code>, <code>setDefaultTimeZoneType</code> 158 * and <code>getDefaultTimeZoneType</code>. 159 * @stable ICU 4.0 160 */ 161 public static final int TIMEZONE_JDK = 1; 162 163 /** 164 * A style specifier for <code>getDisplayName()</code> indicating 165 * a short name, such as "PST." 166 * @see #LONG 167 * @stable ICU 2.0 168 */ 169 public static final int SHORT = 0; 170 171 /** 172 * A style specifier for <code>getDisplayName()</code> indicating 173 * a long name, such as "Pacific Standard Time." 174 * @see #SHORT 175 * @stable ICU 2.0 176 */ 177 public static final int LONG = 1; 178 179 /** 180 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 181 * a short generic name, such as "PT." 182 * @see #LONG_GENERIC 183 * @stable ICU 4.4 184 */ 185 public static final int SHORT_GENERIC = 2; 186 187 /** 188 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 189 * a long generic name, such as "Pacific Time." 190 * @see #SHORT_GENERIC 191 * @stable ICU 4.4 192 */ 193 public static final int LONG_GENERIC = 3; 194 195 /** 196 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 197 * a short name derived from the timezone's offset, such as "-0800." 198 * @see #LONG_GMT 199 * @stable ICU 4.4 200 */ 201 public static final int SHORT_GMT = 4; 202 203 /** 204 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 205 * a long name derived from the timezone's offset, such as "GMT-08:00." 206 * @see #SHORT_GMT 207 * @stable ICU 4.4 208 */ 209 public static final int LONG_GMT = 5; 210 211 /** 212 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 213 * a short name derived from the timezone's short standard or daylight 214 * timezone name ignoring commonlyUsed, such as "PDT." 215 * @stable ICU 4.4 216 */ 217 218 public static final int SHORT_COMMONLY_USED = 6; 219 220 /** 221 * {@icu} A style specifier for <code>getDisplayName()</code> indicating 222 * a long name derived from the timezone's fallback name, such as 223 * "United States (Los Angeles)." 224 * @stable ICU 4.4 225 */ 226 public static final int GENERIC_LOCATION = 7; 227 228 /** 229 * {@icu} The time zone ID reserved for unknown time zone. 230 * @see #getTimeZone(String) 231 * 232 * @stable ICU 4.8 233 */ 234 public static final String UNKNOWN_ZONE_ID = "Etc/Unknown"; 235 236 /** 237 * The canonical ID for GMT(UTC) time zone. 238 */ 239 static final String GMT_ZONE_ID = "Etc/GMT"; 240 241 /** 242 * {@icu} The immutable (frozen) "unknown" time zone. 243 * It behaves like the GMT/UTC time zone but has the UNKNOWN_ZONE_ID = "Etc/Unknown". 244 * {@link TimeZone#getTimeZone(String)} returns a mutable clone of this 245 * time zone if the input ID is not recognized. 246 * 247 * @see #UNKNOWN_ZONE_ID 248 * @see #getTimeZone(String) 249 * 250 * @stable ICU 49 251 */ 252 public static final TimeZone UNKNOWN_ZONE = new ConstantZone(0, UNKNOWN_ZONE_ID).freeze(); 253 254 /** 255 * {@icu} The immutable GMT (=UTC) time zone. Its ID is "Etc/GMT". 256 * 257 * @stable ICU 49 258 */ 259 public static final TimeZone GMT_ZONE = new ConstantZone(0, GMT_ZONE_ID).freeze(); 260 261 /** 262 * {@icu} System time zone type constants used by filtering zones in 263 * {@link TimeZone#getAvailableIDs(SystemTimeZoneType, String, Integer)} 264 * 265 * @stable ICU 4.8 266 */ 267 public enum SystemTimeZoneType { 268 /** 269 * Any system zones. 270 * @stable ICU 4.8 271 */ 272 ANY, 273 274 /** 275 * Canonical system zones. 276 * @stable ICU 4.8 277 */ 278 CANONICAL, 279 280 /** 281 * Canonical system zones associated with actual locations. 282 * @stable ICU 4.8 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 {@link java.util.TimeZone} compatibility 658 * support. The {@link java.util.TimeZone#useDaylightTime()} method only checks the last known 659 * rule(s), therefore it may return false even the zone observes daylight saving time currently. 660 * {@link java.util.TimeZone} added <code>observesDaylightTime()</code> to resolve the issue. 661 * In ICU, {@link #useDaylightTime()} works differently. The ICU implementation checks if the 662 * zone uses daylight saving time in the current calendar year. Therefore, it will never return 663 * <code>false</code> if daylight saving time is currently used. 664 * <p> 665 * ICU's TimeZone subclass implementations override this method to support the same behavior 666 * with {@link java.util.TimeZone#observesDaylightTime()}. 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 */ 676 @SuppressWarnings("javadoc") // java.util.TimeZone#observesDaylightTime() is introduced in Java 7 677 public boolean observesDaylightTime() { 678 return useDaylightTime() || inDaylightTime(new Date()); 679 } 680 681 /** 682 * Queries if the given date is in daylight savings time in 683 * this time zone. 684 * @param date the given Date. 685 * @return true if the given date is in daylight savings time, 686 * false, otherwise. 687 * @stable ICU 2.0 688 */ 689 abstract public boolean inDaylightTime(Date date); 690 691 /** 692 * Gets the <code>TimeZone</code> for the given ID. 693 * 694 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", 695 * or a custom ID such as "GMT-8:00". Note that the support of abbreviations, 696 * such as "PST", is for JDK 1.1.x compatibility only and full names should be used. 697 * 698 * @return the specified <code>TimeZone</code>, or a mutable clone of the UNKNOWN_ZONE 699 * if the given ID cannot be understood or if the given ID is "Etc/Unknown". 700 * @see #UNKNOWN_ZONE 701 * @stable ICU 2.0 702 */ 703 public static TimeZone getTimeZone(String ID) { 704 return getTimeZone(ID, TZ_IMPL, false); 705 } 706 707 /** 708 * Gets the <code>TimeZone</code> for the given ID. The instance of <code>TimeZone</code> 709 * returned by this method is immutable. Any methods mutate the instance({@link #setID(String)}, 710 * {@link #setRawOffset(int)}) will throw <code>UnsupportedOperationException</code> upon its 711 * invocation. 712 * 713 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", 714 * or a custom ID such as "GMT-8:00". Note that the support of abbreviations, 715 * such as "PST", is for JDK 1.1.x compatibility only and full names should be used. 716 * 717 * @return the specified <code>TimeZone</code>, or the UNKNOWN_ZONE 718 * if the given ID cannot be understood. 719 * @see #UNKNOWN_ZONE 720 * @stable ICU 49 721 */ 722 public static TimeZone getFrozenTimeZone(String ID) { 723 return getTimeZone(ID, TZ_IMPL, true); 724 } 725 726 /** 727 * Gets the <code>TimeZone</code> for the given ID and the timezone type. 728 * @param ID the ID for a <code>TimeZone</code>, such as "America/Los_Angeles", or a 729 * custom ID such as "GMT-8:00". Note that the support of abbreviations, such as 730 * "PST", is for JDK 1.1.x compatibility only and full names should be used. 731 * @param type Time zone type, either <code>TIMEZONE_ICU</code> or 732 * <code>TIMEZONE_JDK</code>. 733 * @return the specified <code>TimeZone</code>, or a mutable clone of the UNKNOWN_ZONE if the given ID 734 * cannot be understood or if the given ID is "Etc/Unknown". 735 * @see #UNKNOWN_ZONE 736 * @stable ICU 4.0 737 */ 738 public static TimeZone getTimeZone(String ID, int type) { 739 return getTimeZone(ID, type, false); 740 } 741 742 /** 743 * Gets the <code>TimeZone</code> for the given ID and the timezone type. 744 * @param id time zone ID 745 * @param type time zone implementation type, TIMEZONE_JDK or TIMEZONE_ICU 746 * @param frozen specify if the returned object can be frozen 747 * @return the specified <code>TimeZone</code> or UNKNOWN_ZONE if the given ID 748 * cannot be understood. 749 */ 750 private static TimeZone getTimeZone(String id, int type, boolean frozen) { 751 TimeZone result; 752 if (type == TIMEZONE_JDK) { 753 result = JavaTimeZone.createTimeZone(id); 754 if (result != null) { 755 return frozen ? result.freeze() : result; 756 } 757 result = getFrozenICUTimeZone(id, false); 758 } else { 759 result = getFrozenICUTimeZone(id, true); 760 } 761 if (result == null) { 762 LOGGER.fine("\"" +id + "\" is a bogus id so timezone is falling back to Etc/Unknown(GMT)."); 763 result = UNKNOWN_ZONE; 764 } 765 return frozen ? result : result.cloneAsThawed(); 766 } 767 768 /** 769 * Returns a frozen ICU type TimeZone object given a time zone ID. 770 * @param id the time zone ID 771 * @param trySystem if true tries the system time zones first otherwise skip to the 772 * custom time zones. 773 * @return the frozen ICU TimeZone or null if one could not be created. 774 */ 775 static BasicTimeZone getFrozenICUTimeZone(String id, boolean trySystem) { 776 BasicTimeZone result = null; 777 if (trySystem) { 778 result = ZoneMeta.getSystemTimeZone(id); 779 } 780 if (result == null) { 781 result = ZoneMeta.getCustomTimeZone(id); 782 } 783 return result; 784 } 785 786 /** 787 * Sets the default time zone type used by <code>getTimeZone</code>. 788 * @param type time zone type, either <code>TIMEZONE_ICU</code> or 789 * <code>TIMEZONE_JDK</code>. 790 * @stable ICU 4.0 791 */ 792 public static synchronized void setDefaultTimeZoneType(int type) { 793 if (type != TIMEZONE_ICU && type != TIMEZONE_JDK) { 794 throw new IllegalArgumentException("Invalid timezone type"); 795 } 796 TZ_IMPL = type; 797 } 798 799 /** 800 * {@icu} Returns the default time zone type currently used. 801 * @return The default time zone type, either <code>TIMEZONE_ICU</code> or 802 * <code>TIMEZONE_JDK</code>. 803 * @stable ICU 4.0 804 */ 805 public static int getDefaultTimeZoneType() { 806 return TZ_IMPL; 807 } 808 809 /** 810 * {@icu} Returns a set of time zone ID strings with the given filter conditions. 811 * <p><b>Note:</b>A <code>Set</code> returned by this method is 812 * immutable. 813 * @param zoneType The system time zone type. 814 * @param region The ISO 3166 two-letter country code or UN M.49 three-digit area code. 815 * When null, no filtering done by region. 816 * @param rawOffset An offset from GMT in milliseconds, ignoring the effect of daylight savings 817 * time, if any. When null, no filtering done by zone offset. 818 * @return an immutable set of system time zone IDs. 819 * @see SystemTimeZoneType 820 * 821 * @stable ICU 4.8 822 */ 823 public static Set<String> getAvailableIDs(SystemTimeZoneType zoneType, 824 String region, Integer rawOffset) { 825 return ZoneMeta.getAvailableIDs(zoneType, region, rawOffset); 826 } 827 828 /** 829 * Return a new String array containing all system TimeZone IDs 830 * with the given raw offset from GMT. These IDs may be passed to 831 * <code>get()</code> to construct the corresponding TimeZone 832 * object. 833 * @param rawOffset the offset in milliseconds from GMT 834 * @return an array of IDs for system TimeZones with the given 835 * raw offset. If there are none, return a zero-length array. 836 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 837 * 838 * @stable ICU 2.0 839 */ 840 public static String[] getAvailableIDs(int rawOffset) { 841 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, null, Integer.valueOf(rawOffset)); 842 return ids.toArray(new String[0]); 843 } 844 845 /** 846 * Return a new String array containing all system TimeZone IDs 847 * associated with the given country. These IDs may be passed to 848 * <code>get()</code> to construct the corresponding TimeZone 849 * object. 850 * @param country a two-letter ISO 3166 country code, or <code>null</code> 851 * to return zones not associated with any country 852 * @return an array of IDs for system TimeZones in the given 853 * country. If there are none, return a zero-length array. 854 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 855 * 856 * @stable ICU 2.0 857 */ 858 public static String[] getAvailableIDs(String country) { 859 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, country, null); 860 return ids.toArray(new String[0]); 861 } 862 863 /** 864 * Return a new String array containing all system TimeZone IDs. 865 * These IDs (and only these IDs) may be passed to 866 * <code>get()</code> to construct the corresponding TimeZone 867 * object. 868 * @return an array of all system TimeZone IDs 869 * @see #getAvailableIDs(SystemTimeZoneType, String, Integer) 870 * 871 * @stable ICU 2.0 872 */ 873 public static String[] getAvailableIDs() { 874 Set<String> ids = getAvailableIDs(SystemTimeZoneType.ANY, null, null); 875 return ids.toArray(new String[0]); 876 } 877 878 /** 879 * {@icu} Returns the number of IDs in the equivalency group that 880 * includes the given ID. An equivalency group contains zones 881 * that have the same GMT offset and rules. 882 * 883 * <p>The returned count includes the given ID; it is always >= 1 884 * for valid IDs. The given ID must be a system time zone. If it 885 * is not, returns zero. 886 * @param id a system time zone ID 887 * @return the number of zones in the equivalency group containing 888 * 'id', or zero if 'id' is not a valid system ID 889 * @see #getEquivalentID 890 * @stable ICU 2.0 891 */ 892 public static int countEquivalentIDs(String id) { 893 return ZoneMeta.countEquivalentIDs(id); 894 } 895 896 /** 897 * Returns an ID in the equivalency group that 898 * includes the given ID. An equivalency group contains zones 899 * that have the same GMT offset and rules. 900 * 901 * <p>The given index must be in the range 0..n-1, where n is the 902 * value returned by <code>countEquivalentIDs(id)</code>. For 903 * some value of 'index', the returned value will be equal to the 904 * given id. If the given id is not a valid system time zone, or 905 * if 'index' is out of range, then returns an empty string. 906 * @param id a system time zone ID 907 * @param index a value from 0 to n-1, where n is the value 908 * returned by <code>countEquivalentIDs(id)</code> 909 * @return the ID of the index-th zone in the equivalency group 910 * containing 'id', or an empty string if 'id' is not a valid 911 * system ID or 'index' is out of range 912 * @see #countEquivalentIDs 913 * @stable ICU 2.0 914 */ 915 public static String getEquivalentID(String id, int index) { 916 return ZoneMeta.getEquivalentID(id, index); 917 } 918 919 /** 920 * Gets the default <code>TimeZone</code> for this host. 921 * The source of the default <code>TimeZone</code> 922 * may vary with implementation. 923 * @return a default <code>TimeZone</code>. 924 * @stable ICU 2.0 925 */ 926 public static TimeZone getDefault() { 927 // Copy the reference to the current defaultZone, 928 // so it won't be affected by setDefault(). 929 TimeZone tmpDefaultZone = defaultZone; 930 931 if (tmpDefaultZone == null) { 932 synchronized (java.util.TimeZone.class) { 933 synchronized(TimeZone.class) { 934 tmpDefaultZone = defaultZone; 935 if (tmpDefaultZone == null) { 936 if (TZ_IMPL == TIMEZONE_JDK) { 937 tmpDefaultZone = new JavaTimeZone(); 938 } else { 939 java.util.TimeZone temp = java.util.TimeZone.getDefault(); 940 tmpDefaultZone = getFrozenTimeZone(temp.getID()); 941 } 942 defaultZone = tmpDefaultZone; 943 } 944 } 945 } 946 } 947 948 return tmpDefaultZone.cloneAsThawed(); 949 } 950 951 /** 952 * Sets the <code>TimeZone</code> that is returned by the <code>getDefault</code> 953 * method. This method also sets a Java TimeZone equivalent to the input <code>tz</code> 954 * as the JVM's default time zone if not null. If <code>tz</code> is null, next 955 * {@link #getDefault()} method invocation will reset the default time zone 956 * synchronized with the JVM's default at that time. 957 * 958 * @param tz the new default time zone 959 * @stable ICU 2.0 960 */ 961 public static synchronized void setDefault(TimeZone tz) { 962 // Set default ICU time zone, used by #getDefault() 963 setICUDefault(tz); 964 965 if (tz != null) { 966 // Keep java.util.TimeZone default in sync so java.util.Date 967 // can interoperate with com.ibm.icu.util classes. 968 java.util.TimeZone jdkZone = null; 969 if (tz instanceof JavaTimeZone) { 970 jdkZone = ((JavaTimeZone)tz).unwrap(); 971 } else if (tz instanceof com.ibm.icu.impl.OlsonTimeZone) { 972 // Because of the lack of APIs supporting historic 973 // zone offset/dst saving in JDK TimeZone, 974 // wrapping ICU TimeZone with JDK TimeZone will 975 // cause historic offset calculation in Calendar/Date. 976 // JDK calendar implementation calls getRawOffset() and 977 // getDSTSavings() when the instance of JDK TimeZone 978 // is not an instance of JDK internal TimeZone subclass 979 // (sun.util.calendar.ZoneInfo). Ticket#6459 980 String icuID = tz.getID(); 981 jdkZone = java.util.TimeZone.getTimeZone(icuID); 982 if (!icuID.equals(jdkZone.getID())) { 983 // If the ID was unknown, retry with the canonicalized 984 // ID instead. This will ensure that JDK 1.1.x 985 // compatibility IDs supported by ICU (but not 986 // necessarily supported by the platform) work. 987 // Ticket#11483 988 icuID = getCanonicalID(icuID); 989 jdkZone = java.util.TimeZone.getTimeZone(icuID); 990 if (!icuID.equals(jdkZone.getID())) { 991 // JDK does not know the ID.. 992 jdkZone = null; 993 } 994 } 995 } 996 if (jdkZone == null) { 997 jdkZone = TimeZoneAdapter.wrap(tz); 998 } 999 java.util.TimeZone.setDefault(jdkZone); 1000 } 1001 } 1002 1003 /** 1004 * Sets the <code>TimeZone</code> that is returned by the <code>getDefault</code> 1005 * method. If <code>tz</code> is null, next {@link #getDefault()} method invocation 1006 * will reset the default time zone synchronized with the JVM's default at that time. 1007 * Unlike {@link #setDefault(TimeZone)}, this method does not change the JVM's 1008 * default time zone. 1009 * 1010 * @param tz the new default time zone 1011 * @internal 1012 * @deprecated This API is ICU internal only. 1013 */ 1014 @Deprecated 1015 public static synchronized void setICUDefault(TimeZone tz) { 1016 if (tz == null) { 1017 defaultZone = null; 1018 } else if (tz.isFrozen()) { 1019 // No need to create a defensive copy 1020 defaultZone = tz; 1021 } else { 1022 // Creates a defensive copy and freeze it 1023 defaultZone = ((TimeZone)tz.clone()).freeze(); 1024 } 1025 } 1026 1027 /** 1028 * Returns true if this zone has the same rule and offset as another zone. 1029 * That is, if this zone differs only in ID, if at all. Returns false 1030 * if the other zone is null. 1031 * @param other the <code>TimeZone</code> object to be compared with 1032 * @return true if the other zone is not null and is the same as this one, 1033 * with the possible exception of the ID 1034 * @stable ICU 2.0 1035 */ 1036 public boolean hasSameRules(TimeZone other) { 1037 return other != null && 1038 getRawOffset() == other.getRawOffset() && 1039 useDaylightTime() == other.useDaylightTime(); 1040 } 1041 1042 /** 1043 * Overrides clone. 1044 * @stable ICU 2.0 1045 */ 1046 @Override 1047 public Object clone() { 1048 if (isFrozen()) { 1049 return this; 1050 } 1051 return cloneAsThawed(); 1052 } 1053 1054 /** 1055 * Overrides equals. 1056 * @stable ICU 3.6 1057 */ 1058 @Override 1059 public boolean equals(Object obj){ 1060 if (this == obj) return true; 1061 if (obj == null || getClass() != obj.getClass()) return false; 1062 return (ID.equals(((TimeZone)obj).ID)); 1063 } 1064 1065 /** 1066 * Overrides hashCode. 1067 * @stable ICU 3.6 1068 */ 1069 @Override 1070 public int hashCode(){ 1071 return ID.hashCode(); 1072 } 1073 1074 /** 1075 * {@icu} Returns the time zone data version currently used by ICU. 1076 * 1077 * @return the version string, such as "2007f" 1078 * @throws MissingResourceException if ICU time zone resource bundle 1079 * is missing or the version information is not available. 1080 * 1081 * @stable ICU 3.8 1082 */ 1083 public static String getTZDataVersion() { 1084 // The implementation had been moved to VersionInfo. 1085 return VersionInfo.getTZDataVersion(); 1086 } 1087 1088 /** 1089 * {@icu} Returns the canonical system time zone ID or the normalized 1090 * custom time zone ID for the given time zone ID. 1091 * @param id The input time zone ID to be canonicalized. 1092 * @return The canonical system time zone ID or the custom time zone ID 1093 * in normalized format for the given time zone ID. When the given time zone ID 1094 * is neither a known system time zone ID nor a valid custom time zone ID, 1095 * null is returned. 1096 * @stable ICU 4.0 1097 */ 1098 public static String getCanonicalID(String id) { 1099 return getCanonicalID(id, null); 1100 } 1101 1102 /** 1103 * {@icu} Returns the canonical system time zone ID or the normalized 1104 * custom time zone ID for the given time zone ID. 1105 * @param id The input time zone ID to be canonicalized. 1106 * @param isSystemID When non-null boolean array is specified and 1107 * the given ID is a known system time zone ID, true is set to <code>isSystemID[0]</code> 1108 * @return The canonical system time zone ID or the custom time zone ID 1109 * in normalized format for the given time zone ID. When the given time zone ID 1110 * is neither a known system time zone ID nor a valid custom time zone ID, 1111 * null is returned. 1112 * @stable ICU 4.0 1113 */ 1114 public static String getCanonicalID(String id, boolean[] isSystemID) { 1115 String canonicalID = null; 1116 boolean systemTzid = false; 1117 if (id != null && id.length() != 0) { 1118 if (id.equals(TimeZone.UNKNOWN_ZONE_ID)) { 1119 // special case - Etc/Unknown is a canonical ID, but not system ID 1120 canonicalID = TimeZone.UNKNOWN_ZONE_ID; 1121 systemTzid = false; 1122 } else { 1123 canonicalID = ZoneMeta.getCanonicalCLDRID(id); 1124 if (canonicalID != null) { 1125 systemTzid = true; 1126 } else { 1127 canonicalID = ZoneMeta.getCustomID(id); 1128 } 1129 } 1130 } 1131 if (isSystemID != null) { 1132 isSystemID[0] = systemTzid; 1133 } 1134 return canonicalID; 1135 } 1136 1137 /** 1138 * {@icu} Returns the region code associated with the given 1139 * system time zone ID. The region code is either ISO 3166 1140 * 2-letter country code or UN M.49 3-digit area code. 1141 * When the time zone is not associated with a specific location, 1142 * for example - "Etc/UTC", "EST5EDT", then this method returns 1143 * "001" (UN M.49 area code for World). 1144 * @param id the system time zone ID. 1145 * @return the region code associated with the given 1146 * system time zone ID. 1147 * @throws IllegalArgumentException if <code>id</code> is not a known system ID. 1148 * @see #getAvailableIDs(String) 1149 * 1150 * @stable ICU 4.8 1151 */ 1152 public static String getRegion(String id) { 1153 String region = null; 1154 // "Etc/Unknown" is not a system time zone ID, 1155 // but in the zone data. 1156 if (!id.equals(UNKNOWN_ZONE_ID)) { 1157 region = ZoneMeta.getRegion(id); 1158 } 1159 if (region == null) { 1160 // unknown id 1161 throw new IllegalArgumentException("Unknown system zone id: " + id); 1162 } 1163 return region; 1164 } 1165 1166 /** 1167 * {@icu} Converts a system time zone ID to an equivalent Windows time zone ID. For example, 1168 * Windows time zone ID "Pacific Standard Time" is returned for input "America/Los_Angeles". 1169 * 1170 * <p>There are system time zones that cannot be mapped to Windows zones. When the input 1171 * system time zone ID is unknown or unmappable to a Windows time zone, then this 1172 * method returns <code>null</code>. 1173 * 1174 * <p>This implementation utilizes <a href="http://unicode.org/cldr/charts/supplemental/zone_tzid.html"> 1175 * Zone-Tzid mapping data</a>. The mapping data is updated time to time. To get the latest changes, 1176 * please read the ICU user guide section <a href="http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data"> 1177 * Updating the Time Zone Data</a>. 1178 * 1179 * @param id A system time zone ID 1180 * @return A Windows time zone ID mapped from the input system time zone ID, 1181 * or <code>null</code> when the input ID is unknown or unmappable. 1182 * @see #getIDForWindowsID(String, String) 1183 * 1184 * @stable ICU 52 1185 */ 1186 public static String getWindowsID(String id) { 1187 // canonicalize the input ID 1188 boolean[] isSystemID = {false}; 1189 id = getCanonicalID(id, isSystemID); 1190 if (!isSystemID[0]) { 1191 // mapping data is only applicable to tz database IDs 1192 return null; 1193 } 1194 1195 UResourceBundle top = UResourceBundle.getBundleInstance( 1196 ICUData.ICU_BASE_NAME, "windowsZones", ICUResourceBundle.ICU_DATA_CLASS_LOADER); 1197 UResourceBundle mapTimezones = top.get("mapTimezones"); 1198 1199 UResourceBundleIterator resitr = mapTimezones.getIterator(); 1200 while (resitr.hasNext()) { 1201 UResourceBundle winzone = resitr.next(); 1202 if (winzone.getType() != UResourceBundle.TABLE) { 1203 continue; 1204 } 1205 UResourceBundleIterator rgitr = winzone.getIterator(); 1206 while (rgitr.hasNext()) { 1207 UResourceBundle regionalData = rgitr.next(); 1208 if (regionalData.getType() != UResourceBundle.STRING) { 1209 continue; 1210 } 1211 String[] tzids = regionalData.getString().split(" "); 1212 for (String tzid : tzids) { 1213 if (tzid.equals(id)) { 1214 return winzone.getKey(); 1215 } 1216 } 1217 } 1218 } 1219 1220 return null; 1221 } 1222 1223 /** 1224 * {@icu} Converts a Windows time zone ID to an equivalent system time zone ID 1225 * for a region. For example, system time zone ID "America/Los_Angeles" is returned 1226 * for input Windows ID "Pacific Standard Time" and region "US" (or <code>null</code>), 1227 * "America/Vancouver" is returned for the same Windows ID "Pacific Standard Time" and 1228 * region "CA". 1229 * 1230 * <p>Not all Windows time zones can be mapped to system time zones. When the input 1231 * Windows time zone ID is unknown or unmappable to a system time zone, then this 1232 * method returns <code>null</code>. 1233 * 1234 * <p>This implementation utilizes <a href="http://unicode.org/cldr/charts/supplemental/zone_tzid.html"> 1235 * Zone-Tzid mapping data</a>. The mapping data is updated time to time. To get the latest changes, 1236 * please read the ICU user guide section <a href="http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data"> 1237 * Updating the Time Zone Data</a>. 1238 * 1239 * @param winid A Windows time zone ID 1240 * @param region A region code, or <code>null</code> if no regional preference. 1241 * @return A system time zone ID mapped from the input Windows time zone ID, 1242 * or <code>null</code> when the input ID is unknown or unmappable. 1243 * @see #getWindowsID(String) 1244 * 1245 * @stable ICU 52 1246 */ 1247 public static String getIDForWindowsID(String winid, String region) { 1248 String id = null; 1249 1250 UResourceBundle top = UResourceBundle.getBundleInstance( 1251 ICUData.ICU_BASE_NAME, "windowsZones", ICUResourceBundle.ICU_DATA_CLASS_LOADER); 1252 UResourceBundle mapTimezones = top.get("mapTimezones"); 1253 1254 try { 1255 UResourceBundle zones = mapTimezones.get(winid); 1256 if (region != null) { 1257 try { 1258 id = zones.getString(region); 1259 if (id != null) { 1260 // first ID delimited by space is the default one 1261 int endIdx = id.indexOf(' '); 1262 if (endIdx > 0) { 1263 id = id.substring(0, endIdx); 1264 } 1265 } 1266 } catch (MissingResourceException e) { 1267 // no explicit region mapping found 1268 } 1269 } 1270 if (id == null) { 1271 id = zones.getString("001"); 1272 } 1273 } catch (MissingResourceException e) { 1274 // no mapping data found 1275 } 1276 1277 return id; 1278 } 1279 1280 // Freezable stuffs 1281 1282 /** 1283 * {@inheritDoc} 1284 * @stable ICU 49 1285 */ 1286 @Override 1287 public boolean isFrozen() { 1288 return false; 1289 } 1290 1291 /** 1292 * {@inheritDoc} 1293 * @stable ICU 49 1294 */ 1295 @Override 1296 public TimeZone freeze() { 1297 throw new UnsupportedOperationException("Needs to be implemented by the subclass."); 1298 } 1299 1300 /** 1301 * {@inheritDoc} 1302 * @stable ICU 49 1303 */ 1304 @Override 1305 public TimeZone cloneAsThawed() { 1306 try { 1307 TimeZone other = (TimeZone) super.clone(); 1308 return other; 1309 } catch (CloneNotSupportedException e) { 1310 throw new ICUCloneNotSupportedException(e); 1311 } 1312 } 1313 1314 // =======================privates=============================== 1315 1316 /** 1317 * The string identifier of this <code>TimeZone</code>. This is a 1318 * programmatic identifier used internally to look up <code>TimeZone</code> 1319 * objects from the system table and also to map them to their localized 1320 * display names. <code>ID</code> values are unique in the system 1321 * table but may not be for dynamically created zones. 1322 * @serial 1323 */ 1324 private String ID; 1325 1326 /** 1327 * The default time zone, or null if not set. 1328 */ 1329 private static volatile TimeZone defaultZone = null; 1330 1331 /** 1332 * TimeZone implementation type 1333 */ 1334 private static int TZ_IMPL = TIMEZONE_ICU; 1335 1336 /** 1337 * TimeZone implementation type initialization 1338 */ 1339 private static final String TZIMPL_CONFIG_KEY = "com.ibm.icu.util.TimeZone.DefaultTimeZoneType"; 1340 private static final String TZIMPL_CONFIG_ICU = "ICU"; 1341 private static final String TZIMPL_CONFIG_JDK = "JDK"; 1342 1343 static { 1344 String type = ICUConfig.get(TZIMPL_CONFIG_KEY, TZIMPL_CONFIG_ICU); 1345 if (type.equalsIgnoreCase(TZIMPL_CONFIG_JDK)) { 1346 TZ_IMPL = TIMEZONE_JDK; 1347 } 1348 } 1349 1350 /* 1351 * ConstantZone is a private TimeZone subclass dedicated for the two TimeZone class 1352 * constants - TimeZone.GMT_ZONE and TimeZone.UNKNOWN_ZONE. Previously, these zones 1353 * are instances of SimpleTimeZone. However, when the SimpleTimeZone constructor and 1354 * TimeZone's static methods (such as TimeZone.getDefault()) are called from multiple 1355 * threads at the same time, it causes a deadlock by TimeZone's static initializer 1356 * and SimpleTimeZone's static initializer. To avoid this issue, these TimeZone 1357 * constants (GMT/UNKNOWN) must be implemented by a class not visible from users. 1358 * See ticket#11343. 1359 */ 1360 private static final class ConstantZone extends TimeZone { 1361 private static final long serialVersionUID = 1L; 1362 1363 private int rawOffset; 1364 1365 private ConstantZone(int rawOffset, String ID) { 1366 super(ID); 1367 this.rawOffset = rawOffset; 1368 } 1369 1370 @Override 1371 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) { 1372 return rawOffset; 1373 } 1374 1375 @Override 1376 public void setRawOffset(int offsetMillis) { 1377 if (isFrozen()) { 1378 throw new UnsupportedOperationException("Attempt to modify a frozen TimeZone instance."); 1379 } 1380 rawOffset = offsetMillis; 1381 } 1382 1383 @Override 1384 public int getRawOffset() { 1385 return rawOffset; 1386 } 1387 1388 @Override 1389 public boolean useDaylightTime() { 1390 return false; 1391 } 1392 1393 @Override 1394 public boolean inDaylightTime(Date date) { 1395 return false; 1396 } 1397 1398 private volatile transient boolean isFrozen = false; 1399 1400 @Override 1401 public boolean isFrozen() { 1402 return isFrozen; 1403 } 1404 1405 @Override 1406 public TimeZone freeze() { 1407 isFrozen = true; 1408 return this; 1409 } 1410 1411 @Override 1412 public TimeZone cloneAsThawed() { 1413 ConstantZone tz = (ConstantZone)super.cloneAsThawed(); 1414 tz.isFrozen = false; 1415 return tz; 1416 } 1417 } 1418} 1419 1420//eof 1421