TimeZone.java revision 9e78cee3f3edf84254174717f475605d712aad1c
1/*
2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 *   The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 *   Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.util;
40
41import java.io.IOException;
42import java.io.Serializable;
43import java.util.regex.Matcher;
44import java.util.regex.Pattern;
45import java.lang.ref.SoftReference;
46import java.security.AccessController;
47import java.security.PrivilegedAction;
48import java.util.concurrent.ConcurrentHashMap;
49import libcore.icu.TimeZoneNames;
50import libcore.io.IoUtils;
51import libcore.util.ZoneInfoDB;
52import sun.security.action.GetPropertyAction;
53import org.apache.harmony.luni.internal.util.TimezoneGetter;
54
55/**
56 * <code>TimeZone</code> represents a time zone offset, and also figures out daylight
57 * savings.
58 *
59 * <p>
60 * Typically, you get a <code>TimeZone</code> using <code>getDefault</code>
61 * which creates a <code>TimeZone</code> based on the time zone where the program
62 * is running. For example, for a program running in Japan, <code>getDefault</code>
63 * creates a <code>TimeZone</code> object based on Japanese Standard Time.
64 *
65 * <p>
66 * You can also get a <code>TimeZone</code> using <code>getTimeZone</code>
67 * along with a time zone ID. For instance, the time zone ID for the
68 * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a
69 * U.S. Pacific Time <code>TimeZone</code> object with:
70 * <blockquote><pre>
71 * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
72 * </pre></blockquote>
73 * You can use the <code>getAvailableIDs</code> method to iterate through
74 * all the supported time zone IDs. You can then choose a
75 * supported ID to get a <code>TimeZone</code>.
76 * If the time zone you want is not represented by one of the
77 * supported IDs, then a custom time zone ID can be specified to
78 * produce a TimeZone. The syntax of a custom time zone ID is:
79 *
80 * <blockquote><pre>
81 * <a name="CustomID"><i>CustomID:</i></a>
82 *         <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
83 *         <code>GMT</code> <i>Sign</i> <i>Hours</i> <i>Minutes</i>
84 *         <code>GMT</code> <i>Sign</i> <i>Hours</i>
85 * <i>Sign:</i> one of
86 *         <code>+ -</code>
87 * <i>Hours:</i>
88 *         <i>Digit</i>
89 *         <i>Digit</i> <i>Digit</i>
90 * <i>Minutes:</i>
91 *         <i>Digit</i> <i>Digit</i>
92 * <i>Digit:</i> one of
93 *         <code>0 1 2 3 4 5 6 7 8 9</code>
94 * </pre></blockquote>
95 *
96 * <i>Hours</i> must be between 0 to 23 and <i>Minutes</i> must be
97 * between 00 to 59.  For example, "GMT+10" and "GMT+0010" mean ten
98 * hours and ten minutes ahead of GMT, respectively.
99 * <p>
100 * The format is locale independent and digits must be taken from the
101 * Basic Latin block of the Unicode standard. No daylight saving time
102 * transition schedule can be specified with a custom time zone ID. If
103 * the specified string doesn't match the syntax, <code>"GMT"</code>
104 * is used.
105 * <p>
106 * When creating a <code>TimeZone</code>, the specified custom time
107 * zone ID is normalized in the following syntax:
108 * <blockquote><pre>
109 * <a name="NormalizedCustomID"><i>NormalizedCustomID:</i></a>
110 *         <code>GMT</code> <i>Sign</i> <i>TwoDigitHours</i> <code>:</code> <i>Minutes</i>
111 * <i>Sign:</i> one of
112 *         <code>+ -</code>
113 * <i>TwoDigitHours:</i>
114 *         <i>Digit</i> <i>Digit</i>
115 * <i>Minutes:</i>
116 *         <i>Digit</i> <i>Digit</i>
117 * <i>Digit:</i> one of
118 *         <code>0 1 2 3 4 5 6 7 8 9</code>
119 * </pre></blockquote>
120 * For example, TimeZone.getTimeZone("GMT-8").getID() returns "GMT-08:00".
121 *
122 * <h4>Three-letter time zone IDs</h4>
123 *
124 * For compatibility with JDK 1.1.x, some other three-letter time zone IDs
125 * (such as "PST", "CTT", "AST") are also supported. However, <strong>their
126 * use is deprecated</strong> because the same abbreviation is often used
127 * for multiple time zones (for example, "CST" could be U.S. "Central Standard
128 * Time" and "China Standard Time"), and the Java platform can then only
129 * recognize one of them.
130 *
131 *
132 * @see          Calendar
133 * @see          GregorianCalendar
134 * @see          SimpleTimeZone
135 * @author       Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
136 * @since        JDK1.1
137 */
138abstract public class TimeZone implements Serializable, Cloneable {
139    /**
140     * Sole constructor.  (For invocation by subclass constructors, typically
141     * implicit.)
142     */
143    public TimeZone() {
144    }
145
146    /**
147     * A style specifier for <code>getDisplayName()</code> indicating
148     * a short name, such as "PST."
149     * @see #LONG
150     * @since 1.2
151     */
152    public static final int SHORT = 0;
153
154    /**
155     * A style specifier for <code>getDisplayName()</code> indicating
156     * a long name, such as "Pacific Standard Time."
157     * @see #SHORT
158     * @since 1.2
159     */
160    public static final int LONG  = 1;
161
162    private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
163
164    // Proclaim serialization compatibility with JDK 1.1
165    static final long serialVersionUID = 3581463369166924961L;
166    private static final TimeZone GMT = new SimpleTimeZone(0, "GMT");
167    private static final TimeZone UTC = new SimpleTimeZone(0, "UTC");
168
169    /**
170     * Gets the time zone offset, for current date, modified in case of
171     * daylight savings. This is the offset to add to UTC to get local time.
172     * <p>
173     * This method returns a historically correct offset if an
174     * underlying <code>TimeZone</code> implementation subclass
175     * supports historical Daylight Saving Time schedule and GMT
176     * offset changes.
177     *
178     * @param era the era of the given date.
179     * @param year the year in the given date.
180     * @param month the month in the given date.
181     * Month is 0-based. e.g., 0 for January.
182     * @param day the day-in-month of the given date.
183     * @param dayOfWeek the day-of-week of the given date.
184     * @param milliseconds the milliseconds in day in <em>standard</em>
185     * local time.
186     *
187     * @return the offset in milliseconds to add to GMT to get local time.
188     *
189     * @see Calendar#ZONE_OFFSET
190     * @see Calendar#DST_OFFSET
191     */
192    public abstract int getOffset(int era, int year, int month, int day,
193                                  int dayOfWeek, int milliseconds);
194
195    /**
196     * Returns the offset of this time zone from UTC at the specified
197     * date. If Daylight Saving Time is in effect at the specified
198     * date, the offset value is adjusted with the amount of daylight
199     * saving.
200     * <p>
201     * This method returns a historically correct offset value if an
202     * underlying TimeZone implementation subclass supports historical
203     * Daylight Saving Time schedule and GMT offset changes.
204     *
205     * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT
206     * @return the amount of time in milliseconds to add to UTC to get local time.
207     *
208     * @see Calendar#ZONE_OFFSET
209     * @see Calendar#DST_OFFSET
210     * @since 1.4
211     */
212    public int getOffset(long date) {
213        if (inDaylightTime(new Date(date))) {
214            return getRawOffset() + getDSTSavings();
215        }
216        return getRawOffset();
217    }
218
219    /**
220     * Gets the raw GMT offset and the amount of daylight saving of this
221     * time zone at the given time.
222     * @param date the milliseconds (since January 1, 1970,
223     * 00:00:00.000 GMT) at which the time zone offset and daylight
224     * saving amount are found
225     * @param offset an array of int where the raw GMT offset
226     * (offset[0]) and daylight saving amount (offset[1]) are stored,
227     * or null if those values are not needed. The method assumes that
228     * the length of the given array is two or larger.
229     * @return the total amount of the raw GMT offset and daylight
230     * saving at the specified date.
231     *
232     * @see Calendar#ZONE_OFFSET
233     * @see Calendar#DST_OFFSET
234     */
235    int getOffsets(long date, int[] offsets) {
236        int rawoffset = getRawOffset();
237        int dstoffset = 0;
238        if (inDaylightTime(new Date(date))) {
239            dstoffset = getDSTSavings();
240        }
241        if (offsets != null) {
242            offsets[0] = rawoffset;
243            offsets[1] = dstoffset;
244        }
245        return rawoffset + dstoffset;
246    }
247
248    /**
249     * Sets the base time zone offset to GMT.
250     * This is the offset to add to UTC to get local time.
251     * <p>
252     * If an underlying <code>TimeZone</code> implementation subclass
253     * supports historical GMT offset changes, the specified GMT
254     * offset is set as the latest GMT offset and the difference from
255     * the known latest GMT offset value is used to adjust all
256     * historical GMT offset values.
257     *
258     * @param offsetMillis the given base time zone offset to GMT.
259     */
260    abstract public void setRawOffset(int offsetMillis);
261
262    /**
263     * Returns the amount of time in milliseconds to add to UTC to get
264     * standard time in this time zone. Because this value is not
265     * affected by daylight saving time, it is called <I>raw
266     * offset</I>.
267     * <p>
268     * If an underlying <code>TimeZone</code> implementation subclass
269     * supports historical GMT offset changes, the method returns the
270     * raw offset value of the current date. In Honolulu, for example,
271     * its raw offset changed from GMT-10:30 to GMT-10:00 in 1947, and
272     * this method always returns -36000000 milliseconds (i.e., -10
273     * hours).
274     *
275     * @return the amount of raw offset time in milliseconds to add to UTC.
276     * @see Calendar#ZONE_OFFSET
277     */
278    public abstract int getRawOffset();
279
280    /**
281     * Gets the ID of this time zone.
282     * @return the ID of this time zone.
283     */
284    public String getID()
285    {
286        return ID;
287    }
288
289    /**
290     * Sets the time zone ID. This does not change any other data in
291     * the time zone object.
292     * @param ID the new time zone ID.
293     */
294    public void setID(String ID)
295    {
296        if (ID == null) {
297            throw new NullPointerException();
298        }
299        this.ID = ID;
300    }
301
302    /**
303     * Returns a long standard time name of this {@code TimeZone} suitable for
304     * presentation to the user in the default locale.
305     *
306     * <p>This method is equivalent to:
307     * <pre><blockquote>
308     * getDisplayName(false, {@link #LONG},
309     *                Locale.getDefault({@link Locale.Category#DISPLAY}))
310     * </blockquote></pre>
311     *
312     * @return the human-readable name of this time zone in the default locale.
313     * @since 1.2
314     * @see #getDisplayName(boolean, int, Locale)
315     * @see Locale#getDefault(Locale.Category)
316     * @see Locale.Category
317     */
318    public final String getDisplayName() {
319        return getDisplayName(false, LONG,
320                              Locale.getDefault(Locale.Category.DISPLAY));
321    }
322
323    /**
324     * Returns a long standard time name of this {@code TimeZone} suitable for
325     * presentation to the user in the specified {@code locale}.
326     *
327     * <p>This method is equivalent to:
328     * <pre><blockquote>
329     * getDisplayName(false, {@link #LONG}, locale)
330     * </blockquote></pre>
331     *
332     * @param locale the locale in which to supply the display name.
333     * @return the human-readable name of this time zone in the given locale.
334     * @exception NullPointerException if {@code locale} is {@code null}.
335     * @since 1.2
336     * @see #getDisplayName(boolean, int, Locale)
337     */
338    public final String getDisplayName(Locale locale) {
339        return getDisplayName(false, LONG, locale);
340    }
341
342    /**
343     * Returns a name in the specified {@code style} of this {@code TimeZone}
344     * suitable for presentation to the user in the default locale. If the
345     * specified {@code daylight} is {@code true}, a Daylight Saving Time name
346     * is returned (even if this {@code TimeZone} doesn't observe Daylight Saving
347     * Time). Otherwise, a Standard Time name is returned.
348     *
349     * <p>This method is equivalent to:
350     * <pre><blockquote>
351     * getDisplayName(daylight, style,
352     *                Locale.getDefault({@link Locale.Category#DISPLAY}))
353     * </blockquote></pre>
354     *
355     * @param daylight {@code true} specifying a Daylight Saving Time name, or
356     *                 {@code false} specifying a Standard Time name
357     * @param style either {@link #LONG} or {@link #SHORT}
358     * @return the human-readable name of this time zone in the default locale.
359     * @exception IllegalArgumentException if {@code style} is invalid.
360     * @since 1.2
361     * @see #getDisplayName(boolean, int, Locale)
362     * @see Locale#getDefault(Locale.Category)
363     * @see Locale.Category
364     * @see java.text.DateFormatSymbols#getZoneStrings()
365     */
366    public final String getDisplayName(boolean daylight, int style) {
367        return getDisplayName(daylight, style,
368                              Locale.getDefault(Locale.Category.DISPLAY));
369    }
370
371    /**
372     * Returns the {@link #SHORT short} or {@link #LONG long} name of this time
373     * zone with either standard or daylight time, as written in {@code locale}.
374     * If the name is not available, the result is in the format
375     * {@code GMT[+-]hh:mm}.
376     *
377     * @param daylightTime true for daylight time, false for standard time.
378     * @param style either {@link TimeZone#LONG} or {@link TimeZone#SHORT}.
379     * @param locale the display locale.
380     */
381    public String getDisplayName(boolean daylightTime, int style, Locale locale) {
382        if (style != SHORT && style != LONG) {
383            throw new IllegalArgumentException("Bad style: " + style);
384        }
385
386        String[][] zoneStrings = TimeZoneNames.getZoneStrings(locale);
387        String result = TimeZoneNames.getDisplayName(zoneStrings, getID(), daylightTime, style);
388        if (result != null) {
389            return result;
390        }
391
392        // If we get here, it's because icu4c has nothing for us. Most commonly, this is in the
393        // case of short names. For Pacific/Fiji, for example, icu4c has nothing better to offer
394        // than "GMT+12:00". Why do we re-do this work ourselves? Because we have up-to-date
395        // time zone transition data, which icu4c _doesn't_ use --- it uses its own baked-in copy,
396        // which only gets updated when we update icu4c. http://b/7955614 and http://b/8026776.
397
398        // TODO: should we generate these once, in TimeZoneNames.getDisplayName? Revisit when we
399        // upgrade to icu4c 50 and rewrite the underlying native code. See also the
400        // "element[j] != null" check in SimpleDateFormat.parseTimeZone, and the extra work in
401        // DateFormatSymbols.getZoneStrings.
402        int offsetMillis = getRawOffset();
403        if (daylightTime) {
404            offsetMillis += getDSTSavings();
405        }
406        return createGmtOffsetString(true /* includeGmt */, true /* includeMinuteSeparator */,
407                offsetMillis);
408    }
409
410    /**
411     * Returns a string representation of an offset from UTC.
412     *
413     * <p>The format is "[GMT](+|-)HH[:]MM". The output is not localized.
414     *
415     * @param includeGmt true to include "GMT", false to exclude
416     * @param includeMinuteSeparator true to include the separator between hours and minutes, false
417     *     to exclude.
418     * @param offsetMillis the offset from UTC
419     *
420     * @hide used internally by SimpleDateFormat
421     */
422    public static String createGmtOffsetString(boolean includeGmt,
423            boolean includeMinuteSeparator, int offsetMillis) {
424        int offsetMinutes = offsetMillis / 60000;
425        char sign = '+';
426        if (offsetMinutes < 0) {
427            sign = '-';
428            offsetMinutes = -offsetMinutes;
429        }
430        StringBuilder builder = new StringBuilder(9);
431        if (includeGmt) {
432            builder.append("GMT");
433        }
434        builder.append(sign);
435        appendNumber(builder, 2, offsetMinutes / 60);
436        if (includeMinuteSeparator) {
437            builder.append(':');
438        }
439        appendNumber(builder, 2, offsetMinutes % 60);
440        return builder.toString();
441    }
442
443    private static void appendNumber(StringBuilder builder, int count, int value) {
444        String string = Integer.toString(value);
445        for (int i = 0; i < count - string.length(); i++) {
446            builder.append('0');
447        }
448        builder.append(string);
449    }
450
451    /**
452     * Returns the amount of time to be added to local standard time
453     * to get local wall clock time.
454     *
455     * <p>The default implementation returns 3600000 milliseconds
456     * (i.e., one hour) if a call to {@link #useDaylightTime()}
457     * returns {@code true}. Otherwise, 0 (zero) is returned.
458     *
459     * <p>If an underlying {@code TimeZone} implementation subclass
460     * supports historical and future Daylight Saving Time schedule
461     * changes, this method returns the amount of saving time of the
462     * last known Daylight Saving Time rule that can be a future
463     * prediction.
464     *
465     * <p>If the amount of saving time at any given time stamp is
466     * required, construct a {@link Calendar} with this {@code
467     * TimeZone} and the time stamp, and call {@link Calendar#get(int)
468     * Calendar.get}{@code (}{@link Calendar#DST_OFFSET}{@code )}.
469     *
470     * @return the amount of saving time in milliseconds
471     * @since 1.4
472     * @see #inDaylightTime(Date)
473     * @see #getOffset(long)
474     * @see #getOffset(int,int,int,int,int,int)
475     * @see Calendar#ZONE_OFFSET
476     */
477    public int getDSTSavings() {
478        if (useDaylightTime()) {
479            return 3600000;
480        }
481        return 0;
482    }
483
484    /**
485     * Queries if this {@code TimeZone} uses Daylight Saving Time.
486     *
487     * <p>If an underlying {@code TimeZone} implementation subclass
488     * supports historical and future Daylight Saving Time schedule
489     * changes, this method refers to the last known Daylight Saving Time
490     * rule that can be a future prediction and may not be the same as
491     * the current rule. Consider calling {@link #observesDaylightTime()}
492     * if the current rule should also be taken into account.
493     *
494     * @return {@code true} if this {@code TimeZone} uses Daylight Saving Time,
495     *         {@code false}, otherwise.
496     * @see #inDaylightTime(Date)
497     * @see Calendar#DST_OFFSET
498     */
499    public abstract boolean useDaylightTime();
500
501    /**
502     * Returns {@code true} if this {@code TimeZone} is currently in
503     * Daylight Saving Time, or if a transition from Standard Time to
504     * Daylight Saving Time occurs at any future time.
505     *
506     * <p>The default implementation returns {@code true} if
507     * {@code useDaylightTime()} or {@code inDaylightTime(new Date())}
508     * returns {@code true}.
509     *
510     * @return {@code true} if this {@code TimeZone} is currently in
511     * Daylight Saving Time, or if a transition from Standard Time to
512     * Daylight Saving Time occurs at any future time; {@code false}
513     * otherwise.
514     * @since 1.7
515     * @see #useDaylightTime()
516     * @see #inDaylightTime(Date)
517     * @see Calendar#DST_OFFSET
518     */
519    public boolean observesDaylightTime() {
520        return useDaylightTime() || inDaylightTime(new Date());
521    }
522
523    /**
524     * Queries if the given {@code date} is in Daylight Saving Time in
525     * this time zone.
526     *
527     * @param date the given Date.
528     * @return {@code true} if the given date is in Daylight Saving Time,
529     *         {@code false}, otherwise.
530     */
531    abstract public boolean inDaylightTime(Date date);
532
533    /**
534     * Gets the <code>TimeZone</code> for the given ID.
535     *
536     * @param id the ID for a <code>TimeZone</code>, either an abbreviation
537     * such as "PST", a full name such as "America/Los_Angeles", or a custom
538     * ID such as "GMT-8:00". Note that the support of abbreviations is
539     * for JDK 1.1.x compatibility only and full names should be used.
540     *
541     * @return the specified <code>TimeZone</code>, or the GMT zone if the given ID
542     * cannot be understood.
543     */
544    // Android changed param s/ID/id
545    public static synchronized TimeZone getTimeZone(String id) {
546        if (id == null) {
547            throw new NullPointerException("id == null");
548        }
549
550        // Special cases? These can clone an existing instance.
551        if (id.length() == 3) {
552            if (id.equals("GMT")) {
553                return (TimeZone) GMT.clone();
554            }
555            if (id.equals("UTC")) {
556                return (TimeZone) UTC.clone();
557            }
558        }
559
560        // In the database?
561        TimeZone zone = null;
562        try {
563            zone = ZoneInfoDB.getInstance().makeTimeZone(id);
564        } catch (IOException ignored) {
565        }
566
567        // Custom time zone?
568        if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
569            zone = getCustomTimeZone(id);
570        }
571
572        // We never return null; on failure we return the equivalent of "GMT".
573        return (zone != null) ? zone : (TimeZone) GMT.clone();
574    }
575
576    /**
577     * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
578     */
579    private static TimeZone getCustomTimeZone(String id) {
580        Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
581        if (!m.matches()) {
582            return null;
583        }
584
585        int hour;
586        int minute = 0;
587        try {
588            hour = Integer.parseInt(m.group(1));
589            if (m.group(3) != null) {
590                minute = Integer.parseInt(m.group(3));
591            }
592        } catch (NumberFormatException impossible) {
593            throw new AssertionError(impossible);
594        }
595
596        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
597            return null;
598        }
599
600        char sign = id.charAt(3);
601        int raw = (hour * 3600000) + (minute * 60000);
602        if (sign == '-') {
603            raw = -raw;
604        }
605
606        String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
607        return new SimpleTimeZone(raw, cleanId);
608    }
609
610    /**
611     * Gets the available IDs according to the given time zone offset in milliseconds.
612     *
613     * @param rawOffset the given time zone GMT offset in milliseconds.
614     * @return an array of IDs, where the time zone for that ID has
615     * the specified GMT offset. For example, "America/Phoenix" and "America/Denver"
616     * both have GMT-07:00, but differ in daylight saving behavior.
617     * @see #getRawOffset()
618     */
619    public static synchronized String[] getAvailableIDs(int rawOffset) {
620        return ZoneInfoDB.getInstance().getAvailableIDs(rawOffset);
621    }
622
623    /**
624     * Gets all the available IDs supported.
625     * @return an array of IDs.
626     */
627    public static synchronized String[] getAvailableIDs() {
628        return ZoneInfoDB.getInstance().getAvailableIDs();
629    }
630
631    /**
632     * Gets the platform defined TimeZone ID.
633     **/
634    private static native String getSystemTimeZoneID(String javaHome,
635                                                     String country);
636
637    /**
638     * Gets the custom time zone ID based on the GMT offset of the
639     * platform. (e.g., "GMT+08:00")
640     */
641    private static native String getSystemGMTOffsetID();
642
643    /**
644     * Gets the default <code>TimeZone</code> for this host.
645     * The source of the default <code>TimeZone</code>
646     * may vary with implementation.
647     * @return a default <code>TimeZone</code>.
648     * @see #setDefault
649     */
650    public static TimeZone getDefault() {
651        return (TimeZone) getDefaultRef().clone();
652    }
653
654    /**
655     * Returns the reference to the default TimeZone object. This
656     * method doesn't create a clone.
657     */
658    static synchronized TimeZone getDefaultRef() {
659        if (defaultTimeZone == null) {
660            TimezoneGetter tzGetter = TimezoneGetter.getInstance();
661            String zoneName = (tzGetter != null) ? tzGetter.getId() : null;
662            if (zoneName != null) {
663                zoneName = zoneName.trim();
664            }
665            if (zoneName == null || zoneName.isEmpty()) {
666                try {
667                    // On the host, we can find the configured timezone here.
668                    zoneName = IoUtils.readFileAsString("/etc/timezone");
669                } catch (IOException ex) {
670                    // "vogar --mode device" can end up here.
671                    // TODO: give libcore access to Android system properties and read "persist.sys.timezone".
672                    zoneName = "GMT";
673                }
674            }
675            defaultTimeZone = TimeZone.getTimeZone(zoneName);
676        }
677        return defaultTimeZone;
678    }
679
680    private static boolean hasPermission() {
681        boolean hasPermission = true;
682        SecurityManager sm = System.getSecurityManager();
683        if (sm != null) {
684            try {
685                sm.checkPermission(new PropertyPermission
686                                   ("user.timezone", "write"));
687            } catch (SecurityException e) {
688                hasPermission = false;
689            }
690        }
691        return hasPermission;
692    }
693
694    /**
695     * Sets the <code>TimeZone</code> that is
696     * returned by the <code>getDefault</code> method.  If <code>zone</code>
697     * is null, reset the default to the value it had originally when the
698     * VM first started.
699     * @param timeZone the new default time zone
700     * @see #getDefault
701     */
702    // Android changed s/zone/timeZone
703    public synchronized static void setDefault(TimeZone timeZone)
704    {
705        if (hasPermission()) {
706            defaultTimeZone = timeZone != null ? (TimeZone) timeZone.clone() : null;
707        }
708    }
709
710    /**
711     * Returns true if this zone has the same rule and offset as another zone.
712     * That is, if this zone differs only in ID, if at all.  Returns false
713     * if the other zone is null.
714     * @param other the <code>TimeZone</code> object to be compared with
715     * @return true if the other zone is not null and is the same as this one,
716     * with the possible exception of the ID
717     * @since 1.2
718     */
719    public boolean hasSameRules(TimeZone other) {
720        return other != null && getRawOffset() == other.getRawOffset() &&
721            useDaylightTime() == other.useDaylightTime();
722    }
723
724    /**
725     * Creates a copy of this <code>TimeZone</code>.
726     *
727     * @return a clone of this <code>TimeZone</code>
728     */
729    public Object clone()
730    {
731        try {
732            TimeZone other = (TimeZone) super.clone();
733            other.ID = ID;
734            return other;
735        } catch (CloneNotSupportedException e) {
736            throw new InternalError();
737        }
738    }
739
740    /**
741     * The null constant as a TimeZone.
742     */
743    static final TimeZone NO_TIMEZONE = null;
744
745    // =======================privates===============================
746
747    /**
748     * The string identifier of this <code>TimeZone</code>.  This is a
749     * programmatic identifier used internally to look up <code>TimeZone</code>
750     * objects from the system table and also to map them to their localized
751     * display names.  <code>ID</code> values are unique in the system
752     * table but may not be for dynamically created zones.
753     * @serial
754     */
755    private String           ID;
756    private static volatile TimeZone defaultTimeZone;
757}
758