64 import java.io.DataOutput; |
64 import java.io.DataOutput; |
65 import java.io.IOException; |
65 import java.io.IOException; |
66 import java.io.Serializable; |
66 import java.io.Serializable; |
67 import java.time.format.DateTimeFormatterBuilder; |
67 import java.time.format.DateTimeFormatterBuilder; |
68 import java.time.format.TextStyle; |
68 import java.time.format.TextStyle; |
69 import java.time.temporal.Queries; |
|
70 import java.time.temporal.TemporalAccessor; |
69 import java.time.temporal.TemporalAccessor; |
71 import java.time.temporal.TemporalField; |
70 import java.time.temporal.TemporalField; |
72 import java.time.temporal.TemporalQuery; |
71 import java.time.temporal.TemporalQuery; |
|
72 import java.time.temporal.UnsupportedTemporalTypeException; |
73 import java.time.zone.ZoneRules; |
73 import java.time.zone.ZoneRules; |
74 import java.time.zone.ZoneRulesException; |
74 import java.time.zone.ZoneRulesException; |
75 import java.time.zone.ZoneRulesProvider; |
75 import java.time.zone.ZoneRulesProvider; |
76 import java.util.Collections; |
76 import java.util.Collections; |
77 import java.util.HashMap; |
77 import java.util.HashMap; |
78 import java.util.Locale; |
78 import java.util.Locale; |
79 import java.util.Map; |
79 import java.util.Map; |
80 import java.util.Objects; |
80 import java.util.Objects; |
|
81 import java.util.Set; |
81 import java.util.TimeZone; |
82 import java.util.TimeZone; |
82 |
83 |
83 /** |
84 /** |
84 * A time-zone ID, such as {@code Europe/Paris}. |
85 * A time-zone ID, such as {@code Europe/Paris}. |
85 * <p> |
86 * <p> |
91 * the same offset for all local date-times |
92 * the same offset for all local date-times |
92 * <li>Geographical regions - an area where a specific set of rules for finding |
93 * <li>Geographical regions - an area where a specific set of rules for finding |
93 * the offset from UTC/Greenwich apply |
94 * the offset from UTC/Greenwich apply |
94 * </ul><p> |
95 * </ul><p> |
95 * Most fixed offsets are represented by {@link ZoneOffset}. |
96 * Most fixed offsets are represented by {@link ZoneOffset}. |
|
97 * Calling {@link #normalized()} on any {@code ZoneId} will ensure that a |
|
98 * fixed offset ID will be represented as a {@code ZoneOffset}. |
96 * <p> |
99 * <p> |
97 * The actual rules, describing when and how the offset changes, are defined by {@link ZoneRules}. |
100 * The actual rules, describing when and how the offset changes, are defined by {@link ZoneRules}. |
98 * This class is simply an ID used to obtain the underlying rules. |
101 * This class is simply an ID used to obtain the underlying rules. |
99 * This approach is taken because rules are defined by governments and change |
102 * This approach is taken because rules are defined by governments and change |
100 * frequently, whereas the ID is stable. |
103 * frequently, whereas the ID is stable. |
101 * <p> |
104 * <p> |
102 * The distinction has other effects. Serializing the {@code ZoneId} will only send |
105 * The distinction has other effects. Serializing the {@code ZoneId} will only send |
103 * the ID, whereas serializing the rules sends the entire data set. |
106 * the ID, whereas serializing the rules sends the entire data set. |
104 * Similarly, a comparison of two IDs only examines the ID, whereas |
107 * Similarly, a comparison of two IDs only examines the ID, whereas |
105 * a comparison of two rules examines the entire data set. |
108 * a comparison of two rules examines the entire data set. |
106 * <p> |
|
107 * The code supports loading a {@code ZoneId} on a JVM which does not have available rules |
|
108 * for that ID. This allows the date-time object, such as {@link ZonedDateTime}, |
|
109 * to still be queried. |
|
110 * |
109 * |
111 * <h3>Time-zone IDs</h3> |
110 * <h3>Time-zone IDs</h3> |
112 * The ID is unique within the system. |
111 * The ID is unique within the system. |
113 * The formats for offset and region IDs differ. |
112 * There are three types of ID. |
114 * <p> |
113 * <p> |
115 * An ID is parsed as an offset ID if it starts with 'UTC', 'GMT', 'UT' '+' or '-', or |
114 * The simplest type of ID is that from {@code ZoneOffset}. |
116 * is a single letter. For example, 'Z', '+02:00', '-05:00', 'UTC+05', 'GMT-6' and |
115 * This consists of 'Z' and IDs starting with '+' or '-'. |
117 * 'UT+01:00' are all valid offset IDs. |
116 * <p> |
118 * Note that some IDs, such as 'D' or '+ABC' meet the criteria to be parsed as offset IDs, |
117 * The next type of ID are offset-style IDs with some form of prefix, |
119 * but have an invalid offset. |
118 * such as 'GMT+2' or 'UTC+01:00'. |
120 * <p> |
119 * The recognised prefixes are 'UTC', 'GMT' and 'UT'. |
121 * All other IDs are considered to be region IDs. |
120 * The offset is the suffix and will be normalized during creation. |
122 * <p> |
121 * These IDs can be normalized to a {@code ZoneOffset} using {@code normalized()}. |
123 * Region IDs are defined by configuration, which can be thought of as a {@code Map} |
122 * <p> |
124 * from region ID to {@code ZoneRules}, see {@link ZoneRulesProvider}. |
123 * The third type of ID are region-based IDs. A region-based ID must be of |
125 * <p> |
124 * two or more characters, and not start with 'UTC', 'GMT', 'UT' '+' or '-'. |
126 * Time-zones are defined by governments and change frequently. There are a number of |
125 * Region-based IDs are defined by configuration, see {@link ZoneRulesProvider}. |
127 * organizations, known here as groups, that monitor time-zone changes and collate them. |
126 * The configuration focuses on providing the lookup from the ID to the |
|
127 * underlying {@code ZoneRules}. |
|
128 * <p> |
|
129 * Time-zone rules are defined by governments and change frequently. |
|
130 * There are a number of organizations, known here as groups, that monitor |
|
131 * time-zone changes and collate them. |
128 * The default group is the IANA Time Zone Database (TZDB). |
132 * The default group is the IANA Time Zone Database (TZDB). |
129 * Other organizations include IATA (the airline industry body) and Microsoft. |
133 * Other organizations include IATA (the airline industry body) and Microsoft. |
130 * <p> |
134 * <p> |
131 * Each group defines its own format for the region ID it provides. |
135 * Each group defines its own format for the region ID it provides. |
132 * The TZDB group defines IDs such as 'Europe/London' or 'America/New_York'. |
136 * The TZDB group defines IDs such as 'Europe/London' or 'America/New_York'. |
137 * region IDs are typically the same as the three letter airport code. |
141 * region IDs are typically the same as the three letter airport code. |
138 * However, the airport of Utrecht has the code 'UTC', which is obviously a conflict. |
142 * However, the airport of Utrecht has the code 'UTC', which is obviously a conflict. |
139 * The recommended format for region IDs from groups other than TZDB is 'group~region'. |
143 * The recommended format for region IDs from groups other than TZDB is 'group~region'. |
140 * Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'. |
144 * Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'. |
141 * |
145 * |
|
146 * <h3>Serialization</h3> |
|
147 * This class can be serialized and stores the string zone ID in the external form. |
|
148 * The {@code ZoneOffset} subclass uses a dedicated format that only stores the |
|
149 * offset from UTC/Greenwich. |
|
150 * <p> |
|
151 * A {@code ZoneId} can be deserialized in a Java Runtime where the ID is unknown. |
|
152 * For example, if a server-side Java Runtime has been updated with a new zone ID, but |
|
153 * the client-side Java Runtime has not been updated. In this case, the {@code ZoneId} |
|
154 * object will exist, and can be queried using {@code getId}, {@code equals}, |
|
155 * {@code hashCode}, {@code toString}, {@code getDisplayName} and {@code normalized}. |
|
156 * However, any call to {@code getRules} will fail with {@code ZoneRulesException}. |
|
157 * This approach is designed to allow a {@link ZonedDateTime} to be loaded and |
|
158 * queried, but not modified, on a Java Runtime with incomplete time-zone information. |
|
159 * |
142 * <h3>Specification for implementors</h3> |
160 * <h3>Specification for implementors</h3> |
143 * This abstract class has two implementations, both of which are immutable and thread-safe. |
161 * This abstract class has two implementations, both of which are immutable and thread-safe. |
144 * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling |
162 * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling |
145 * offset-based IDs. This difference is visible in serialization. |
163 * offset-based IDs. This difference is visible in serialization. |
146 * |
164 * |
147 * @since 1.8 |
165 * @since 1.8 |
148 */ |
166 */ |
149 public abstract class ZoneId implements Serializable { |
167 public abstract class ZoneId implements Serializable { |
150 |
168 |
151 /** |
169 /** |
152 * A map of zone overrides to enable the older US time-zone names to be used. |
170 * A map of zone overrides to enable the older short time-zone names to be used. |
|
171 * <p> |
|
172 * Use of short zone IDs has been deprecated in {@code java.util.TimeZone}. |
|
173 * This map allows the IDs to continue to be used via the |
|
174 * {@link #of(String, Map)} factory method. |
|
175 * <p> |
|
176 * This map contains an older mapping of the IDs, where 'EST', 'MST' and 'HST' |
|
177 * map to IDs which include daylight savings. |
|
178 * This is in line with versions of TZDB before 2005r. |
153 * <p> |
179 * <p> |
154 * This maps as follows: |
180 * This maps as follows: |
155 * <p><ul> |
181 * <p><ul> |
156 * <li>EST - America/New_York</li> |
182 * <li>EST - America/New_York</li> |
157 * <li>MST - America/Denver</li> |
183 * <li>MST - America/Denver</li> |
251 base.put("VST", "Asia/Ho_Chi_Minh"); |
285 base.put("VST", "Asia/Ho_Chi_Minh"); |
252 Map<String, String> pre = new HashMap<>(base); |
286 Map<String, String> pre = new HashMap<>(base); |
253 pre.put("EST", "America/New_York"); |
287 pre.put("EST", "America/New_York"); |
254 pre.put("MST", "America/Denver"); |
288 pre.put("MST", "America/Denver"); |
255 pre.put("HST", "Pacific/Honolulu"); |
289 pre.put("HST", "Pacific/Honolulu"); |
256 OLD_IDS_PRE_2005 = Collections.unmodifiableMap(pre); |
290 OLD_SHORT_IDS = Collections.unmodifiableMap(pre); |
257 Map<String, String> post = new HashMap<>(base); |
291 Map<String, String> post = new HashMap<>(base); |
258 post.put("EST", "-05:00"); |
292 post.put("EST", "-05:00"); |
259 post.put("MST", "-07:00"); |
293 post.put("MST", "-07:00"); |
260 post.put("HST", "-10:00"); |
294 post.put("HST", "-10:00"); |
261 OLD_IDS_POST_2005 = Collections.unmodifiableMap(post); |
295 SHORT_IDS = Collections.unmodifiableMap(post); |
262 } |
296 } |
263 /** |
297 /** |
264 * Serialization version. |
298 * Serialization version. |
265 */ |
299 */ |
266 private static final long serialVersionUID = 8352817235686L; |
300 private static final long serialVersionUID = 8352817235686L; |
308 |
358 |
309 /** |
359 /** |
310 * Obtains an instance of {@code ZoneId} from an ID ensuring that the |
360 * Obtains an instance of {@code ZoneId} from an ID ensuring that the |
311 * ID is valid and available for use. |
361 * ID is valid and available for use. |
312 * <p> |
362 * <p> |
313 * This method parses the ID, applies any appropriate normalization, and validates it |
363 * This method parses the ID producing a {@code ZoneId} or {@code ZoneOffset}. |
314 * against the known set of IDs for which rules are available. |
364 * A {@code ZoneOffset} is returned if the ID is 'Z', or starts with '+' or '-'. |
315 * <p> |
365 * The result will always be a valid ID for which {@link ZoneRules} can be obtained. |
316 * An ID is parsed as though it is an offset ID if it starts with 'UTC', 'GMT', 'UT', '+' |
366 * <p> |
317 * or '-', or if it has less then two letters. |
367 * Parsing matches the zone ID step by step as follows. |
318 * The offset of {@linkplain ZoneOffset#UTC zero} may be represented in multiple ways, |
368 * <ul> |
319 * including 'Z', 'UTC', 'GMT', 'UT', 'UTC0', 'GMT0', 'UT0', '+00:00', '-00:00' and 'UTC+00:00'. |
369 * <li>If the zone ID equals 'Z', the result is {@code ZoneOffset.UTC}. |
320 * <p> |
370 * <li>If the zone ID consists of a single letter, the zone ID is invalid |
321 * Six forms of ID are recognized: |
371 * and {@code DateTimeException} is thrown. |
322 * <p><ul> |
372 * <li>If the zone ID starts with '+' or '-', the ID is parsed as a |
323 * <li><code>Z</code> - an offset of zero, which is {@code ZoneOffset.UTC} |
373 * {@code ZoneOffset} using {@link ZoneOffset#of(String)}. |
324 * <li><code>{offset}</code> - a {@code ZoneOffset} ID, such as '+02:00' |
374 * <li>If the zone ID equals 'GMT', 'UTC' or 'UT' then the result is a {@code ZoneId} |
325 * <li><code>{utcPrefix}</code> - a {@code ZoneOffset} ID equal to 'Z' |
375 * with the same ID and rules equivalent to {@code ZoneOffset.UTC}. |
326 * <li><code>{utcPrefix}0</code> - a {@code ZoneOffset} ID equal to 'Z' |
376 * <li>If the zone ID starts with 'UTC+', 'UTC-', 'GMT+', 'GMT-', 'UT+' or 'UT-' |
327 * <li><code>{utcPrefix}{offset}</code> - a {@code ZoneOffset} ID equal to '{offset}' |
377 * then the ID is a prefixed offset-based ID. The ID is split in two, with |
328 * <li><code>{regionID}</code> - full region ID, loaded from configuration |
378 * a two or three letter prefix and a suffix starting with the sign. |
329 * </ul><p> |
379 * The suffix is parsed as a {@link ZoneOffset#of(String) ZoneOffset}. |
330 * The {offset} is a valid format for {@link ZoneOffset#of(String)}, excluding 'Z'. |
380 * The result will be a {@code ZoneId} with the specified UTC/GMT/UT prefix |
331 * The {utcPrefix} is 'UTC', 'GMT' or 'UT'. |
381 * and the normalized offset ID as per {@link ZoneOffset#getId()}. |
332 * Region IDs must match the regular expression <code>[A-Za-z][A-Za-z0-9~/._+-]+</code>. |
382 * The rules of the returned {@code ZoneId} will be equivalent to the |
333 * <p> |
383 * parsed {@code ZoneOffset}. |
334 * The detailed format of the region ID depends on the group supplying the data. |
384 * <li>All other IDs are parsed as region-based zone IDs. Region IDs must |
335 * The default set of data is supplied by the IANA Time Zone Database (TZDB) |
385 * match the regular expression <code>[A-Za-z][A-Za-z0-9~/._+-]+</code> |
336 * This has region IDs of the form '{area}/{city}', such as 'Europe/Paris' or 'America/New_York'. |
386 * otherwise a {@code DateTimeException} is thrown. If the zone ID is not |
337 * This is compatible with most IDs from {@link java.util.TimeZone}. |
387 * in the configured set of IDs, {@code ZoneRulesException} is thrown. |
|
388 * The detailed format of the region ID depends on the group supplying the data. |
|
389 * The default set of data is supplied by the IANA Time Zone Database (TZDB). |
|
390 * This has region IDs of the form '{area}/{city}', such as 'Europe/Paris' or 'America/New_York'. |
|
391 * This is compatible with most IDs from {@link java.util.TimeZone}. |
|
392 * </ul> |
338 * |
393 * |
339 * @param zoneId the time-zone ID, not null |
394 * @param zoneId the time-zone ID, not null |
340 * @return the zone ID, not null |
395 * @return the zone ID, not null |
341 * @throws DateTimeException if the zone ID has an invalid format |
396 * @throws DateTimeException if the zone ID has an invalid format |
342 * @throws ZoneRulesException if the zone ID is a region ID that cannot be found |
397 * @throws ZoneRulesException if the zone ID is a region ID that cannot be found |
343 */ |
398 */ |
344 public static ZoneId of(String zoneId) { |
399 public static ZoneId of(String zoneId) { |
|
400 return of(zoneId, true); |
|
401 } |
|
402 |
|
403 /** |
|
404 * Parses the ID, taking a flag to indicate whether {@code ZoneRulesException} |
|
405 * should be thrown or not, used in deserialization. |
|
406 * |
|
407 * @param zoneId the time-zone ID, not null |
|
408 * @param checkAvailable whether to check if the zone ID is available |
|
409 * @return the zone ID, not null |
|
410 * @throws DateTimeException if the ID format is invalid |
|
411 * @throws ZoneRulesException if checking availability and the ID cannot be found |
|
412 */ |
|
413 static ZoneId of(String zoneId, boolean checkAvailable) { |
345 Objects.requireNonNull(zoneId, "zoneId"); |
414 Objects.requireNonNull(zoneId, "zoneId"); |
346 if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) { |
415 if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) { |
347 return ZoneOffset.of(zoneId); |
416 return ZoneOffset.of(zoneId); |
348 } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) { |
417 } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) { |
349 return ofWithPrefix(zoneId, 3); |
418 return ofWithPrefix(zoneId, 3, checkAvailable); |
350 } else if (zoneId.startsWith("UT")) { |
419 } else if (zoneId.startsWith("UT")) { |
351 return ofWithPrefix(zoneId, 2); |
420 return ofWithPrefix(zoneId, 2, checkAvailable); |
352 } |
421 } |
353 return ZoneRegion.ofId(zoneId, true); |
422 return ZoneRegion.ofId(zoneId, checkAvailable); |
354 } |
423 } |
355 |
424 |
356 /** |
425 /** |
357 * Parse once a prefix is established. |
426 * Parse once a prefix is established. |
358 * |
427 * |
359 * @param zoneId the time-zone ID, not null |
428 * @param zoneId the time-zone ID, not null |
360 * @param prefixLength the length of the prefix, 2 or 3 |
429 * @param prefixLength the length of the prefix, 2 or 3 |
361 * @return the zone ID, not null |
430 * @return the zone ID, not null |
362 * @return the zone ID, not null |
|
363 * @throws DateTimeException if the zone ID has an invalid format |
431 * @throws DateTimeException if the zone ID has an invalid format |
364 */ |
432 */ |
365 private static ZoneId ofWithPrefix(String zoneId, int prefixLength) { |
433 private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) { |
366 if (zoneId.length() == prefixLength || |
434 String prefix = zoneId.substring(0, prefixLength); |
367 (zoneId.length() == prefixLength + 1 && zoneId.charAt(prefixLength) == '0')) { |
435 if (zoneId.length() == prefixLength) { |
368 return ZoneOffset.UTC; |
436 return ZoneRegion.ofPrefixedOffset(prefix, ZoneOffset.UTC); |
369 } |
437 } |
370 if (zoneId.charAt(prefixLength) == '+' || zoneId.charAt(prefixLength) == '-') { |
438 if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') { |
371 try { |
439 return ZoneRegion.ofId(zoneId, checkAvailable); // drop through to ZoneRulesProvider |
372 return ZoneOffset.of(zoneId.substring(prefixLength)); |
440 } |
373 } catch (DateTimeException ex) { |
441 try { |
374 throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex); |
442 ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength)); |
|
443 if (offset == ZoneOffset.UTC) { |
|
444 return ZoneRegion.ofPrefixedOffset(prefix, offset); |
375 } |
445 } |
376 } |
446 return ZoneRegion.ofPrefixedOffset(prefix + offset.toString(), offset); |
377 throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId); |
447 } catch (DateTimeException ex) { |
|
448 throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex); |
|
449 } |
378 } |
450 } |
379 |
451 |
380 //----------------------------------------------------------------------- |
452 //----------------------------------------------------------------------- |
381 /** |
453 /** |
382 * Obtains an instance of {@code ZoneId} from a temporal object. |
454 * Obtains an instance of {@code ZoneId} from a temporal object. |
464 * @param style the length of the text required, not null |
513 * @param style the length of the text required, not null |
465 * @param locale the locale to use, not null |
514 * @param locale the locale to use, not null |
466 * @return the text value of the zone, not null |
515 * @return the text value of the zone, not null |
467 */ |
516 */ |
468 public String getDisplayName(TextStyle style, Locale locale) { |
517 public String getDisplayName(TextStyle style, Locale locale) { |
469 return new DateTimeFormatterBuilder().appendZoneText(style).toFormatter(locale).format(new TemporalAccessor() { |
518 return new DateTimeFormatterBuilder().appendZoneText(style).toFormatter(locale).format(toTemporal()); |
|
519 } |
|
520 |
|
521 /** |
|
522 * Converts this zone to a {@code TemporalAccessor}. |
|
523 * <p> |
|
524 * A {@code ZoneId} can be fully represented as a {@code TemporalAccessor}. |
|
525 * However, the interface is not implemented by this class as most of the |
|
526 * methods on the interface have no meaning to {@code ZoneId}. |
|
527 * <p> |
|
528 * The returned temporal has no supported fields, with the query method |
|
529 * supporting the return of the zone using {@link TemporalQuery#zoneId()}. |
|
530 * |
|
531 * @return a temporal equivalent to this zone, not null |
|
532 */ |
|
533 private TemporalAccessor toTemporal() { |
|
534 return new TemporalAccessor() { |
470 @Override |
535 @Override |
471 public boolean isSupported(TemporalField field) { |
536 public boolean isSupported(TemporalField field) { |
472 return false; |
537 return false; |
473 } |
538 } |
474 @Override |
539 @Override |
475 public long getLong(TemporalField field) { |
540 public long getLong(TemporalField field) { |
476 throw new DateTimeException("Unsupported field: " + field); |
541 throw new UnsupportedTemporalTypeException("Unsupported field: " + field); |
477 } |
542 } |
478 @SuppressWarnings("unchecked") |
543 @SuppressWarnings("unchecked") |
479 @Override |
544 @Override |
480 public <R> R query(TemporalQuery<R> query) { |
545 public <R> R query(TemporalQuery<R> query) { |
481 if (query == Queries.zoneId()) { |
546 if (query == TemporalQuery.zoneId()) { |
482 return (R) ZoneId.this; |
547 return (R) ZoneId.this; |
483 } |
548 } |
484 return TemporalAccessor.super.query(query); |
549 return TemporalAccessor.super.query(query); |
485 } |
550 } |
486 }); |
551 }; |
|
552 } |
|
553 |
|
554 //----------------------------------------------------------------------- |
|
555 /** |
|
556 * Gets the time-zone rules for this ID allowing calculations to be performed. |
|
557 * <p> |
|
558 * The rules provide the functionality associated with a time-zone, |
|
559 * such as finding the offset for a given instant or local date-time. |
|
560 * <p> |
|
561 * A time-zone can be invalid if it is deserialized in a Java Runtime which |
|
562 * does not have the same rules loaded as the Java Runtime that stored it. |
|
563 * In this case, calling this method will throw a {@code ZoneRulesException}. |
|
564 * <p> |
|
565 * The rules are supplied by {@link ZoneRulesProvider}. An advanced provider may |
|
566 * support dynamic updates to the rules without restarting the Java Runtime. |
|
567 * If so, then the result of this method may change over time. |
|
568 * Each individual call will be still remain thread-safe. |
|
569 * <p> |
|
570 * {@link ZoneOffset} will always return a set of rules where the offset never changes. |
|
571 * |
|
572 * @return the rules, not null |
|
573 * @throws ZoneRulesException if no rules are available for this ID |
|
574 */ |
|
575 public abstract ZoneRules getRules(); |
|
576 |
|
577 /** |
|
578 * Normalizes the time-zone ID, returning a {@code ZoneOffset} where possible. |
|
579 * <p> |
|
580 * The returns a normalized {@code ZoneId} that can be used in place of this ID. |
|
581 * The result will have {@code ZoneRules} equivalent to those returned by this object, |
|
582 * however the ID returned by {@code getId()} may be different. |
|
583 * <p> |
|
584 * The normalization checks if the rules of this {@code ZoneId} have a fixed offset. |
|
585 * If they do, then the {@code ZoneOffset} equal to that offset is returned. |
|
586 * Otherwise {@code this} is returned. |
|
587 * |
|
588 * @return the time-zone unique ID, not null |
|
589 */ |
|
590 public ZoneId normalized() { |
|
591 try { |
|
592 ZoneRules rules = getRules(); |
|
593 if (rules.isFixedOffset()) { |
|
594 return rules.getOffset(Instant.EPOCH); |
|
595 } |
|
596 } catch (ZoneRulesException ex) { |
|
597 // invalid ZoneRegion is not important to this method |
|
598 } |
|
599 return this; |
487 } |
600 } |
488 |
601 |
489 //----------------------------------------------------------------------- |
602 //----------------------------------------------------------------------- |
490 /** |
603 /** |
491 * Checks if this time-zone ID is equal to another time-zone ID. |
604 * Checks if this time-zone ID is equal to another time-zone ID. |