rt/emul/compact/src/main/java/java/util/TimeZone.java
branchjdk7-b147
changeset 1334 588d5bf7a560
child 1340 41046f76a76a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/rt/emul/compact/src/main/java/java/util/TimeZone.java	Thu Oct 03 15:40:35 2013 +0200
     1.3 @@ -0,0 +1,864 @@
     1.4 +/*
     1.5 + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Oracle designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Oracle in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.25 + * or visit www.oracle.com if you need additional information or have any
    1.26 + * questions.
    1.27 + */
    1.28 +
    1.29 +/*
    1.30 + * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
    1.31 + * (C) Copyright IBM Corp. 1996 - All Rights Reserved
    1.32 + *
    1.33 + *   The original version of this source code and documentation is copyrighted
    1.34 + * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
    1.35 + * materials are provided under terms of a License Agreement between Taligent
    1.36 + * and Sun. This technology is protected by multiple US and International
    1.37 + * patents. This notice and attribution to Taligent may not be removed.
    1.38 + *   Taligent is a registered trademark of Taligent, Inc.
    1.39 + *
    1.40 + */
    1.41 +
    1.42 +package java.util;
    1.43 +
    1.44 +import java.io.Serializable;
    1.45 +import java.lang.ref.SoftReference;
    1.46 +import java.security.AccessController;
    1.47 +import java.security.PrivilegedAction;
    1.48 +import java.util.concurrent.ConcurrentHashMap;
    1.49 +import sun.security.action.GetPropertyAction;
    1.50 +import sun.util.TimeZoneNameUtility;
    1.51 +import sun.util.calendar.ZoneInfo;
    1.52 +import sun.util.calendar.ZoneInfoFile;
    1.53 +
    1.54 +/**
    1.55 + * <code>TimeZone</code> represents a time zone offset, and also figures out daylight
    1.56 + * savings.
    1.57 + *
    1.58 + * <p>
    1.59 + * Typically, you get a <code>TimeZone</code> using <code>getDefault</code>
    1.60 + * which creates a <code>TimeZone</code> based on the time zone where the program
    1.61 + * is running. For example, for a program running in Japan, <code>getDefault</code>
    1.62 + * creates a <code>TimeZone</code> object based on Japanese Standard Time.
    1.63 + *
    1.64 + * <p>
    1.65 + * You can also get a <code>TimeZone</code> using <code>getTimeZone</code>
    1.66 + * along with a time zone ID. For instance, the time zone ID for the
    1.67 + * U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a
    1.68 + * U.S. Pacific Time <code>TimeZone</code> object with:
    1.69 + * <blockquote><pre>
    1.70 + * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
    1.71 + * </pre></blockquote>
    1.72 + * You can use the <code>getAvailableIDs</code> method to iterate through
    1.73 + * all the supported time zone IDs. You can then choose a
    1.74 + * supported ID to get a <code>TimeZone</code>.
    1.75 + * If the time zone you want is not represented by one of the
    1.76 + * supported IDs, then a custom time zone ID can be specified to
    1.77 + * produce a TimeZone. The syntax of a custom time zone ID is:
    1.78 + *
    1.79 + * <blockquote><pre>
    1.80 + * <a name="CustomID"><i>CustomID:</i></a>
    1.81 + *         <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
    1.82 + *         <code>GMT</code> <i>Sign</i> <i>Hours</i> <i>Minutes</i>
    1.83 + *         <code>GMT</code> <i>Sign</i> <i>Hours</i>
    1.84 + * <i>Sign:</i> one of
    1.85 + *         <code>+ -</code>
    1.86 + * <i>Hours:</i>
    1.87 + *         <i>Digit</i>
    1.88 + *         <i>Digit</i> <i>Digit</i>
    1.89 + * <i>Minutes:</i>
    1.90 + *         <i>Digit</i> <i>Digit</i>
    1.91 + * <i>Digit:</i> one of
    1.92 + *         <code>0 1 2 3 4 5 6 7 8 9</code>
    1.93 + * </pre></blockquote>
    1.94 + *
    1.95 + * <i>Hours</i> must be between 0 to 23 and <i>Minutes</i> must be
    1.96 + * between 00 to 59.  For example, "GMT+10" and "GMT+0010" mean ten
    1.97 + * hours and ten minutes ahead of GMT, respectively.
    1.98 + * <p>
    1.99 + * The format is locale independent and digits must be taken from the
   1.100 + * Basic Latin block of the Unicode standard. No daylight saving time
   1.101 + * transition schedule can be specified with a custom time zone ID. If
   1.102 + * the specified string doesn't match the syntax, <code>"GMT"</code>
   1.103 + * is used.
   1.104 + * <p>
   1.105 + * When creating a <code>TimeZone</code>, the specified custom time
   1.106 + * zone ID is normalized in the following syntax:
   1.107 + * <blockquote><pre>
   1.108 + * <a name="NormalizedCustomID"><i>NormalizedCustomID:</i></a>
   1.109 + *         <code>GMT</code> <i>Sign</i> <i>TwoDigitHours</i> <code>:</code> <i>Minutes</i>
   1.110 + * <i>Sign:</i> one of
   1.111 + *         <code>+ -</code>
   1.112 + * <i>TwoDigitHours:</i>
   1.113 + *         <i>Digit</i> <i>Digit</i>
   1.114 + * <i>Minutes:</i>
   1.115 + *         <i>Digit</i> <i>Digit</i>
   1.116 + * <i>Digit:</i> one of
   1.117 + *         <code>0 1 2 3 4 5 6 7 8 9</code>
   1.118 + * </pre></blockquote>
   1.119 + * For example, TimeZone.getTimeZone("GMT-8").getID() returns "GMT-08:00".
   1.120 + *
   1.121 + * <h4>Three-letter time zone IDs</h4>
   1.122 + *
   1.123 + * For compatibility with JDK 1.1.x, some other three-letter time zone IDs
   1.124 + * (such as "PST", "CTT", "AST") are also supported. However, <strong>their
   1.125 + * use is deprecated</strong> because the same abbreviation is often used
   1.126 + * for multiple time zones (for example, "CST" could be U.S. "Central Standard
   1.127 + * Time" and "China Standard Time"), and the Java platform can then only
   1.128 + * recognize one of them.
   1.129 + *
   1.130 + *
   1.131 + * @see          Calendar
   1.132 + * @see          GregorianCalendar
   1.133 + * @see          SimpleTimeZone
   1.134 + * @author       Mark Davis, David Goldsmith, Chen-Lieh Huang, Alan Liu
   1.135 + * @since        JDK1.1
   1.136 + */
   1.137 +abstract public class TimeZone implements Serializable, Cloneable {
   1.138 +    /**
   1.139 +     * Sole constructor.  (For invocation by subclass constructors, typically
   1.140 +     * implicit.)
   1.141 +     */
   1.142 +    public TimeZone() {
   1.143 +    }
   1.144 +
   1.145 +    /**
   1.146 +     * A style specifier for <code>getDisplayName()</code> indicating
   1.147 +     * a short name, such as "PST."
   1.148 +     * @see #LONG
   1.149 +     * @since 1.2
   1.150 +     */
   1.151 +    public static final int SHORT = 0;
   1.152 +
   1.153 +    /**
   1.154 +     * A style specifier for <code>getDisplayName()</code> indicating
   1.155 +     * a long name, such as "Pacific Standard Time."
   1.156 +     * @see #SHORT
   1.157 +     * @since 1.2
   1.158 +     */
   1.159 +    public static final int LONG  = 1;
   1.160 +
   1.161 +    // Constants used internally; unit is milliseconds
   1.162 +    private static final int ONE_MINUTE = 60*1000;
   1.163 +    private static final int ONE_HOUR   = 60*ONE_MINUTE;
   1.164 +    private static final int ONE_DAY    = 24*ONE_HOUR;
   1.165 +
   1.166 +    // Proclaim serialization compatibility with JDK 1.1
   1.167 +    static final long serialVersionUID = 3581463369166924961L;
   1.168 +
   1.169 +    /**
   1.170 +     * Gets the time zone offset, for current date, modified in case of
   1.171 +     * daylight savings. This is the offset to add to UTC to get local time.
   1.172 +     * <p>
   1.173 +     * This method returns a historically correct offset if an
   1.174 +     * underlying <code>TimeZone</code> implementation subclass
   1.175 +     * supports historical Daylight Saving Time schedule and GMT
   1.176 +     * offset changes.
   1.177 +     *
   1.178 +     * @param era the era of the given date.
   1.179 +     * @param year the year in the given date.
   1.180 +     * @param month the month in the given date.
   1.181 +     * Month is 0-based. e.g., 0 for January.
   1.182 +     * @param day the day-in-month of the given date.
   1.183 +     * @param dayOfWeek the day-of-week of the given date.
   1.184 +     * @param milliseconds the milliseconds in day in <em>standard</em>
   1.185 +     * local time.
   1.186 +     *
   1.187 +     * @return the offset in milliseconds to add to GMT to get local time.
   1.188 +     *
   1.189 +     * @see Calendar#ZONE_OFFSET
   1.190 +     * @see Calendar#DST_OFFSET
   1.191 +     */
   1.192 +    public abstract int getOffset(int era, int year, int month, int day,
   1.193 +                                  int dayOfWeek, int milliseconds);
   1.194 +
   1.195 +    /**
   1.196 +     * Returns the offset of this time zone from UTC at the specified
   1.197 +     * date. If Daylight Saving Time is in effect at the specified
   1.198 +     * date, the offset value is adjusted with the amount of daylight
   1.199 +     * saving.
   1.200 +     * <p>
   1.201 +     * This method returns a historically correct offset value if an
   1.202 +     * underlying TimeZone implementation subclass supports historical
   1.203 +     * Daylight Saving Time schedule and GMT offset changes.
   1.204 +     *
   1.205 +     * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT
   1.206 +     * @return the amount of time in milliseconds to add to UTC to get local time.
   1.207 +     *
   1.208 +     * @see Calendar#ZONE_OFFSET
   1.209 +     * @see Calendar#DST_OFFSET
   1.210 +     * @since 1.4
   1.211 +     */
   1.212 +    public int getOffset(long date) {
   1.213 +        if (inDaylightTime(new Date(date))) {
   1.214 +            return getRawOffset() + getDSTSavings();
   1.215 +        }
   1.216 +        return getRawOffset();
   1.217 +    }
   1.218 +
   1.219 +    /**
   1.220 +     * Gets the raw GMT offset and the amount of daylight saving of this
   1.221 +     * time zone at the given time.
   1.222 +     * @param date the milliseconds (since January 1, 1970,
   1.223 +     * 00:00:00.000 GMT) at which the time zone offset and daylight
   1.224 +     * saving amount are found
   1.225 +     * @param offset an array of int where the raw GMT offset
   1.226 +     * (offset[0]) and daylight saving amount (offset[1]) are stored,
   1.227 +     * or null if those values are not needed. The method assumes that
   1.228 +     * the length of the given array is two or larger.
   1.229 +     * @return the total amount of the raw GMT offset and daylight
   1.230 +     * saving at the specified date.
   1.231 +     *
   1.232 +     * @see Calendar#ZONE_OFFSET
   1.233 +     * @see Calendar#DST_OFFSET
   1.234 +     */
   1.235 +    int getOffsets(long date, int[] offsets) {
   1.236 +        int rawoffset = getRawOffset();
   1.237 +        int dstoffset = 0;
   1.238 +        if (inDaylightTime(new Date(date))) {
   1.239 +            dstoffset = getDSTSavings();
   1.240 +        }
   1.241 +        if (offsets != null) {
   1.242 +            offsets[0] = rawoffset;
   1.243 +            offsets[1] = dstoffset;
   1.244 +        }
   1.245 +        return rawoffset + dstoffset;
   1.246 +    }
   1.247 +
   1.248 +    /**
   1.249 +     * Sets the base time zone offset to GMT.
   1.250 +     * This is the offset to add to UTC to get local time.
   1.251 +     * <p>
   1.252 +     * If an underlying <code>TimeZone</code> implementation subclass
   1.253 +     * supports historical GMT offset changes, the specified GMT
   1.254 +     * offset is set as the latest GMT offset and the difference from
   1.255 +     * the known latest GMT offset value is used to adjust all
   1.256 +     * historical GMT offset values.
   1.257 +     *
   1.258 +     * @param offsetMillis the given base time zone offset to GMT.
   1.259 +     */
   1.260 +    abstract public void setRawOffset(int offsetMillis);
   1.261 +
   1.262 +    /**
   1.263 +     * Returns the amount of time in milliseconds to add to UTC to get
   1.264 +     * standard time in this time zone. Because this value is not
   1.265 +     * affected by daylight saving time, it is called <I>raw
   1.266 +     * offset</I>.
   1.267 +     * <p>
   1.268 +     * If an underlying <code>TimeZone</code> implementation subclass
   1.269 +     * supports historical GMT offset changes, the method returns the
   1.270 +     * raw offset value of the current date. In Honolulu, for example,
   1.271 +     * its raw offset changed from GMT-10:30 to GMT-10:00 in 1947, and
   1.272 +     * this method always returns -36000000 milliseconds (i.e., -10
   1.273 +     * hours).
   1.274 +     *
   1.275 +     * @return the amount of raw offset time in milliseconds to add to UTC.
   1.276 +     * @see Calendar#ZONE_OFFSET
   1.277 +     */
   1.278 +    public abstract int getRawOffset();
   1.279 +
   1.280 +    /**
   1.281 +     * Gets the ID of this time zone.
   1.282 +     * @return the ID of this time zone.
   1.283 +     */
   1.284 +    public String getID()
   1.285 +    {
   1.286 +        return ID;
   1.287 +    }
   1.288 +
   1.289 +    /**
   1.290 +     * Sets the time zone ID. This does not change any other data in
   1.291 +     * the time zone object.
   1.292 +     * @param ID the new time zone ID.
   1.293 +     */
   1.294 +    public void setID(String ID)
   1.295 +    {
   1.296 +        if (ID == null) {
   1.297 +            throw new NullPointerException();
   1.298 +        }
   1.299 +        this.ID = ID;
   1.300 +    }
   1.301 +
   1.302 +    /**
   1.303 +     * Returns a long standard time name of this {@code TimeZone} suitable for
   1.304 +     * presentation to the user in the default locale.
   1.305 +     *
   1.306 +     * <p>This method is equivalent to:
   1.307 +     * <pre><blockquote>
   1.308 +     * getDisplayName(false, {@link #LONG},
   1.309 +     *                Locale.getDefault({@link Locale.Category#DISPLAY}))
   1.310 +     * </blockquote></pre>
   1.311 +     *
   1.312 +     * @return the human-readable name of this time zone in the default locale.
   1.313 +     * @since 1.2
   1.314 +     * @see #getDisplayName(boolean, int, Locale)
   1.315 +     * @see Locale#getDefault(Locale.Category)
   1.316 +     * @see Locale.Category
   1.317 +     */
   1.318 +    public final String getDisplayName() {
   1.319 +        return getDisplayName(false, LONG,
   1.320 +                              Locale.getDefault(Locale.Category.DISPLAY));
   1.321 +    }
   1.322 +
   1.323 +    /**
   1.324 +     * Returns a long standard time name of this {@code TimeZone} suitable for
   1.325 +     * presentation to the user in the specified {@code locale}.
   1.326 +     *
   1.327 +     * <p>This method is equivalent to:
   1.328 +     * <pre><blockquote>
   1.329 +     * getDisplayName(false, {@link #LONG}, locale)
   1.330 +     * </blockquote></pre>
   1.331 +     *
   1.332 +     * @param locale the locale in which to supply the display name.
   1.333 +     * @return the human-readable name of this time zone in the given locale.
   1.334 +     * @exception NullPointerException if {@code locale} is {@code null}.
   1.335 +     * @since 1.2
   1.336 +     * @see #getDisplayName(boolean, int, Locale)
   1.337 +     */
   1.338 +    public final String getDisplayName(Locale locale) {
   1.339 +        return getDisplayName(false, LONG, locale);
   1.340 +    }
   1.341 +
   1.342 +    /**
   1.343 +     * Returns a name in the specified {@code style} of this {@code TimeZone}
   1.344 +     * suitable for presentation to the user in the default locale. If the
   1.345 +     * specified {@code daylight} is {@code true}, a Daylight Saving Time name
   1.346 +     * is returned (even if this {@code TimeZone} doesn't observe Daylight Saving
   1.347 +     * Time). Otherwise, a Standard Time name is returned.
   1.348 +     *
   1.349 +     * <p>This method is equivalent to:
   1.350 +     * <pre><blockquote>
   1.351 +     * getDisplayName(daylight, style,
   1.352 +     *                Locale.getDefault({@link Locale.Category#DISPLAY}))
   1.353 +     * </blockquote></pre>
   1.354 +     *
   1.355 +     * @param daylight {@code true} specifying a Daylight Saving Time name, or
   1.356 +     *                 {@code false} specifying a Standard Time name
   1.357 +     * @param style either {@link #LONG} or {@link #SHORT}
   1.358 +     * @return the human-readable name of this time zone in the default locale.
   1.359 +     * @exception IllegalArgumentException if {@code style} is invalid.
   1.360 +     * @since 1.2
   1.361 +     * @see #getDisplayName(boolean, int, Locale)
   1.362 +     * @see Locale#getDefault(Locale.Category)
   1.363 +     * @see Locale.Category
   1.364 +     * @see java.text.DateFormatSymbols#getZoneStrings()
   1.365 +     */
   1.366 +    public final String getDisplayName(boolean daylight, int style) {
   1.367 +        return getDisplayName(daylight, style,
   1.368 +                              Locale.getDefault(Locale.Category.DISPLAY));
   1.369 +    }
   1.370 +
   1.371 +    /**
   1.372 +     * Returns a name in the specified {@code style} of this {@code TimeZone}
   1.373 +     * suitable for presentation to the user in the specified {@code
   1.374 +     * locale}. If the specified {@code daylight} is {@code true}, a Daylight
   1.375 +     * Saving Time name is returned (even if this {@code TimeZone} doesn't
   1.376 +     * observe Daylight Saving Time). Otherwise, a Standard Time name is
   1.377 +     * returned.
   1.378 +     *
   1.379 +     * <p>When looking up a time zone name, the {@linkplain
   1.380 +     * ResourceBundle.Control#getCandidateLocales(String,Locale) default
   1.381 +     * <code>Locale</code> search path of <code>ResourceBundle</code>} derived
   1.382 +     * from the specified {@code locale} is used. (No {@linkplain
   1.383 +     * ResourceBundle.Control#getFallbackLocale(String,Locale) fallback
   1.384 +     * <code>Locale</code>} search is performed.) If a time zone name in any
   1.385 +     * {@code Locale} of the search path, including {@link Locale#ROOT}, is
   1.386 +     * found, the name is returned. Otherwise, a string in the
   1.387 +     * <a href="#NormalizedCustomID">normalized custom ID format</a> is returned.
   1.388 +     *
   1.389 +     * @param daylight {@code true} specifying a Daylight Saving Time name, or
   1.390 +     *                 {@code false} specifying a Standard Time name
   1.391 +     * @param style either {@link #LONG} or {@link #SHORT}
   1.392 +     * @param locale   the locale in which to supply the display name.
   1.393 +     * @return the human-readable name of this time zone in the given locale.
   1.394 +     * @exception IllegalArgumentException if {@code style} is invalid.
   1.395 +     * @exception NullPointerException if {@code locale} is {@code null}.
   1.396 +     * @since 1.2
   1.397 +     * @see java.text.DateFormatSymbols#getZoneStrings()
   1.398 +     */
   1.399 +    public String getDisplayName(boolean daylight, int style, Locale locale) {
   1.400 +        if (style != SHORT && style != LONG) {
   1.401 +            throw new IllegalArgumentException("Illegal style: " + style);
   1.402 +        }
   1.403 +
   1.404 +        String id = getID();
   1.405 +        String[] names = getDisplayNames(id, locale);
   1.406 +        if (names == null) {
   1.407 +            if (id.startsWith("GMT")) {
   1.408 +                char sign = id.charAt(3);
   1.409 +                if (sign == '+' || sign == '-') {
   1.410 +                    return id;
   1.411 +                }
   1.412 +            }
   1.413 +            int offset = getRawOffset();
   1.414 +            if (daylight) {
   1.415 +                offset += getDSTSavings();
   1.416 +            }
   1.417 +            return ZoneInfoFile.toCustomID(offset);
   1.418 +        }
   1.419 +
   1.420 +        int index = daylight ? 3 : 1;
   1.421 +        if (style == SHORT) {
   1.422 +            index++;
   1.423 +        }
   1.424 +        return names[index];
   1.425 +    }
   1.426 +
   1.427 +    private static class DisplayNames {
   1.428 +        // Cache for managing display names per timezone per locale
   1.429 +        // The structure is:
   1.430 +        //   Map(key=id, value=SoftReference(Map(key=locale, value=displaynames)))
   1.431 +        private static final Map<String, SoftReference<Map<Locale, String[]>>> CACHE =
   1.432 +            new ConcurrentHashMap<String, SoftReference<Map<Locale, String[]>>>();
   1.433 +    }
   1.434 +
   1.435 +    private static final String[] getDisplayNames(String id, Locale locale) {
   1.436 +        Map<String, SoftReference<Map<Locale, String[]>>> displayNames = DisplayNames.CACHE;
   1.437 +
   1.438 +        SoftReference<Map<Locale, String[]>> ref = displayNames.get(id);
   1.439 +        if (ref != null) {
   1.440 +            Map<Locale, String[]> perLocale = ref.get();
   1.441 +            if (perLocale != null) {
   1.442 +                String[] names = perLocale.get(locale);
   1.443 +                if (names != null) {
   1.444 +                    return names;
   1.445 +                }
   1.446 +                names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
   1.447 +                if (names != null) {
   1.448 +                    perLocale.put(locale, names);
   1.449 +                }
   1.450 +                return names;
   1.451 +            }
   1.452 +        }
   1.453 +
   1.454 +        String[] names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
   1.455 +        if (names != null) {
   1.456 +            Map<Locale, String[]> perLocale = new ConcurrentHashMap<Locale, String[]>();
   1.457 +            perLocale.put(locale, names);
   1.458 +            ref = new SoftReference<Map<Locale, String[]>>(perLocale);
   1.459 +            displayNames.put(id, ref);
   1.460 +        }
   1.461 +        return names;
   1.462 +    }
   1.463 +
   1.464 +    /**
   1.465 +     * Returns the amount of time to be added to local standard time
   1.466 +     * to get local wall clock time.
   1.467 +     *
   1.468 +     * <p>The default implementation returns 3600000 milliseconds
   1.469 +     * (i.e., one hour) if a call to {@link #useDaylightTime()}
   1.470 +     * returns {@code true}. Otherwise, 0 (zero) is returned.
   1.471 +     *
   1.472 +     * <p>If an underlying {@code TimeZone} implementation subclass
   1.473 +     * supports historical and future Daylight Saving Time schedule
   1.474 +     * changes, this method returns the amount of saving time of the
   1.475 +     * last known Daylight Saving Time rule that can be a future
   1.476 +     * prediction.
   1.477 +     *
   1.478 +     * <p>If the amount of saving time at any given time stamp is
   1.479 +     * required, construct a {@link Calendar} with this {@code
   1.480 +     * TimeZone} and the time stamp, and call {@link Calendar#get(int)
   1.481 +     * Calendar.get}{@code (}{@link Calendar#DST_OFFSET}{@code )}.
   1.482 +     *
   1.483 +     * @return the amount of saving time in milliseconds
   1.484 +     * @since 1.4
   1.485 +     * @see #inDaylightTime(Date)
   1.486 +     * @see #getOffset(long)
   1.487 +     * @see #getOffset(int,int,int,int,int,int)
   1.488 +     * @see Calendar#ZONE_OFFSET
   1.489 +     */
   1.490 +    public int getDSTSavings() {
   1.491 +        if (useDaylightTime()) {
   1.492 +            return 3600000;
   1.493 +        }
   1.494 +        return 0;
   1.495 +    }
   1.496 +
   1.497 +    /**
   1.498 +     * Queries if this {@code TimeZone} uses Daylight Saving Time.
   1.499 +     *
   1.500 +     * <p>If an underlying {@code TimeZone} implementation subclass
   1.501 +     * supports historical and future Daylight Saving Time schedule
   1.502 +     * changes, this method refers to the last known Daylight Saving Time
   1.503 +     * rule that can be a future prediction and may not be the same as
   1.504 +     * the current rule. Consider calling {@link #observesDaylightTime()}
   1.505 +     * if the current rule should also be taken into account.
   1.506 +     *
   1.507 +     * @return {@code true} if this {@code TimeZone} uses Daylight Saving Time,
   1.508 +     *         {@code false}, otherwise.
   1.509 +     * @see #inDaylightTime(Date)
   1.510 +     * @see Calendar#DST_OFFSET
   1.511 +     */
   1.512 +    public abstract boolean useDaylightTime();
   1.513 +
   1.514 +    /**
   1.515 +     * Returns {@code true} if this {@code TimeZone} is currently in
   1.516 +     * Daylight Saving Time, or if a transition from Standard Time to
   1.517 +     * Daylight Saving Time occurs at any future time.
   1.518 +     *
   1.519 +     * <p>The default implementation returns {@code true} if
   1.520 +     * {@code useDaylightTime()} or {@code inDaylightTime(new Date())}
   1.521 +     * returns {@code true}.
   1.522 +     *
   1.523 +     * @return {@code true} if this {@code TimeZone} is currently in
   1.524 +     * Daylight Saving Time, or if a transition from Standard Time to
   1.525 +     * Daylight Saving Time occurs at any future time; {@code false}
   1.526 +     * otherwise.
   1.527 +     * @since 1.7
   1.528 +     * @see #useDaylightTime()
   1.529 +     * @see #inDaylightTime(Date)
   1.530 +     * @see Calendar#DST_OFFSET
   1.531 +     */
   1.532 +    public boolean observesDaylightTime() {
   1.533 +        return useDaylightTime() || inDaylightTime(new Date());
   1.534 +    }
   1.535 +
   1.536 +    /**
   1.537 +     * Queries if the given {@code date} is in Daylight Saving Time in
   1.538 +     * this time zone.
   1.539 +     *
   1.540 +     * @param date the given Date.
   1.541 +     * @return {@code true} if the given date is in Daylight Saving Time,
   1.542 +     *         {@code false}, otherwise.
   1.543 +     */
   1.544 +    abstract public boolean inDaylightTime(Date date);
   1.545 +
   1.546 +    /**
   1.547 +     * Gets the <code>TimeZone</code> for the given ID.
   1.548 +     *
   1.549 +     * @param ID the ID for a <code>TimeZone</code>, either an abbreviation
   1.550 +     * such as "PST", a full name such as "America/Los_Angeles", or a custom
   1.551 +     * ID such as "GMT-8:00". Note that the support of abbreviations is
   1.552 +     * for JDK 1.1.x compatibility only and full names should be used.
   1.553 +     *
   1.554 +     * @return the specified <code>TimeZone</code>, or the GMT zone if the given ID
   1.555 +     * cannot be understood.
   1.556 +     */
   1.557 +    public static synchronized TimeZone getTimeZone(String ID) {
   1.558 +        return getTimeZone(ID, true);
   1.559 +    }
   1.560 +
   1.561 +    private static TimeZone getTimeZone(String ID, boolean fallback) {
   1.562 +        TimeZone tz = ZoneInfo.getTimeZone(ID);
   1.563 +        if (tz == null) {
   1.564 +            tz = parseCustomTimeZone(ID);
   1.565 +            if (tz == null && fallback) {
   1.566 +                tz = new ZoneInfo(GMT_ID, 0);
   1.567 +            }
   1.568 +        }
   1.569 +        return tz;
   1.570 +    }
   1.571 +
   1.572 +    /**
   1.573 +     * Gets the available IDs according to the given time zone offset in milliseconds.
   1.574 +     *
   1.575 +     * @param rawOffset the given time zone GMT offset in milliseconds.
   1.576 +     * @return an array of IDs, where the time zone for that ID has
   1.577 +     * the specified GMT offset. For example, "America/Phoenix" and "America/Denver"
   1.578 +     * both have GMT-07:00, but differ in daylight saving behavior.
   1.579 +     * @see #getRawOffset()
   1.580 +     */
   1.581 +    public static synchronized String[] getAvailableIDs(int rawOffset) {
   1.582 +        return ZoneInfo.getAvailableIDs(rawOffset);
   1.583 +    }
   1.584 +
   1.585 +    /**
   1.586 +     * Gets all the available IDs supported.
   1.587 +     * @return an array of IDs.
   1.588 +     */
   1.589 +    public static synchronized String[] getAvailableIDs() {
   1.590 +        return ZoneInfo.getAvailableIDs();
   1.591 +    }
   1.592 +
   1.593 +    /**
   1.594 +     * Gets the platform defined TimeZone ID.
   1.595 +     **/
   1.596 +    private static native String getSystemTimeZoneID(String javaHome,
   1.597 +                                                     String country);
   1.598 +
   1.599 +    /**
   1.600 +     * Gets the custom time zone ID based on the GMT offset of the
   1.601 +     * platform. (e.g., "GMT+08:00")
   1.602 +     */
   1.603 +    private static native String getSystemGMTOffsetID();
   1.604 +
   1.605 +    /**
   1.606 +     * Gets the default <code>TimeZone</code> for this host.
   1.607 +     * The source of the default <code>TimeZone</code>
   1.608 +     * may vary with implementation.
   1.609 +     * @return a default <code>TimeZone</code>.
   1.610 +     * @see #setDefault
   1.611 +     */
   1.612 +    public static TimeZone getDefault() {
   1.613 +        return (TimeZone) getDefaultRef().clone();
   1.614 +    }
   1.615 +
   1.616 +    /**
   1.617 +     * Returns the reference to the default TimeZone object. This
   1.618 +     * method doesn't create a clone.
   1.619 +     */
   1.620 +    static TimeZone getDefaultRef() {
   1.621 +        TimeZone defaultZone = defaultZoneTL.get();
   1.622 +        if (defaultZone == null) {
   1.623 +            defaultZone = defaultTimeZone;
   1.624 +            if (defaultZone == null) {
   1.625 +                // Need to initialize the default time zone.
   1.626 +                defaultZone = setDefaultZone();
   1.627 +                assert defaultZone != null;
   1.628 +            }
   1.629 +        }
   1.630 +        // Don't clone here.
   1.631 +        return defaultZone;
   1.632 +    }
   1.633 +
   1.634 +    private static synchronized TimeZone setDefaultZone() {
   1.635 +        TimeZone tz = null;
   1.636 +        // get the time zone ID from the system properties
   1.637 +        String zoneID = AccessController.doPrivileged(
   1.638 +                new GetPropertyAction("user.timezone"));
   1.639 +
   1.640 +        // if the time zone ID is not set (yet), perform the
   1.641 +        // platform to Java time zone ID mapping.
   1.642 +        if (zoneID == null || zoneID.equals("")) {
   1.643 +            String country = AccessController.doPrivileged(
   1.644 +                    new GetPropertyAction("user.country"));
   1.645 +            String javaHome = AccessController.doPrivileged(
   1.646 +                    new GetPropertyAction("java.home"));
   1.647 +            try {
   1.648 +                zoneID = getSystemTimeZoneID(javaHome, country);
   1.649 +                if (zoneID == null) {
   1.650 +                    zoneID = GMT_ID;
   1.651 +                }
   1.652 +            } catch (NullPointerException e) {
   1.653 +                zoneID = GMT_ID;
   1.654 +            }
   1.655 +        }
   1.656 +
   1.657 +        // Get the time zone for zoneID. But not fall back to
   1.658 +        // "GMT" here.
   1.659 +        tz = getTimeZone(zoneID, false);
   1.660 +
   1.661 +        if (tz == null) {
   1.662 +            // If the given zone ID is unknown in Java, try to
   1.663 +            // get the GMT-offset-based time zone ID,
   1.664 +            // a.k.a. custom time zone ID (e.g., "GMT-08:00").
   1.665 +            String gmtOffsetID = getSystemGMTOffsetID();
   1.666 +            if (gmtOffsetID != null) {
   1.667 +                zoneID = gmtOffsetID;
   1.668 +            }
   1.669 +            tz = getTimeZone(zoneID, true);
   1.670 +        }
   1.671 +        assert tz != null;
   1.672 +
   1.673 +        final String id = zoneID;
   1.674 +        AccessController.doPrivileged(new PrivilegedAction<Object>() {
   1.675 +                public Object run() {
   1.676 +                    System.setProperty("user.timezone", id);
   1.677 +                    return null;
   1.678 +                }
   1.679 +            });
   1.680 +
   1.681 +        defaultTimeZone = tz;
   1.682 +        return tz;
   1.683 +    }
   1.684 +
   1.685 +    private static boolean hasPermission() {
   1.686 +        boolean hasPermission = true;
   1.687 +        SecurityManager sm = System.getSecurityManager();
   1.688 +        if (sm != null) {
   1.689 +            try {
   1.690 +                sm.checkPermission(new PropertyPermission
   1.691 +                                   ("user.timezone", "write"));
   1.692 +            } catch (SecurityException e) {
   1.693 +                hasPermission = false;
   1.694 +            }
   1.695 +        }
   1.696 +        return hasPermission;
   1.697 +    }
   1.698 +
   1.699 +    /**
   1.700 +     * Sets the <code>TimeZone</code> that is
   1.701 +     * returned by the <code>getDefault</code> method.  If <code>zone</code>
   1.702 +     * is null, reset the default to the value it had originally when the
   1.703 +     * VM first started.
   1.704 +     * @param zone the new default time zone
   1.705 +     * @see #getDefault
   1.706 +     */
   1.707 +    public static void setDefault(TimeZone zone)
   1.708 +    {
   1.709 +        if (hasPermission()) {
   1.710 +            synchronized (TimeZone.class) {
   1.711 +                defaultTimeZone = zone;
   1.712 +                defaultZoneTL.set(null);
   1.713 +            }
   1.714 +        } else {
   1.715 +            defaultZoneTL.set(zone);
   1.716 +        }
   1.717 +    }
   1.718 +
   1.719 +    /**
   1.720 +     * Returns true if this zone has the same rule and offset as another zone.
   1.721 +     * That is, if this zone differs only in ID, if at all.  Returns false
   1.722 +     * if the other zone is null.
   1.723 +     * @param other the <code>TimeZone</code> object to be compared with
   1.724 +     * @return true if the other zone is not null and is the same as this one,
   1.725 +     * with the possible exception of the ID
   1.726 +     * @since 1.2
   1.727 +     */
   1.728 +    public boolean hasSameRules(TimeZone other) {
   1.729 +        return other != null && getRawOffset() == other.getRawOffset() &&
   1.730 +            useDaylightTime() == other.useDaylightTime();
   1.731 +    }
   1.732 +
   1.733 +    /**
   1.734 +     * Creates a copy of this <code>TimeZone</code>.
   1.735 +     *
   1.736 +     * @return a clone of this <code>TimeZone</code>
   1.737 +     */
   1.738 +    public Object clone()
   1.739 +    {
   1.740 +        try {
   1.741 +            TimeZone other = (TimeZone) super.clone();
   1.742 +            other.ID = ID;
   1.743 +            return other;
   1.744 +        } catch (CloneNotSupportedException e) {
   1.745 +            throw new InternalError();
   1.746 +        }
   1.747 +    }
   1.748 +
   1.749 +    /**
   1.750 +     * The null constant as a TimeZone.
   1.751 +     */
   1.752 +    static final TimeZone NO_TIMEZONE = null;
   1.753 +
   1.754 +    // =======================privates===============================
   1.755 +
   1.756 +    /**
   1.757 +     * The string identifier of this <code>TimeZone</code>.  This is a
   1.758 +     * programmatic identifier used internally to look up <code>TimeZone</code>
   1.759 +     * objects from the system table and also to map them to their localized
   1.760 +     * display names.  <code>ID</code> values are unique in the system
   1.761 +     * table but may not be for dynamically created zones.
   1.762 +     * @serial
   1.763 +     */
   1.764 +    private String           ID;
   1.765 +    private static volatile TimeZone defaultTimeZone;
   1.766 +    private static final InheritableThreadLocal<TimeZone> defaultZoneTL
   1.767 +                                        = new InheritableThreadLocal<TimeZone>();
   1.768 +
   1.769 +    static final String         GMT_ID        = "GMT";
   1.770 +    private static final int    GMT_ID_LENGTH = 3;
   1.771 +
   1.772 +    /**
   1.773 +     * Parses a custom time zone identifier and returns a corresponding zone.
   1.774 +     * This method doesn't support the RFC 822 time zone format. (e.g., +hhmm)
   1.775 +     *
   1.776 +     * @param id a string of the <a href="#CustomID">custom ID form</a>.
   1.777 +     * @return a newly created TimeZone with the given offset and
   1.778 +     * no daylight saving time, or null if the id cannot be parsed.
   1.779 +     */
   1.780 +    private static final TimeZone parseCustomTimeZone(String id) {
   1.781 +        int length;
   1.782 +
   1.783 +        // Error if the length of id isn't long enough or id doesn't
   1.784 +        // start with "GMT".
   1.785 +        if ((length = id.length()) < (GMT_ID_LENGTH + 2) ||
   1.786 +            id.indexOf(GMT_ID) != 0) {
   1.787 +            return null;
   1.788 +        }
   1.789 +
   1.790 +        ZoneInfo zi;
   1.791 +
   1.792 +        // First, we try to find it in the cache with the given
   1.793 +        // id. Even the id is not normalized, the returned ZoneInfo
   1.794 +        // should have its normalized id.
   1.795 +        zi = ZoneInfoFile.getZoneInfo(id);
   1.796 +        if (zi != null) {
   1.797 +            return zi;
   1.798 +        }
   1.799 +
   1.800 +        int index = GMT_ID_LENGTH;
   1.801 +        boolean negative = false;
   1.802 +        char c = id.charAt(index++);
   1.803 +        if (c == '-') {
   1.804 +            negative = true;
   1.805 +        } else if (c != '+') {
   1.806 +            return null;
   1.807 +        }
   1.808 +
   1.809 +        int hours = 0;
   1.810 +        int num = 0;
   1.811 +        int countDelim = 0;
   1.812 +        int len = 0;
   1.813 +        while (index < length) {
   1.814 +            c = id.charAt(index++);
   1.815 +            if (c == ':') {
   1.816 +                if (countDelim > 0) {
   1.817 +                    return null;
   1.818 +                }
   1.819 +                if (len > 2) {
   1.820 +                    return null;
   1.821 +                }
   1.822 +                hours = num;
   1.823 +                countDelim++;
   1.824 +                num = 0;
   1.825 +                len = 0;
   1.826 +                continue;
   1.827 +            }
   1.828 +            if (c < '0' || c > '9') {
   1.829 +                return null;
   1.830 +            }
   1.831 +            num = num * 10 + (c - '0');
   1.832 +            len++;
   1.833 +        }
   1.834 +        if (index != length) {
   1.835 +            return null;
   1.836 +        }
   1.837 +        if (countDelim == 0) {
   1.838 +            if (len <= 2) {
   1.839 +                hours = num;
   1.840 +                num = 0;
   1.841 +            } else {
   1.842 +                hours = num / 100;
   1.843 +                num %= 100;
   1.844 +            }
   1.845 +        } else {
   1.846 +            if (len != 2) {
   1.847 +                return null;
   1.848 +            }
   1.849 +        }
   1.850 +        if (hours > 23 || num > 59) {
   1.851 +            return null;
   1.852 +        }
   1.853 +        int gmtOffset =  (hours * 60 + num) * 60 * 1000;
   1.854 +
   1.855 +        if (gmtOffset == 0) {
   1.856 +            zi = ZoneInfoFile.getZoneInfo(GMT_ID);
   1.857 +            if (negative) {
   1.858 +                zi.setID("GMT-00:00");
   1.859 +            } else {
   1.860 +                zi.setID("GMT+00:00");
   1.861 +            }
   1.862 +        } else {
   1.863 +            zi = ZoneInfoFile.getCustomTimeZone(id, negative ? -gmtOffset : gmtOffset);
   1.864 +        }
   1.865 +        return zi;
   1.866 +    }
   1.867 +}