TimeZone.java revision 2c87ad3a45cecf9e344487cad1abfdebe79f2c7c
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27/* 28 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved 29 * (C) Copyright IBM Corp. 1996 - All Rights Reserved 30 * 31 * The original version of this source code and documentation is copyrighted 32 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These 33 * materials are provided under terms of a License Agreement between Taligent 34 * and Sun. This technology is protected by multiple US and International 35 * patents. This notice and attribution to Taligent may not be removed. 36 * Taligent is a registered trademark of Taligent, Inc. 37 * 38 */ 39 40package java.util; 41 42import java.io.IOException; 43import java.io.Serializable; 44import java.util.regex.Matcher; 45import java.util.regex.Pattern; 46import java.lang.ref.SoftReference; 47import java.security.AccessController; 48import java.security.PrivilegedAction; 49import java.util.concurrent.ConcurrentHashMap; 50import libcore.icu.TimeZoneNames; 51import libcore.io.IoUtils; 52import libcore.util.ZoneInfoDB; 53import sun.security.action.GetPropertyAction; 54import org.apache.harmony.luni.internal.util.TimezoneGetter; 55 56/** 57 * <code>TimeZone</code> represents a time zone offset, and also figures out daylight 58 * savings. 59 * 60 * <p> 61 * Typically, you get a <code>TimeZone</code> using <code>getDefault</code> 62 * which creates a <code>TimeZone</code> based on the time zone where the program 63 * is running. For example, for a program running in Japan, <code>getDefault</code> 64 * creates a <code>TimeZone</code> object based on Japanese Standard Time. 65 * 66 * <p> 67 * You can also get a <code>TimeZone</code> using <code>getTimeZone</code> 68 * along with a time zone ID. For instance, the time zone ID for the 69 * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a 70 * U.S. Pacific Time <code>TimeZone</code> object with: 71 * <blockquote><pre> 72 * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); 73 * </pre></blockquote> 74 * You can use the <code>getAvailableIDs</code> method to iterate through 75 * all the supported time zone IDs. You can then choose a 76 * supported ID to get a <code>TimeZone</code>. 77 * If the time zone you want is not represented by one of the 78 * supported IDs, then a custom time zone ID can be specified to 79 * produce a TimeZone. The syntax of a custom time zone ID is: 80 * 81 * <blockquote><pre> 82 * <a name="CustomID"><i>CustomID:</i></a> 83 * <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i> 84 * <code>GMT</code> <i>Sign</i> <i>Hours</i> <i>Minutes</i> 85 * <code>GMT</code> <i>Sign</i> <i>Hours</i> 86 * <i>Sign:</i> one of 87 * <code>+ -</code> 88 * <i>Hours:</i> 89 * <i>Digit</i> 90 * <i>Digit</i> <i>Digit</i> 91 * <i>Minutes:</i> 92 * <i>Digit</i> <i>Digit</i> 93 * <i>Digit:</i> one of 94 * <code>0 1 2 3 4 5 6 7 8 9</code> 95 * </pre></blockquote> 96 * 97 * <i>Hours</i> must be between 0 to 23 and <i>Minutes</i> must be 98 * between 00 to 59. For example, "GMT+10" and "GMT+0010" mean ten 99 * hours and ten minutes ahead of GMT, respectively. 100 * <p> 101 * The format is locale independent and digits must be taken from the 102 * Basic Latin block of the Unicode standard. No daylight saving time 103 * transition schedule can be specified with a custom time zone ID. If 104 * the specified string doesn't match the syntax, <code>"GMT"</code> 105 * is used. 106 * <p> 107 * When creating a <code>TimeZone</code>, the specified custom time 108 * zone ID is normalized in the following syntax: 109 * <blockquote><pre> 110 * <a name="NormalizedCustomID"><i>NormalizedCustomID:</i></a> 111 * <code>GMT</code> <i>Sign</i> <i>TwoDigitHours</i> <code>:</code> <i>Minutes</i> 112 * <i>Sign:</i> one of 113 * <code>+ -</code> 114 * <i>TwoDigitHours:</i> 115 * <i>Digit</i> <i>Digit</i> 116 * <i>Minutes:</i> 117 * <i>Digit</i> <i>Digit</i> 118 * <i>Digit:</i> one of 119 * <code>0 1 2 3 4 5 6 7 8 9</code> 120 * </pre></blockquote> 121 * For example, TimeZone.getTimeZone("GMT-8").getID() returns "GMT-08:00". 122 * 123 * <h4>Three-letter time zone IDs</h4> 124 * 125 * For compatibility with JDK 1.1.x, some other three-letter time zone IDs 126 * (such as "PST", "CTT", "AST") are also supported. However, <strong>their 127 * use is deprecated</strong> because the same abbreviation is often used 128 * for multiple time zones (for example, "CST" could be U.S. "Central Standard 129 * Time" and "China Standard Time"), and the Java platform can then only 130 * recognize one of them. 131 * 132 * 133 * @see Calendar 134 * @see GregorianCalendar 135 * @see SimpleTimeZone 136 * @author Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu 137 * @since JDK1.1 138 */ 139abstract public class TimeZone implements Serializable, Cloneable { 140 /** 141 * Sole constructor. (For invocation by subclass constructors, typically 142 * implicit.) 143 */ 144 public TimeZone() { 145 } 146 147 /** 148 * A style specifier for <code>getDisplayName()</code> indicating 149 * a short name, such as "PST." 150 * @see #LONG 151 * @since 1.2 152 */ 153 public static final int SHORT = 0; 154 155 /** 156 * A style specifier for <code>getDisplayName()</code> indicating 157 * a long name, such as "Pacific Standard Time." 158 * @see #SHORT 159 * @since 1.2 160 */ 161 public static final int LONG = 1; 162 163 private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$"); 164 165 // Proclaim serialization compatibility with JDK 1.1 166 static final long serialVersionUID = 3581463369166924961L; 167 private static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); 168 private static final TimeZone UTC = new SimpleTimeZone(0, "UTC"); 169 170 /** 171 * Gets the time zone offset, for current date, modified in case of 172 * daylight savings. This is the offset to add to UTC to get local time. 173 * <p> 174 * This method returns a historically correct offset if an 175 * underlying <code>TimeZone</code> implementation subclass 176 * supports historical Daylight Saving Time schedule and GMT 177 * offset changes. 178 * 179 * @param era the era of the given date. 180 * @param year the year in the given date. 181 * @param month the month in the given date. 182 * Month is 0-based. e.g., 0 for January. 183 * @param day the day-in-month of the given date. 184 * @param dayOfWeek the day-of-week of the given date. 185 * @param milliseconds the milliseconds in day in <em>standard</em> 186 * local time. 187 * 188 * @return the offset in milliseconds to add to GMT to get local time. 189 * 190 * @see Calendar#ZONE_OFFSET 191 * @see Calendar#DST_OFFSET 192 */ 193 public abstract int getOffset(int era, int year, int month, int day, 194 int dayOfWeek, int milliseconds); 195 196 /** 197 * Returns the offset of this time zone from UTC at the specified 198 * date. If Daylight Saving Time is in effect at the specified 199 * date, the offset value is adjusted with the amount of daylight 200 * saving. 201 * <p> 202 * This method returns a historically correct offset value if an 203 * underlying TimeZone implementation subclass supports historical 204 * Daylight Saving Time schedule and GMT offset changes. 205 * 206 * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT 207 * @return the amount of time in milliseconds to add to UTC to get local time. 208 * 209 * @see Calendar#ZONE_OFFSET 210 * @see Calendar#DST_OFFSET 211 * @since 1.4 212 */ 213 public int getOffset(long date) { 214 if (inDaylightTime(new Date(date))) { 215 return getRawOffset() + getDSTSavings(); 216 } 217 return getRawOffset(); 218 } 219 220 /** 221 * Gets the raw GMT offset and the amount of daylight saving of this 222 * time zone at the given time. 223 * @param date the milliseconds (since January 1, 1970, 224 * 00:00:00.000 GMT) at which the time zone offset and daylight 225 * saving amount are found 226 * @param offset an array of int where the raw GMT offset 227 * (offset[0]) and daylight saving amount (offset[1]) are stored, 228 * or null if those values are not needed. The method assumes that 229 * the length of the given array is two or larger. 230 * @return the total amount of the raw GMT offset and daylight 231 * saving at the specified date. 232 * 233 * @see Calendar#ZONE_OFFSET 234 * @see Calendar#DST_OFFSET 235 */ 236 int getOffsets(long date, int[] offsets) { 237 int rawoffset = getRawOffset(); 238 int dstoffset = 0; 239 if (inDaylightTime(new Date(date))) { 240 dstoffset = getDSTSavings(); 241 } 242 if (offsets != null) { 243 offsets[0] = rawoffset; 244 offsets[1] = dstoffset; 245 } 246 return rawoffset + dstoffset; 247 } 248 249 /** 250 * Sets the base time zone offset to GMT. 251 * This is the offset to add to UTC to get local time. 252 * <p> 253 * If an underlying <code>TimeZone</code> implementation subclass 254 * supports historical GMT offset changes, the specified GMT 255 * offset is set as the latest GMT offset and the difference from 256 * the known latest GMT offset value is used to adjust all 257 * historical GMT offset values. 258 * 259 * @param offsetMillis the given base time zone offset to GMT. 260 */ 261 abstract public void setRawOffset(int offsetMillis); 262 263 /** 264 * Returns the amount of time in milliseconds to add to UTC to get 265 * standard time in this time zone. Because this value is not 266 * affected by daylight saving time, it is called <I>raw 267 * offset</I>. 268 * <p> 269 * If an underlying <code>TimeZone</code> implementation subclass 270 * supports historical GMT offset changes, the method returns the 271 * raw offset value of the current date. In Honolulu, for example, 272 * its raw offset changed from GMT-10:30 to GMT-10:00 in 1947, and 273 * this method always returns -36000000 milliseconds (i.e., -10 274 * hours). 275 * 276 * @return the amount of raw offset time in milliseconds to add to UTC. 277 * @see Calendar#ZONE_OFFSET 278 */ 279 public abstract int getRawOffset(); 280 281 /** 282 * Gets the ID of this time zone. 283 * @return the ID of this time zone. 284 */ 285 public String getID() 286 { 287 return ID; 288 } 289 290 /** 291 * Sets the time zone ID. This does not change any other data in 292 * the time zone object. 293 * @param ID the new time zone ID. 294 */ 295 public void setID(String ID) 296 { 297 if (ID == null) { 298 throw new NullPointerException(); 299 } 300 this.ID = ID; 301 } 302 303 /** 304 * Returns a long standard time name of this {@code TimeZone} suitable for 305 * presentation to the user in the default locale. 306 * 307 * <p>This method is equivalent to: 308 * <pre><blockquote> 309 * getDisplayName(false, {@link #LONG}, 310 * Locale.getDefault({@link Locale.Category#DISPLAY})) 311 * </blockquote></pre> 312 * 313 * @return the human-readable name of this time zone in the default locale. 314 * @since 1.2 315 * @see #getDisplayName(boolean, int, Locale) 316 * @see Locale#getDefault(Locale.Category) 317 * @see Locale.Category 318 */ 319 public final String getDisplayName() { 320 return getDisplayName(false, LONG, 321 Locale.getDefault(Locale.Category.DISPLAY)); 322 } 323 324 /** 325 * Returns a long standard time name of this {@code TimeZone} suitable for 326 * presentation to the user in the specified {@code locale}. 327 * 328 * <p>This method is equivalent to: 329 * <pre><blockquote> 330 * getDisplayName(false, {@link #LONG}, locale) 331 * </blockquote></pre> 332 * 333 * @param locale the locale in which to supply the display name. 334 * @return the human-readable name of this time zone in the given locale. 335 * @exception NullPointerException if {@code locale} is {@code null}. 336 * @since 1.2 337 * @see #getDisplayName(boolean, int, Locale) 338 */ 339 public final String getDisplayName(Locale locale) { 340 return getDisplayName(false, LONG, locale); 341 } 342 343 /** 344 * Returns a name in the specified {@code style} of this {@code TimeZone} 345 * suitable for presentation to the user in the default locale. If the 346 * specified {@code daylight} is {@code true}, a Daylight Saving Time name 347 * is returned (even if this {@code TimeZone} doesn't observe Daylight Saving 348 * Time). Otherwise, a Standard Time name is returned. 349 * 350 * <p>This method is equivalent to: 351 * <pre><blockquote> 352 * getDisplayName(daylight, style, 353 * Locale.getDefault({@link Locale.Category#DISPLAY})) 354 * </blockquote></pre> 355 * 356 * @param daylight {@code true} specifying a Daylight Saving Time name, or 357 * {@code false} specifying a Standard Time name 358 * @param style either {@link #LONG} or {@link #SHORT} 359 * @return the human-readable name of this time zone in the default locale. 360 * @exception IllegalArgumentException if {@code style} is invalid. 361 * @since 1.2 362 * @see #getDisplayName(boolean, int, Locale) 363 * @see Locale#getDefault(Locale.Category) 364 * @see Locale.Category 365 * @see java.text.DateFormatSymbols#getZoneStrings() 366 */ 367 public final String getDisplayName(boolean daylight, int style) { 368 return getDisplayName(daylight, style, 369 Locale.getDefault(Locale.Category.DISPLAY)); 370 } 371 372 /** 373 * Returns the {@link #SHORT short} or {@link #LONG long} name of this time 374 * zone with either standard or daylight time, as written in {@code locale}. 375 * If the name is not available, the result is in the format 376 * {@code GMT[+-]hh:mm}. 377 * 378 * @param daylightTime true for daylight time, false for standard time. 379 * @param style either {@link TimeZone#LONG} or {@link TimeZone#SHORT}. 380 * @param locale the display locale. 381 */ 382 public String getDisplayName(boolean daylightTime, int style, Locale locale) { 383 if (style != SHORT && style != LONG) { 384 throw new IllegalArgumentException("Bad style: " + style); 385 } 386 387 String[][] zoneStrings = TimeZoneNames.getZoneStrings(locale); 388 String result = TimeZoneNames.getDisplayName(zoneStrings, getID(), daylightTime, style); 389 if (result != null) { 390 return result; 391 } 392 393 // If we get here, it's because icu4c has nothing for us. Most commonly, this is in the 394 // case of short names. For Pacific/Fiji, for example, icu4c has nothing better to offer 395 // than "GMT+12:00". Why do we re-do this work ourselves? Because we have up-to-date 396 // time zone transition data, which icu4c _doesn't_ use --- it uses its own baked-in copy, 397 // which only gets updated when we update icu4c. http://b/7955614 and http://b/8026776. 398 399 // TODO: should we generate these once, in TimeZoneNames.getDisplayName? Revisit when we 400 // upgrade to icu4c 50 and rewrite the underlying native code. See also the 401 // "element[j] != null" check in SimpleDateFormat.parseTimeZone, and the extra work in 402 // DateFormatSymbols.getZoneStrings. 403 int offsetMillis = getRawOffset(); 404 if (daylightTime) { 405 offsetMillis += getDSTSavings(); 406 } 407 return createGmtOffsetString(true /* includeGmt */, true /* includeMinuteSeparator */, 408 offsetMillis); 409 } 410 411 /** 412 * Returns a string representation of an offset from UTC. 413 * 414 * <p>The format is "[GMT](+|-)HH[:]MM". The output is not localized. 415 * 416 * @param includeGmt true to include "GMT", false to exclude 417 * @param includeMinuteSeparator true to include the separator between hours and minutes, false 418 * to exclude. 419 * @param offsetMillis the offset from UTC 420 * 421 * @hide used internally by SimpleDateFormat 422 */ 423 public static String createGmtOffsetString(boolean includeGmt, 424 boolean includeMinuteSeparator, int offsetMillis) { 425 int offsetMinutes = offsetMillis / 60000; 426 char sign = '+'; 427 if (offsetMinutes < 0) { 428 sign = '-'; 429 offsetMinutes = -offsetMinutes; 430 } 431 StringBuilder builder = new StringBuilder(9); 432 if (includeGmt) { 433 builder.append("GMT"); 434 } 435 builder.append(sign); 436 appendNumber(builder, 2, offsetMinutes / 60); 437 if (includeMinuteSeparator) { 438 builder.append(':'); 439 } 440 appendNumber(builder, 2, offsetMinutes % 60); 441 return builder.toString(); 442 } 443 444 private static void appendNumber(StringBuilder builder, int count, int value) { 445 String string = Integer.toString(value); 446 for (int i = 0; i < count - string.length(); i++) { 447 builder.append('0'); 448 } 449 builder.append(string); 450 } 451 452 /** 453 * Returns the amount of time to be added to local standard time 454 * to get local wall clock time. 455 * 456 * <p>The default implementation returns 3600000 milliseconds 457 * (i.e., one hour) if a call to {@link #useDaylightTime()} 458 * returns {@code true}. Otherwise, 0 (zero) is returned. 459 * 460 * <p>If an underlying {@code TimeZone} implementation subclass 461 * supports historical and future Daylight Saving Time schedule 462 * changes, this method returns the amount of saving time of the 463 * last known Daylight Saving Time rule that can be a future 464 * prediction. 465 * 466 * <p>If the amount of saving time at any given time stamp is 467 * required, construct a {@link Calendar} with this {@code 468 * TimeZone} and the time stamp, and call {@link Calendar#get(int) 469 * Calendar.get}{@code (}{@link Calendar#DST_OFFSET}{@code )}. 470 * 471 * @return the amount of saving time in milliseconds 472 * @since 1.4 473 * @see #inDaylightTime(Date) 474 * @see #getOffset(long) 475 * @see #getOffset(int,int,int,int,int,int) 476 * @see Calendar#ZONE_OFFSET 477 */ 478 public int getDSTSavings() { 479 if (useDaylightTime()) { 480 return 3600000; 481 } 482 return 0; 483 } 484 485 /** 486 * Queries if this {@code TimeZone} uses Daylight Saving Time. 487 * 488 * <p>If an underlying {@code TimeZone} implementation subclass 489 * supports historical and future Daylight Saving Time schedule 490 * changes, this method refers to the last known Daylight Saving Time 491 * rule that can be a future prediction and may not be the same as 492 * the current rule. Consider calling {@link #observesDaylightTime()} 493 * if the current rule should also be taken into account. 494 * 495 * @return {@code true} if this {@code TimeZone} uses Daylight Saving Time, 496 * {@code false}, otherwise. 497 * @see #inDaylightTime(Date) 498 * @see Calendar#DST_OFFSET 499 */ 500 public abstract boolean useDaylightTime(); 501 502 /** 503 * Returns {@code true} if this {@code TimeZone} is currently in 504 * Daylight Saving Time, or if a transition from Standard Time to 505 * Daylight Saving Time occurs at any future time. 506 * 507 * <p>The default implementation returns {@code true} if 508 * {@code useDaylightTime()} or {@code inDaylightTime(new Date())} 509 * returns {@code true}. 510 * 511 * @return {@code true} if this {@code TimeZone} is currently in 512 * Daylight Saving Time, or if a transition from Standard Time to 513 * Daylight Saving Time occurs at any future time; {@code false} 514 * otherwise. 515 * @since 1.7 516 * @see #useDaylightTime() 517 * @see #inDaylightTime(Date) 518 * @see Calendar#DST_OFFSET 519 */ 520 public boolean observesDaylightTime() { 521 return useDaylightTime() || inDaylightTime(new Date()); 522 } 523 524 /** 525 * Queries if the given {@code date} is in Daylight Saving Time in 526 * this time zone. 527 * 528 * @param date the given Date. 529 * @return {@code true} if the given date is in Daylight Saving Time, 530 * {@code false}, otherwise. 531 */ 532 abstract public boolean inDaylightTime(Date date); 533 534 /** 535 * Gets the <code>TimeZone</code> for the given ID. 536 * 537 * @param id the ID for a <code>TimeZone</code>, either an abbreviation 538 * such as "PST", a full name such as "America/Los_Angeles", or a custom 539 * ID such as "GMT-8:00". Note that the support of abbreviations is 540 * for JDK 1.1.x compatibility only and full names should be used. 541 * 542 * @return the specified <code>TimeZone</code>, or the GMT zone if the given ID 543 * cannot be understood. 544 */ 545 // Android changed param s/ID/id 546 public static synchronized TimeZone getTimeZone(String id) { 547 if (id == null) { 548 throw new NullPointerException("id == null"); 549 } 550 551 // Special cases? These can clone an existing instance. 552 if (id.length() == 3) { 553 if (id.equals("GMT")) { 554 return (TimeZone) GMT.clone(); 555 } 556 if (id.equals("UTC")) { 557 return (TimeZone) UTC.clone(); 558 } 559 } 560 561 // In the database? 562 TimeZone zone = null; 563 try { 564 zone = ZoneInfoDB.getInstance().makeTimeZone(id); 565 } catch (IOException ignored) { 566 } 567 568 // Custom time zone? 569 if (zone == null && id.length() > 3 && id.startsWith("GMT")) { 570 zone = getCustomTimeZone(id); 571 } 572 573 // We never return null; on failure we return the equivalent of "GMT". 574 return (zone != null) ? zone : (TimeZone) GMT.clone(); 575 } 576 577 /** 578 * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null. 579 */ 580 private static TimeZone getCustomTimeZone(String id) { 581 Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id); 582 if (!m.matches()) { 583 return null; 584 } 585 586 int hour; 587 int minute = 0; 588 try { 589 hour = Integer.parseInt(m.group(1)); 590 if (m.group(3) != null) { 591 minute = Integer.parseInt(m.group(3)); 592 } 593 } catch (NumberFormatException impossible) { 594 throw new AssertionError(impossible); 595 } 596 597 if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { 598 return null; 599 } 600 601 char sign = id.charAt(3); 602 int raw = (hour * 3600000) + (minute * 60000); 603 if (sign == '-') { 604 raw = -raw; 605 } 606 607 String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute); 608 return new SimpleTimeZone(raw, cleanId); 609 } 610 611 /** 612 * Gets the available IDs according to the given time zone offset in milliseconds. 613 * 614 * @param rawOffset the given time zone GMT offset in milliseconds. 615 * @return an array of IDs, where the time zone for that ID has 616 * the specified GMT offset. For example, "America/Phoenix" and "America/Denver" 617 * both have GMT-07:00, but differ in daylight saving behavior. 618 * @see #getRawOffset() 619 */ 620 public static synchronized String[] getAvailableIDs(int rawOffset) { 621 return ZoneInfoDB.getInstance().getAvailableIDs(rawOffset); 622 } 623 624 /** 625 * Gets all the available IDs supported. 626 * @return an array of IDs. 627 */ 628 public static synchronized String[] getAvailableIDs() { 629 return ZoneInfoDB.getInstance().getAvailableIDs(); 630 } 631 632 /** 633 * Gets the platform defined TimeZone ID. 634 **/ 635 private static native String getSystemTimeZoneID(String javaHome, 636 String country); 637 638 /** 639 * Gets the custom time zone ID based on the GMT offset of the 640 * platform. (e.g., "GMT+08:00") 641 */ 642 private static native String getSystemGMTOffsetID(); 643 644 /** 645 * Gets the default <code>TimeZone</code> for this host. 646 * The source of the default <code>TimeZone</code> 647 * may vary with implementation. 648 * @return a default <code>TimeZone</code>. 649 * @see #setDefault 650 */ 651 public static TimeZone getDefault() { 652 return (TimeZone) getDefaultRef().clone(); 653 } 654 655 /** 656 * Returns the reference to the default TimeZone object. This 657 * method doesn't create a clone. 658 */ 659 static synchronized TimeZone getDefaultRef() { 660 if (defaultTimeZone == null) { 661 TimezoneGetter tzGetter = TimezoneGetter.getInstance(); 662 String zoneName = (tzGetter != null) ? tzGetter.getId() : null; 663 if (zoneName != null) { 664 zoneName = zoneName.trim(); 665 } 666 if (zoneName == null || zoneName.isEmpty()) { 667 try { 668 // On the host, we can find the configured timezone here. 669 zoneName = IoUtils.readFileAsString("/etc/timezone"); 670 } catch (IOException ex) { 671 // "vogar --mode device" can end up here. 672 // TODO: give libcore access to Android system properties and read "persist.sys.timezone". 673 zoneName = "GMT"; 674 } 675 } 676 defaultTimeZone = TimeZone.getTimeZone(zoneName); 677 } 678 return defaultTimeZone; 679 } 680 681 private static boolean hasPermission() { 682 boolean hasPermission = true; 683 SecurityManager sm = System.getSecurityManager(); 684 if (sm != null) { 685 try { 686 sm.checkPermission(new PropertyPermission 687 ("user.timezone", "write")); 688 } catch (SecurityException e) { 689 hasPermission = false; 690 } 691 } 692 return hasPermission; 693 } 694 695 /** 696 * Sets the <code>TimeZone</code> that is 697 * returned by the <code>getDefault</code> method. If <code>zone</code> 698 * is null, reset the default to the value it had originally when the 699 * VM first started. 700 * @param timeZone the new default time zone 701 * @see #getDefault 702 */ 703 // Android changed s/zone/timeZone 704 public synchronized static void setDefault(TimeZone timeZone) 705 { 706 if (hasPermission()) { 707 defaultTimeZone = timeZone != null ? (TimeZone) timeZone.clone() : null; 708 } 709 } 710 711 /** 712 * Returns true if this zone has the same rule and offset as another zone. 713 * That is, if this zone differs only in ID, if at all. Returns false 714 * if the other zone is null. 715 * @param other the <code>TimeZone</code> object to be compared with 716 * @return true if the other zone is not null and is the same as this one, 717 * with the possible exception of the ID 718 * @since 1.2 719 */ 720 public boolean hasSameRules(TimeZone other) { 721 return other != null && getRawOffset() == other.getRawOffset() && 722 useDaylightTime() == other.useDaylightTime(); 723 } 724 725 /** 726 * Creates a copy of this <code>TimeZone</code>. 727 * 728 * @return a clone of this <code>TimeZone</code> 729 */ 730 public Object clone() 731 { 732 try { 733 TimeZone other = (TimeZone) super.clone(); 734 other.ID = ID; 735 return other; 736 } catch (CloneNotSupportedException e) { 737 throw new InternalError(); 738 } 739 } 740 741 /** 742 * The null constant as a TimeZone. 743 */ 744 static final TimeZone NO_TIMEZONE = null; 745 746 // =======================privates=============================== 747 748 /** 749 * The string identifier of this <code>TimeZone</code>. This is a 750 * programmatic identifier used internally to look up <code>TimeZone</code> 751 * objects from the system table and also to map them to their localized 752 * display names. <code>ID</code> values are unique in the system 753 * table but may not be for dynamically created zones. 754 * @serial 755 */ 756 private String ID; 757 private static volatile TimeZone defaultTimeZone; 758} 759