TimeZone.java revision 9c853c5b9ebbb0ef60a013ae10ee411d70dfa832
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    public static synchronized TimeZone getTimeZone(String id) {
545        if (id == null) {
546            throw new NullPointerException("id == null");
547        }
548
549        // Special cases? These can clone an existing instance.
550        if (id.length() == 3) {
551            if (id.equals("GMT")) {
552                return (TimeZone) GMT.clone();
553            }
554            if (id.equals("UTC")) {
555                return (TimeZone) UTC.clone();
556            }
557        }
558
559        // In the database?
560        TimeZone zone = null;
561        try {
562            zone = ZoneInfoDB.getInstance().makeTimeZone(id);
563        } catch (IOException ignored) {
564        }
565
566        // Custom time zone?
567        if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
568            zone = getCustomTimeZone(id);
569        }
570
571        // We never return null; on failure we return the equivalent of "GMT".
572        return (zone != null) ? zone : (TimeZone) GMT.clone();
573    }
574
575    /**
576     * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
577     */
578    private static TimeZone getCustomTimeZone(String id) {
579        Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
580        if (!m.matches()) {
581            return null;
582        }
583
584        int hour;
585        int minute = 0;
586        try {
587            hour = Integer.parseInt(m.group(1));
588            if (m.group(3) != null) {
589                minute = Integer.parseInt(m.group(3));
590            }
591        } catch (NumberFormatException impossible) {
592            throw new AssertionError(impossible);
593        }
594
595        if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
596            return null;
597        }
598
599        char sign = id.charAt(3);
600        int raw = (hour * 3600000) + (minute * 60000);
601        if (sign == '-') {
602            raw = -raw;
603        }
604
605        String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
606        return new SimpleTimeZone(raw, cleanId);
607    }
608
609    /**
610     * Gets the available IDs according to the given time zone offset in milliseconds.
611     *
612     * @param rawOffset the given time zone GMT offset in milliseconds.
613     * @return an array of IDs, where the time zone for that ID has
614     * the specified GMT offset. For example, "America/Phoenix" and "America/Denver"
615     * both have GMT-07:00, but differ in daylight saving behavior.
616     * @see #getRawOffset()
617     */
618    public static synchronized String[] getAvailableIDs(int rawOffset) {
619        return ZoneInfoDB.getInstance().getAvailableIDs(rawOffset);
620    }
621
622    /**
623     * Gets all the available IDs supported.
624     * @return an array of IDs.
625     */
626    public static synchronized String[] getAvailableIDs() {
627        return ZoneInfoDB.getInstance().getAvailableIDs();
628    }
629
630    /**
631     * Gets the platform defined TimeZone ID.
632     **/
633    private static native String getSystemTimeZoneID(String javaHome,
634                                                     String country);
635
636    /**
637     * Gets the custom time zone ID based on the GMT offset of the
638     * platform. (e.g., "GMT+08:00")
639     */
640    private static native String getSystemGMTOffsetID();
641
642    /**
643     * Gets the default <code>TimeZone</code> for this host.
644     * The source of the default <code>TimeZone</code>
645     * may vary with implementation.
646     * @return a default <code>TimeZone</code>.
647     * @see #setDefault
648     */
649    public static TimeZone getDefault() {
650        return (TimeZone) getDefaultRef().clone();
651    }
652
653    /**
654     * Returns the reference to the default TimeZone object. This
655     * method doesn't create a clone.
656     */
657    static synchronized TimeZone getDefaultRef() {
658        if (defaultTimeZone == null) {
659            TimezoneGetter tzGetter = TimezoneGetter.getInstance();
660            String zoneName = (tzGetter != null) ? tzGetter.getId() : null;
661            if (zoneName != null) {
662                zoneName = zoneName.trim();
663            }
664            if (zoneName == null || zoneName.isEmpty()) {
665                try {
666                    // On the host, we can find the configured timezone here.
667                    zoneName = IoUtils.readFileAsString("/etc/timezone");
668                } catch (IOException ex) {
669                    // "vogar --mode device" can end up here.
670                    // TODO: give libcore access to Android system properties and read "persist.sys.timezone".
671                    zoneName = "GMT";
672                }
673            }
674            defaultTimeZone = TimeZone.getTimeZone(zoneName);
675        }
676        return defaultTimeZone;
677    }
678
679    private static boolean hasPermission() {
680        boolean hasPermission = true;
681        SecurityManager sm = System.getSecurityManager();
682        if (sm != null) {
683            try {
684                sm.checkPermission(new PropertyPermission
685                                   ("user.timezone", "write"));
686            } catch (SecurityException e) {
687                hasPermission = false;
688            }
689        }
690        return hasPermission;
691    }
692
693    /**
694     * Sets the <code>TimeZone</code> that is
695     * returned by the <code>getDefault</code> method.  If <code>zone</code>
696     * is null, reset the default to the value it had originally when the
697     * VM first started.
698     * @param zone the new default time zone
699     * @see #getDefault
700     */
701    public synchronized static void setDefault(TimeZone timeZone)
702    {
703        if (hasPermission()) {
704            defaultTimeZone = timeZone != null ? (TimeZone) timeZone.clone() : null;
705        }
706    }
707
708    /**
709     * Returns true if this zone has the same rule and offset as another zone.
710     * That is, if this zone differs only in ID, if at all.  Returns false
711     * if the other zone is null.
712     * @param other the <code>TimeZone</code> object to be compared with
713     * @return true if the other zone is not null and is the same as this one,
714     * with the possible exception of the ID
715     * @since 1.2
716     */
717    public boolean hasSameRules(TimeZone other) {
718        return other != null && getRawOffset() == other.getRawOffset() &&
719            useDaylightTime() == other.useDaylightTime();
720    }
721
722    /**
723     * Creates a copy of this <code>TimeZone</code>.
724     *
725     * @return a clone of this <code>TimeZone</code>
726     */
727    public Object clone()
728    {
729        try {
730            TimeZone other = (TimeZone) super.clone();
731            other.ID = ID;
732            return other;
733        } catch (CloneNotSupportedException e) {
734            throw new InternalError();
735        }
736    }
737
738    /**
739     * The null constant as a TimeZone.
740     */
741    static final TimeZone NO_TIMEZONE = null;
742
743    // =======================privates===============================
744
745    /**
746     * The string identifier of this <code>TimeZone</code>.  This is a
747     * programmatic identifier used internally to look up <code>TimeZone</code>
748     * objects from the system table and also to map them to their localized
749     * display names.  <code>ID</code> values are unique in the system
750     * table but may not be for dynamically created zones.
751     * @serial
752     */
753    private String           ID;
754    private static volatile TimeZone defaultTimeZone;
755}
756