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 &gt;= 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