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