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