• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.util;
19 
20 import java.io.Serializable;
21 import libcore.icu.TimeZones;
22 import libcore.util.ZoneInfoDB;
23 
24 /**
25  * {@code TimeZone} represents a time zone, primarily used for configuring a {@link Calendar} or
26  * {@link java.text.SimpleDateFormat} instance.
27  *
28  * <p>Most applications will use {@link #getDefault} which returns a {@code TimeZone} based on
29  * the time zone where the program is running.
30  *
31  * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by id}.
32  *
33  * <p>It is highly unlikely you'll ever want to use anything but the factory methods yourself.
34  * Let classes like {@link Calendar} and {@link java.text.SimpleDateFormat} do the date
35  * computations for you.
36  *
37  * <p>If you do need to do date computations manually, there are two common cases to take into
38  * account:
39  * <ul>
40  * <li>Somewhere like California, where daylight time is used.
41  * The {@link #useDaylightTime} method will always return true, and {@link #inDaylightTime}
42  * must be used to determine whether or not daylight time applies to a given {@code Date}.
43  * The {@link #getRawOffset} method will return a raw offset of (in this case) -8 hours from UTC,
44  * which isn't usually very useful. More usefully, the {@link #getOffset} methods return the
45  * actual offset from UTC <i>for a given point in time</i>; this is the raw offset plus (if the
46  * point in time is {@link #inDaylightTime in daylight time}) the applicable
47  * {@link #getDSTSavings DST savings} (usually, but not necessarily, 1 hour).
48  * <li>Somewhere like Japan, where daylight time is not used.
49  * The {@link #useDaylightTime} and {@link #inDaylightTime} methods both always return false,
50  * and the raw and actual offsets will always be the same.
51  * </ul>
52  *
53  * <p>Note the type returned by the factory methods {@link #getDefault} and {@link #getTimeZone} is
54  * implementation dependent. This may introduce serialization incompatibility issues between
55  * different implementations. Android returns instances of {@link SimpleTimeZone} so that
56  * the bytes serialized by Android can be deserialized successfully on other
57  * implementations, but the reverse compatibility cannot be guaranteed.
58  *
59  * @see Calendar
60  * @see GregorianCalendar
61  * @see SimpleDateFormat
62  * @see SimpleTimeZone
63  */
64 public abstract class TimeZone implements Serializable, Cloneable {
65     private static final long serialVersionUID = 3581463369166924961L;
66 
67     /**
68      * The short display name style, such as {@code PDT}. Requests for this
69      * style may yield GMT offsets like {@code GMT-08:00}.
70      */
71     public static final int SHORT = 0;
72 
73     /**
74      * The long display name style, such as {@code Pacific Daylight Time}.
75      * Requests for this style may yield GMT offsets like {@code GMT-08:00}.
76      */
77     public static final int LONG = 1;
78 
79     static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); // Greenwich Mean Time
80 
81     private static TimeZone defaultTimeZone;
82 
83     private String ID;
84 
TimeZone()85     public TimeZone() {}
86 
87     /**
88      * Returns a new time zone with the same ID, raw offset, and daylight
89      * savings time rules as this time zone.
90      */
clone()91     @Override public Object clone() {
92         try {
93             return super.clone();
94         } catch (CloneNotSupportedException e) {
95             throw new AssertionError(e);
96         }
97     }
98 
99     /**
100      * Returns the system's installed time zone IDs. Any of these IDs can be
101      * passed to {@link #getTimeZone} to lookup the corresponding time zone
102      * instance.
103      */
getAvailableIDs()104     public static synchronized String[] getAvailableIDs() {
105         return ZoneInfoDB.getAvailableIDs();
106     }
107 
108     /**
109      * Returns the IDs of the time zones whose offset from UTC is {@code
110      * offsetMillis}. Any of these IDs can be passed to {@link #getTimeZone} to
111      * lookup the corresponding time zone instance.
112      *
113      * @return a possibly-empty array.
114      */
getAvailableIDs(int offsetMillis)115     public static synchronized String[] getAvailableIDs(int offsetMillis) {
116         return ZoneInfoDB.getAvailableIDs(offsetMillis);
117     }
118 
119     /**
120      * Returns the user's preferred time zone. This may have been overridden for
121      * this process with {@link #setDefault}.
122      *
123      * <p>Since the user's time zone changes dynamically, avoid caching this
124      * value. Instead, use this method to look it up for each use.
125      */
getDefault()126     public static synchronized TimeZone getDefault() {
127         if (defaultTimeZone == null) {
128             defaultTimeZone = ZoneInfoDB.getSystemDefault();
129         }
130         return (TimeZone) defaultTimeZone.clone();
131     }
132 
133     /**
134      * Equivalent to {@code getDisplayName(false, TimeZone.LONG, Locale.getDefault())}.
135      * <a href="../util/Locale.html#default_locale">Be wary of the default locale</a>.
136      */
getDisplayName()137     public final String getDisplayName() {
138         return getDisplayName(false, LONG, Locale.getDefault());
139     }
140 
141     /**
142      * Equivalent to {@code getDisplayName(false, TimeZone.LONG, locale)}.
143      */
getDisplayName(Locale locale)144     public final String getDisplayName(Locale locale) {
145         return getDisplayName(false, LONG, locale);
146     }
147 
148     /**
149      * Equivalent to {@code getDisplayName(daylightTime, style, Locale.getDefault())}.
150      * <a href="../util/Locale.html#default_locale">Be wary of the default locale</a>.
151      */
getDisplayName(boolean daylightTime, int style)152     public final String getDisplayName(boolean daylightTime, int style) {
153         return getDisplayName(daylightTime, style, Locale.getDefault());
154     }
155 
156     /**
157      * Returns the {@link #SHORT short} or {@link #LONG long} name of this time
158      * zone with either standard or daylight time, as written in {@code locale}.
159      * If the name is not available, the result is in the format
160      * {@code GMT[+-]hh:mm}.
161      *
162      * @param daylightTime true for daylight time, false for standard time.
163      * @param style either {@link TimeZone#LONG} or {@link TimeZone#SHORT}.
164      * @param locale the display locale.
165      */
getDisplayName(boolean daylightTime, int style, Locale locale)166     public String getDisplayName(boolean daylightTime, int style, Locale locale) {
167         if (style != SHORT && style != LONG) {
168             throw new IllegalArgumentException();
169         }
170 
171         boolean useDaylight = daylightTime && useDaylightTime();
172 
173         String[][] zoneStrings = TimeZones.getZoneStrings(locale);
174         String result = TimeZones.getDisplayName(zoneStrings, getID(), daylightTime, style);
175         if (result != null) {
176             return result;
177         }
178 
179         // TODO: do we ever get here?
180 
181         int offset = getRawOffset();
182         if (useDaylight && this instanceof SimpleTimeZone) {
183             offset += getDSTSavings();
184         }
185         offset /= 60000;
186         char sign = '+';
187         if (offset < 0) {
188             sign = '-';
189             offset = -offset;
190         }
191         StringBuilder builder = new StringBuilder(9);
192         builder.append("GMT");
193         builder.append(sign);
194         appendNumber(builder, 2, offset / 60);
195         builder.append(':');
196         appendNumber(builder, 2, offset % 60);
197         return builder.toString();
198     }
199 
appendNumber(StringBuilder builder, int count, int value)200     private void appendNumber(StringBuilder builder, int count, int value) {
201         String string = Integer.toString(value);
202         for (int i = 0; i < count - string.length(); i++) {
203             builder.append('0');
204         }
205         builder.append(string);
206     }
207 
208     /**
209      * Returns the ID of this {@code TimeZone}, such as
210      * {@code America/Los_Angeles}, {@code GMT-08:00} or {@code UTC}.
211      */
getID()212     public String getID() {
213         return ID;
214     }
215 
216     /**
217      * Returns the daylight savings offset in milliseconds for this time zone.
218      * The base implementation returns {@code 3600000} (1 hour) for time zones
219      * that use daylight savings time and {@code 0} for timezones that do not.
220      * Subclasses should override this method for other daylight savings
221      * offsets.
222      *
223      * <p>Note that this method doesn't tell you whether or not to apply the
224      * offset: you need to call {@code inDaylightTime} for the specific time
225      * you're interested in. If this method returns a non-zero offset, that only
226      * tells you that this {@code TimeZone} sometimes observes daylight savings.
227      */
getDSTSavings()228     public int getDSTSavings() {
229         return useDaylightTime() ? 3600000 : 0;
230     }
231 
232     /**
233      * Returns the offset in milliseconds from UTC for this time zone at {@code
234      * time}. The offset includes daylight savings time if the specified
235      * date is within the daylight savings time period.
236      *
237      * @param time the date in milliseconds since January 1, 1970 00:00:00 UTC
238      */
getOffset(long time)239     public int getOffset(long time) {
240         if (inDaylightTime(new Date(time))) {
241             return getRawOffset() + getDSTSavings();
242         }
243         return getRawOffset();
244     }
245 
246     /**
247      * Returns this time zone's offset in milliseconds from UTC at the specified
248      * date and time. The offset includes daylight savings time if the date
249      * and time is within the daylight savings time period.
250      *
251      * <p>This method is intended to be used by {@link Calendar} to compute
252      * {@link Calendar#DST_OFFSET} and {@link Calendar#ZONE_OFFSET}. Application
253      * code should have no reason to call this method directly. Each parameter
254      * is interpreted in the same way as the corresponding {@code Calendar}
255      * field. Refer to {@link Calendar} for specific definitions of this
256      * method's parameters.
257      */
getOffset(int era, int year, int month, int day, int dayOfWeek, int timeOfDayMillis)258     public abstract int getOffset(int era, int year, int month, int day,
259             int dayOfWeek, int timeOfDayMillis);
260 
261     /**
262      * Returns the offset in milliseconds from UTC of this time zone's standard
263      * time.
264      */
getRawOffset()265     public abstract int getRawOffset();
266 
267     /**
268      * Returns a {@code TimeZone} suitable for {@code id}, or {@code GMT} for unknown ids.
269      *
270      * <p>An id can be an Olson name of the form <i>Area</i>/<i>Location</i>, such
271      * as {@code America/Los_Angeles}. The {@link #getAvailableIDs} method returns
272      * the supported names.
273      *
274      * <p>This method can also create a custom {@code TimeZone} using the following
275      * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code TimeZone.getTimeZone("GMT+14:00")}
276      * would return an object with a raw offset of +14 hours from UTC, and which does <i>not</i>
277      * use daylight savings. These are rarely useful, because they don't correspond to time
278      * zones actually in use.
279      *
280      * <p>Other than the special cases "UTC" and "GMT" (which are synonymous in this context,
281      * both corresponding to UTC), Android does not support the deprecated three-letter time
282      * zone IDs used in Java 1.1.
283      */
getTimeZone(String id)284     public static synchronized TimeZone getTimeZone(String id) {
285         if (id == null) {
286             throw new NullPointerException("id == null");
287         }
288         TimeZone zone = ZoneInfoDB.getTimeZone(id);
289         if (zone != null) {
290             return zone;
291         }
292         if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
293             zone = getCustomTimeZone(id);
294         }
295         if (zone == null) {
296             zone = (TimeZone) GMT.clone();
297         }
298         return zone;
299     }
300 
301     /**
302      * Returns a new SimpleTimeZone for an id of the form "GMT[+|-]hh[[:]mm]", or null.
303      */
getCustomTimeZone(String id)304     private static TimeZone getCustomTimeZone(String id) {
305         char sign = id.charAt(3);
306         if (sign != '+' && sign != '-') {
307             return null;
308         }
309         int[] position = new int[1];
310         String formattedName = formatTimeZoneName(id, 4);
311         int hour = parseNumber(formattedName, 4, position);
312         if (hour < 0 || hour > 23) {
313             return null;
314         }
315         int index = position[0];
316         if (index == -1) {
317             return null;
318         }
319         int raw = hour * 3600000;
320         if (index < formattedName.length() && formattedName.charAt(index) == ':') {
321             int minute = parseNumber(formattedName, index + 1, position);
322             if (position[0] == -1 || minute < 0 || minute > 59) {
323                 return null;
324             }
325             raw += minute * 60000;
326         } else if (hour >= 30 || index > 6) {
327             raw = (hour / 100 * 3600000) + (hour % 100 * 60000);
328         }
329         if (sign == '-') {
330             raw = -raw;
331         }
332         return new SimpleTimeZone(raw, formattedName);
333     }
334 
formatTimeZoneName(String name, int offset)335     private static String formatTimeZoneName(String name, int offset) {
336         StringBuilder buf = new StringBuilder();
337         int index = offset, length = name.length();
338         buf.append(name.substring(0, offset));
339 
340         while (index < length) {
341             if (Character.digit(name.charAt(index), 10) != -1) {
342                 buf.append(name.charAt(index));
343                 if ((length - (index + 1)) == 2) {
344                     buf.append(':');
345                 }
346             } else if (name.charAt(index) == ':') {
347                 buf.append(':');
348             }
349             index++;
350         }
351 
352         if (buf.toString().indexOf(":") == -1) {
353             buf.append(':');
354             buf.append("00");
355         }
356 
357         if (buf.toString().indexOf(":") == 5) {
358             buf.insert(4, '0');
359         }
360 
361         return buf.toString();
362     }
363 
364     /**
365      * Returns true if {@code timeZone} has the same rules as this time zone.
366      *
367      * <p>The base implementation returns true if both time zones have the same
368      * raw offset.
369      */
hasSameRules(TimeZone timeZone)370     public boolean hasSameRules(TimeZone timeZone) {
371         if (timeZone == null) {
372             return false;
373         }
374         return getRawOffset() == timeZone.getRawOffset();
375     }
376 
377     /**
378      * Returns true if {@code time} is in a daylight savings time period for
379      * this time zone.
380      */
inDaylightTime(Date time)381     public abstract boolean inDaylightTime(Date time);
382 
parseNumber(String string, int offset, int[] position)383     private static int parseNumber(String string, int offset, int[] position) {
384         int index = offset, length = string.length(), digit, result = 0;
385         while (index < length
386                 && (digit = Character.digit(string.charAt(index), 10)) != -1) {
387             index++;
388             result = result * 10 + digit;
389         }
390         position[0] = index == offset ? -1 : index;
391         return result;
392     }
393 
394     /**
395      * Overrides the default time zone for the current process only.
396      *
397      * <p><strong>Warning</strong>: avoid using this method to use a custom time
398      * zone in your process. This value may be cleared or overwritten at any
399      * time, which can cause unexpected behavior. Instead, manually supply a
400      * custom time zone as needed.
401      *
402      * @param timeZone a custom time zone, or {@code null} to set the default to
403      *     the user's preferred value.
404      */
setDefault(TimeZone timeZone)405     public static synchronized void setDefault(TimeZone timeZone) {
406         defaultTimeZone = timeZone != null ? (TimeZone) timeZone.clone() : null;
407     }
408 
409     /**
410      * Sets the ID of this {@code TimeZone}.
411      */
setID(String id)412     public void setID(String id) {
413         if (id == null) {
414             throw new NullPointerException();
415         }
416         ID = id;
417     }
418 
419     /**
420      * Sets the offset in milliseconds from UTC of this time zone's standard
421      * time.
422      */
setRawOffset(int offsetMillis)423     public abstract void setRawOffset(int offsetMillis);
424 
425     /**
426      * Returns true if this time zone has a future transition to or from
427      * daylight savings time.
428      *
429      * <p><strong>Warning:</strong> this returns false for time zones like
430      * {@code Asia/Kuala_Lumpur} that have previously used DST but do not
431      * currently. A hypothetical country that has never observed daylight
432      * savings before but plans to start next year would return true.
433      *
434      * <p><strong>Warning:</strong> this returns true for time zones that use
435      * DST, even when it is not active.
436      *
437      * <p>Use {@link #inDaylightTime} to find out whether daylight savings is
438      * in effect at a specific time.
439      *
440      * <p>Most applications should not use this method.
441      */
useDaylightTime()442     public abstract boolean useDaylightTime();
443 }
444