TimeZone.java revision 6fb123c8681a795c65cbd67ffcc0ef641c473288
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 libcore.icu.TimeZones; 22import 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 */ 64public 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 381 public abstract boolean inDaylightTime(Date time); 382 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 */ 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 */ 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 */ 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 */ 442 public abstract boolean useDaylightTime(); 443} 444