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