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