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