src/share/classes/java/time/ZoneId.java

Print this page

        

@@ -69,10 +69,11 @@
 import java.time.temporal.Queries;
 import java.time.temporal.TemporalAccessor;
 import java.time.temporal.TemporalField;
 import java.time.temporal.TemporalQuery;
 import java.time.zone.ZoneRules;
+import java.time.zone.ZoneRulesException;
 import java.time.zone.ZoneRulesProvider;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;

@@ -109,14 +110,15 @@
  *
  * <h3>Time-zone IDs</h3>
  * The ID is unique within the system.
  * The formats for offset and region IDs differ.
  * <p>
- * An ID is parsed as an offset ID if it starts with 'UTC', 'GMT', '+' or '-', or
- * is a single letter.
- * For example, 'Z', '+02:00', '-05:00', 'UTC+05' and 'GMT-6' are all valid offset IDs.
- * Note that some IDs, such as 'D' or '+ABC' meet the criteria, but are invalid.
+ * An ID is parsed as an offset ID if it starts with 'UTC', 'GMT', 'UT' '+' or '-', or
+ * is a single letter. For example, 'Z', '+02:00', '-05:00', 'UTC+05', 'GMT-6' and
+ * 'UT+01:00' are all valid offset IDs.
+ * Note that some IDs, such as 'D' or '+ABC' meet the criteria to be parsed as offset IDs,
+ * but have an invalid offset.
  * <p>
  * All other IDs are considered to be region IDs.
  * <p>
  * Region IDs are defined by configuration, which can be thought of as a {@code Map}
  * from region ID to {@code ZoneRules}, see {@link ZoneRulesProvider}.

@@ -124,37 +126,37 @@
  * Time-zones are defined by governments and change frequently. There are a number of
  * organizations, known here as groups, that monitor time-zone changes and collate them.
  * The default group is the IANA Time Zone Database (TZDB).
  * Other organizations include IATA (the airline industry body) and Microsoft.
  * <p>
- * Each group defines its own format for region ID.
+ * Each group defines its own format for the region ID it provides.
  * The TZDB group defines IDs such as 'Europe/London' or 'America/New_York'.
  * TZDB IDs take precedence over other groups.
  * <p>
- * It is strongly recommended that the group name is included in all Ids supplied by
+ * It is strongly recommended that the group name is included in all IDs supplied by
  * groups other than TZDB to avoid conflicts. For example, IATA airline time-zone
  * region IDs are typically the same as the three letter airport code.
  * However, the airport of Utrecht has the code 'UTC', which is obviously a conflict.
  * The recommended format for region IDs from groups other than TZDB is 'group~region'.
  * Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'.
  *
  * <h3>Specification for implementors</h3>
  * This abstract class has two implementations, both of which are immutable and thread-safe.
  * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling
- * offset-based IDs.
+ * offset-based IDs. This difference is visible in serialization.
  *
  * @since 1.8
  */
 public abstract class ZoneId implements Serializable {
 
     /**
      * A map of zone overrides to enable the older US time-zone names to be used.
      * <p>
      * This maps as follows:
      * <p><ul>
-     * <li>EST - America/Indianapolis</li>
-     * <li>MST - America/Phoenix</li>
+     * <li>EST - America/New_York</li>
+     * <li>MST - America/Denver</li>
      * <li>HST - Pacific/Honolulu</li>
      * <li>ACT - Australia/Darwin</li>
      * <li>AET - Australia/Sydney</li>
      * <li>AGT - America/Argentina/Buenos_Aires</li>
      * <li>ART - Africa/Cairo</li>

@@ -246,12 +248,12 @@
         base.put("PRT", "America/Puerto_Rico");
         base.put("PST", "America/Los_Angeles");
         base.put("SST", "Pacific/Guadalcanal");
         base.put("VST", "Asia/Ho_Chi_Minh");
         Map<String, String> pre = new HashMap<>(base);
-        pre.put("EST", "America/Indianapolis");
-        pre.put("MST", "America/Phoenix");
+        pre.put("EST", "America/New_York");
+        pre.put("MST", "America/Denver");
         pre.put("HST", "Pacific/Honolulu");
         OLD_IDS_PRE_2005 = Collections.unmodifiableMap(pre);
         Map<String, String> post = new HashMap<>(base);
         post.put("EST", "-05:00");
         post.put("MST", "-07:00");

@@ -271,11 +273,11 @@
      * and converts it to a {@code ZoneId}. If the system default time-zone is changed,
      * then the result of this method will also change.
      *
      * @return the zone ID, not null
      * @throws DateTimeException if the converted zone ID has an invalid format
-     * @throws java.time.zone.ZoneRulesException if the converted zone region ID cannot be found
+     * @throws ZoneRulesException if the converted zone region ID cannot be found
      */
     public static ZoneId systemDefault() {
         return ZoneId.of(TimeZone.getDefault().getID(), OLD_IDS_POST_2005);
     }
 

@@ -292,11 +294,11 @@
      *
      * @param zoneId  the time-zone ID, not null
      * @param aliasMap  a map of alias zone IDs (typically abbreviations) to real zone IDs, not null
      * @return the zone ID, not null
      * @throws DateTimeException if the zone ID has an invalid format
-     * @throws java.time.zone.ZoneRulesException if the zone region ID cannot be found
+     * @throws ZoneRulesException if the zone ID is a region ID that cannot be found
      */
     public static ZoneId of(String zoneId, Map<String, String> aliasMap) {
         Objects.requireNonNull(zoneId, "zoneId");
         Objects.requireNonNull(aliasMap, "aliasMap");
         String id = aliasMap.get(zoneId);

@@ -309,55 +311,82 @@
      * ID is valid and available for use.
      * <p>
      * This method parses the ID, applies any appropriate normalization, and validates it
      * against the known set of IDs for which rules are available.
      * <p>
-     * An ID is parsed as though it is an offset ID if it starts with 'UTC', 'GMT', '+'
+     * An ID is parsed as though it is an offset ID if it starts with 'UTC', 'GMT', 'UT', '+'
      * or '-', or if it has less then two letters.
-     * The offset of {@link ZoneOffset#UTC zero} may be represented in multiple ways,
-     * including 'Z', 'UTC', 'GMT', 'UTC0' 'GMT0', '+00:00', '-00:00' and 'UTC+00:00'.
+     * The offset of {@linkplain ZoneOffset#UTC zero} may be represented in multiple ways,
+     * including 'Z', 'UTC', 'GMT', 'UT', 'UTC0', 'GMT0', 'UT0', '+00:00', '-00:00' and 'UTC+00:00'.
      * <p>
-     * Eight forms of ID are recognized, where '{offset}' means to parse using {@link ZoneOffset#of(String)}:
+     * Six forms of ID are recognized:
      * <p><ul>
-     * <li><code>{offset}</code> - a {@link ZoneOffset} ID, such as 'Z' or '+02:00'
-     * <li><code>UTC</code> - alternate form of a {@code ZoneOffset} ID equal to 'Z'
-     * <li><code>UTC0</code> - alternate form of a {@code ZoneOffset} ID equal to 'Z'
-     * <li><code>UTC{offset}</code> - alternate form of a {@code ZoneOffset} ID equal to '{offset}'
-     * <li><code>GMT</code> - alternate form of a {@code ZoneOffset} ID equal to 'Z'
-     * <li><code>GMT0</code> - alternate form of a {@code ZoneOffset} ID equal to 'Z'
-     * <li><code>GMT{offset}</code> - alternate form of a {@code ZoneOffset} ID equal to '{offset}'r
+     * <li><code>Z</code> - an offset of zero, which is {@code ZoneOffset.UTC}
+     * <li><code>{offset}</code> - a {@code ZoneOffset} ID, such as '+02:00'
+     * <li><code>{utcPrefix}</code> - a {@code ZoneOffset} ID equal to 'Z'
+     * <li><code>{utcPrefix}0</code> - a {@code ZoneOffset} ID equal to 'Z'
+     * <li><code>{utcPrefix}{offset}</code> - a {@code ZoneOffset} ID equal to '{offset}'
      * <li><code>{regionID}</code> - full region ID, loaded from configuration
      * </ul><p>
+     * The {offset} is a valid format for {@link ZoneOffset#of(String)}, excluding 'Z'.
+     * The {utcPrefix} is 'UTC', 'GMT' or 'UT'.
      * Region IDs must match the regular expression <code>[A-Za-z][A-Za-z0-9~/._+-]+</code>.
      * <p>
      * The detailed format of the region ID depends on the group supplying the data.
      * The default set of data is supplied by the IANA Time Zone Database (TZDB)
      * This has region IDs of the form '{area}/{city}', such as 'Europe/Paris' or 'America/New_York'.
      * This is compatible with most IDs from {@link java.util.TimeZone}.
      *
      * @param zoneId  the time-zone ID, not null
      * @return the zone ID, not null
      * @throws DateTimeException if the zone ID has an invalid format
-     * @throws java.time.zone.ZoneRulesException if the zone region ID cannot be found
+     * @throws ZoneRulesException if the zone ID is a region ID that cannot be found
      */
     public static ZoneId of(String zoneId) {
         Objects.requireNonNull(zoneId, "zoneId");
         if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) {
             return ZoneOffset.of(zoneId);
         } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) {
-            if (zoneId.length() == 3 || (zoneId.length() == 4 && zoneId.charAt(3) == '0')) {
+            return ofWithPrefix(zoneId, 3);
+        } else if (zoneId.startsWith("UT")) {
+            return ofWithPrefix(zoneId, 2);
+        }
+        return ZoneRegion.ofId(zoneId, true);
+    }
+
+    /**
+     * Parse once a prefix is established.
+     *
+     * @param zoneId  the time-zone ID, not null
+     * @param prefixLength  the length of the prefix, 2 or 3
+     * @return the zone ID, not null
+     * @return the zone ID, not null
+     * @throws DateTimeException if the zone ID has an invalid format
+     */
+    private static ZoneId ofWithPrefix(String zoneId, int prefixLength) {
+        if (zoneId.length() == prefixLength ||
+                (zoneId.length() == prefixLength + 1 && zoneId.charAt(prefixLength) == '0')) {
                 return ZoneOffset.UTC;
             }
-            return ZoneOffset.of(zoneId.substring(3));
+        if (zoneId.charAt(prefixLength) == '+' || zoneId.charAt(prefixLength) == '-') {
+            try {
+                return ZoneOffset.of(zoneId.substring(prefixLength));
+            } catch (DateTimeException ex) {
+                throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex);
         }
-        return ZoneRegion.ofId(zoneId, true);
+        }
+        throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId);
     }
 
     //-----------------------------------------------------------------------
     /**
      * Obtains an instance of {@code ZoneId} from a temporal object.
      * <p>
+     * This obtains a zone based on the specified temporal.
+     * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+     * which this factory converts to an instance of {@code ZoneId}.
+     * <p>
      * A {@code TemporalAccessor} represents some form of date and time information.
      * This factory converts the arbitrary temporal object to an instance of {@code ZoneId}.
      * <p>
      * The conversion will try to obtain the zone in a way that favours region-based
      * zones over offset-based zones using {@link Queries#zone()}.

@@ -415,29 +444,31 @@
      * Each individual call will be still remain thread-safe.
      * <p>
      * {@link ZoneOffset} will always return a set of rules where the offset never changes.
      *
      * @return the rules, not null
-     * @throws DateTimeException if no rules are available for this ID
+     * @throws ZoneRulesException if no rules are available for this ID
      */
     public abstract ZoneRules getRules();
 
     //-----------------------------------------------------------------------
     /**
      * Gets the textual representation of the zone, such as 'British Time' or
      * '+02:00'.
      * <p>
-     * This returns a textual description for the time-zone ID.
+     * This returns the textual name used to identify the time-zone ID,
+     * suitable for presentation to the user.
+     * The parameters control the style of the returned text and the locale.
      * <p>
      * If no textual mapping is found then the {@link #getId() full ID} is returned.
      *
      * @param style  the length of the text required, not null
      * @param locale  the locale to use, not null
      * @return the text value of the zone, not null
      */
-    public String getText(TextStyle style, Locale locale) {
-        return new DateTimeFormatterBuilder().appendZoneText(style).toFormatter(locale).print(new TemporalAccessor() {
+    public String getDisplayName(TextStyle style, Locale locale) {
+        return new DateTimeFormatterBuilder().appendZoneText(style).toFormatter(locale).format(new TemporalAccessor() {
             @Override
             public boolean isSupported(TemporalField field) {
                 return false;
             }
             @Override