src/share/classes/java/time/ZoneOffset.java

Print this page




  44  *    and/or other materials provided with the distribution.
  45  *
  46  *  * Neither the name of JSR-310 nor the names of its contributors
  47  *    may be used to endorse or promote products derived from this software
  48  *    without specific prior written permission.
  49  *
  50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  51  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  52  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  53  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  54  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  55  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  56  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  57  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  58  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  59  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  60  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  61  */
  62 package java.time;
  63 

  64 import static java.time.LocalTime.MINUTES_PER_HOUR;
  65 import static java.time.LocalTime.SECONDS_PER_HOUR;
  66 import static java.time.LocalTime.SECONDS_PER_MINUTE;
  67 import static java.time.temporal.ChronoField.OFFSET_SECONDS;
  68 
  69 import java.io.DataInput;
  70 import java.io.DataOutput;
  71 import java.io.IOException;
  72 import java.io.InvalidObjectException;
  73 import java.io.ObjectStreamException;
  74 import java.io.Serializable;
  75 import java.time.temporal.ChronoField;
  76 import java.time.temporal.Queries;
  77 import java.time.temporal.Temporal;
  78 import java.time.temporal.TemporalAccessor;
  79 import java.time.temporal.TemporalAdjuster;
  80 import java.time.temporal.TemporalField;
  81 import java.time.temporal.TemporalQuery;
  82 import java.time.temporal.ValueRange;
  83 import java.time.zone.ZoneRules;
  84 import java.util.Objects;
  85 import java.util.concurrent.ConcurrentHashMap;
  86 import java.util.concurrent.ConcurrentMap;
  87 
  88 /**
  89  * A time-zone offset from Greenwich/UTC, such as {@code +02:00}.
  90  * <p>
  91  * A time-zone offset is the period of time that a time-zone differs from Greenwich/UTC.
  92  * This is usually a fixed number of hours and minutes.
  93  * <p>
  94  * Different parts of the world have different time-zone offsets.
  95  * The rules for how offsets vary by place and time of year are captured in the
  96  * {@link ZoneId} class.


 305      * @return the zone-offset, not null
 306      * @throws DateTimeException if the offset is not in the required range
 307      */
 308     public static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds) {
 309         validate(hours, minutes, seconds);
 310         int totalSeconds = totalSeconds(hours, minutes, seconds);
 311         return ofTotalSeconds(totalSeconds);
 312     }
 313 
 314     //-----------------------------------------------------------------------
 315     /**
 316      * Obtains an instance of {@code ZoneOffset} from a temporal object.
 317      * <p>
 318      * This obtains an offset based on the specified temporal.
 319      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
 320      * which this factory converts to an instance of {@code ZoneOffset}.
 321      * <p>
 322      * A {@code TemporalAccessor} represents some form of date and time information.
 323      * This factory converts the arbitrary temporal object to an instance of {@code ZoneOffset}.
 324      * <p>
 325      * The conversion uses the {@link Queries#offset()} query, which relies
 326      * on extracting the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS} field.
 327      * <p>
 328      * This method matches the signature of the functional interface {@link TemporalQuery}
 329      * allowing it to be used in queries via method reference, {@code ZoneOffset::from}.
 330      *
 331      * @param temporal  the temporal object to convert, not null
 332      * @return the zone-offset, not null
 333      * @throws DateTimeException if unable to convert to an {@code ZoneOffset}
 334      */
 335     public static ZoneOffset from(TemporalAccessor temporal) {
 336         ZoneOffset offset = temporal.query(Queries.offset());
 337         if (offset == null) {
 338             throw new DateTimeException("Unable to obtain ZoneOffset from TemporalAccessor: " + temporal.getClass());
 339         }
 340         return offset;
 341     }
 342 
 343     //-----------------------------------------------------------------------
 344     /**
 345      * Validates the offset fields.
 346      *
 347      * @param hours  the time-zone offset in hours, from -18 to +18
 348      * @param minutes  the time-zone offset in minutes, from 0 to &plusmn;59
 349      * @param seconds  the time-zone offset in seconds, from 0 to &plusmn;59
 350      * @throws DateTimeException if the offset is not in the required range
 351      */
 352     private static void validate(int hours, int minutes, int seconds) {
 353         if (hours < -18 || hours > 18) {
 354             throw new DateTimeException("Zone offset hours not in valid range: value " + hours +
 355                     " is not in the range -18 to 18");
 356         }


 517      */
 518     @Override
 519     public boolean isSupported(TemporalField field) {
 520         if (field instanceof ChronoField) {
 521             return field == OFFSET_SECONDS;
 522         }
 523         return field != null && field.isSupportedBy(this);
 524     }
 525 
 526     /**
 527      * Gets the range of valid values for the specified field.
 528      * <p>
 529      * The range object expresses the minimum and maximum valid values for a field.
 530      * This offset is used to enhance the accuracy of the returned range.
 531      * If it is not possible to return the range, because the field is not supported
 532      * or for some other reason, an exception is thrown.
 533      * <p>
 534      * If the field is a {@link ChronoField} then the query is implemented here.
 535      * The {@link #isSupported(TemporalField) supported fields} will return
 536      * appropriate range instances.
 537      * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
 538      * <p>
 539      * If the field is not a {@code ChronoField}, then the result of this method
 540      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
 541      * passing {@code this} as the argument.
 542      * Whether the range can be obtained is determined by the field.
 543      *
 544      * @param field  the field to query the range for, not null
 545      * @return the range of valid values for the field, not null
 546      * @throws DateTimeException if the range for the field cannot be obtained

 547      */
 548     @Override  // override for Javadoc
 549     public ValueRange range(TemporalField field) {
 550         return TemporalAccessor.super.range(field);
 551     }
 552 
 553     /**
 554      * Gets the value of the specified field from this offset as an {@code int}.
 555      * <p>
 556      * This queries this offset for the value for the specified field.
 557      * The returned value will always be within the valid range of values for the field.
 558      * If it is not possible to return the value, because the field is not supported
 559      * or for some other reason, an exception is thrown.
 560      * <p>
 561      * If the field is a {@link ChronoField} then the query is implemented here.
 562      * The {@code OFFSET_SECONDS} field returns the value of the offset.
 563      * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
 564      * <p>
 565      * If the field is not a {@code ChronoField}, then the result of this method
 566      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 567      * passing {@code this} as the argument. Whether the value can be obtained,
 568      * and what the value represents, is determined by the field.
 569      *
 570      * @param field  the field to get, not null
 571      * @return the value for the field
 572      * @throws DateTimeException if a value for the field cannot be obtained



 573      * @throws ArithmeticException if numeric overflow occurs
 574      */
 575     @Override  // override for Javadoc and performance
 576     public int get(TemporalField field) {
 577         if (field == OFFSET_SECONDS) {
 578             return totalSeconds;
 579         } else if (field instanceof ChronoField) {
 580             throw new DateTimeException("Unsupported field: " + field.getName());
 581         }
 582         return range(field).checkValidIntValue(getLong(field), field);
 583     }
 584 
 585     /**
 586      * Gets the value of the specified field from this offset as a {@code long}.
 587      * <p>
 588      * This queries this offset for the value for the specified field.
 589      * If it is not possible to return the value, because the field is not supported
 590      * or for some other reason, an exception is thrown.
 591      * <p>
 592      * If the field is a {@link ChronoField} then the query is implemented here.
 593      * The {@code OFFSET_SECONDS} field returns the value of the offset.
 594      * All other {@code ChronoField} instances will throw a {@code DateTimeException}.
 595      * <p>
 596      * If the field is not a {@code ChronoField}, then the result of this method
 597      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 598      * passing {@code this} as the argument. Whether the value can be obtained,
 599      * and what the value represents, is determined by the field.
 600      *
 601      * @param field  the field to get, not null
 602      * @return the value for the field
 603      * @throws DateTimeException if a value for the field cannot be obtained

 604      * @throws ArithmeticException if numeric overflow occurs
 605      */
 606     @Override
 607     public long getLong(TemporalField field) {
 608         if (field == OFFSET_SECONDS) {
 609             return totalSeconds;
 610         } else if (field instanceof ChronoField) {
 611             throw new DateTimeException("Unsupported field: " + field.getName());
 612         }
 613         return field.getFrom(this);
 614     }
 615 
 616     //-----------------------------------------------------------------------
 617     /**
 618      * Queries this offset using the specified query.
 619      * <p>
 620      * This queries this offset using the specified query strategy object.
 621      * The {@code TemporalQuery} object defines the logic to be used to
 622      * obtain the result. Read the documentation of the query to understand
 623      * what the result of this method will be.
 624      * <p>
 625      * The result of this method is obtained by invoking the
 626      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
 627      * specified query passing {@code this} as the argument.
 628      *
 629      * @param <R> the type of the result
 630      * @param query  the query to invoke, not null
 631      * @return the query result, null may be returned (defined by the query)
 632      * @throws DateTimeException if unable to query (defined by the query)
 633      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
 634      */
 635     @SuppressWarnings("unchecked")
 636     @Override
 637     public <R> R query(TemporalQuery<R> query) {
 638         if (query == Queries.offset() || query == Queries.zone()) {
 639             return (R) this;
 640         }
 641         return TemporalAccessor.super.query(query);
 642     }
 643 
 644     /**
 645      * Adjusts the specified temporal object to have the same offset as this object.
 646      * <p>
 647      * This returns a temporal object of the same observable type as the input
 648      * with the offset changed to be the same as this.
 649      * <p>
 650      * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
 651      * passing {@link ChronoField#OFFSET_SECONDS} as the field.
 652      * <p>
 653      * In most cases, it is clearer to reverse the calling pattern by using
 654      * {@link Temporal#with(TemporalAdjuster)}:
 655      * <pre>
 656      *   // these two lines are equivalent, but the second approach is recommended
 657      *   temporal = thisOffset.adjustInto(temporal);
 658      *   temporal = temporal.with(thisOffset);




  44  *    and/or other materials provided with the distribution.
  45  *
  46  *  * Neither the name of JSR-310 nor the names of its contributors
  47  *    may be used to endorse or promote products derived from this software
  48  *    without specific prior written permission.
  49  *
  50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  51  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  52  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  53  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  54  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  55  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  56  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  57  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  58  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  59  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  60  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  61  */
  62 package java.time;
  63 
  64 import java.time.temporal.UnsupportedTemporalTypeException;
  65 import static java.time.LocalTime.MINUTES_PER_HOUR;
  66 import static java.time.LocalTime.SECONDS_PER_HOUR;
  67 import static java.time.LocalTime.SECONDS_PER_MINUTE;
  68 import static java.time.temporal.ChronoField.OFFSET_SECONDS;
  69 
  70 import java.io.DataInput;
  71 import java.io.DataOutput;
  72 import java.io.IOException;
  73 import java.io.InvalidObjectException;
  74 import java.io.ObjectStreamException;
  75 import java.io.Serializable;
  76 import java.time.temporal.ChronoField;

  77 import java.time.temporal.Temporal;
  78 import java.time.temporal.TemporalAccessor;
  79 import java.time.temporal.TemporalAdjuster;
  80 import java.time.temporal.TemporalField;
  81 import java.time.temporal.TemporalQuery;
  82 import java.time.temporal.ValueRange;
  83 import java.time.zone.ZoneRules;
  84 import java.util.Objects;
  85 import java.util.concurrent.ConcurrentHashMap;
  86 import java.util.concurrent.ConcurrentMap;
  87 
  88 /**
  89  * A time-zone offset from Greenwich/UTC, such as {@code +02:00}.
  90  * <p>
  91  * A time-zone offset is the period of time that a time-zone differs from Greenwich/UTC.
  92  * This is usually a fixed number of hours and minutes.
  93  * <p>
  94  * Different parts of the world have different time-zone offsets.
  95  * The rules for how offsets vary by place and time of year are captured in the
  96  * {@link ZoneId} class.


 305      * @return the zone-offset, not null
 306      * @throws DateTimeException if the offset is not in the required range
 307      */
 308     public static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds) {
 309         validate(hours, minutes, seconds);
 310         int totalSeconds = totalSeconds(hours, minutes, seconds);
 311         return ofTotalSeconds(totalSeconds);
 312     }
 313 
 314     //-----------------------------------------------------------------------
 315     /**
 316      * Obtains an instance of {@code ZoneOffset} from a temporal object.
 317      * <p>
 318      * This obtains an offset based on the specified temporal.
 319      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
 320      * which this factory converts to an instance of {@code ZoneOffset}.
 321      * <p>
 322      * A {@code TemporalAccessor} represents some form of date and time information.
 323      * This factory converts the arbitrary temporal object to an instance of {@code ZoneOffset}.
 324      * <p>
 325      * The conversion uses the {@link TemporalQuery#offset()} query, which relies
 326      * on extracting the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS} field.
 327      * <p>
 328      * This method matches the signature of the functional interface {@link TemporalQuery}
 329      * allowing it to be used in queries via method reference, {@code ZoneOffset::from}.
 330      *
 331      * @param temporal  the temporal object to convert, not null
 332      * @return the zone-offset, not null
 333      * @throws DateTimeException if unable to convert to an {@code ZoneOffset}
 334      */
 335     public static ZoneOffset from(TemporalAccessor temporal) {
 336         ZoneOffset offset = temporal.query(TemporalQuery.offset());
 337         if (offset == null) {
 338             throw new DateTimeException("Unable to obtain ZoneOffset from TemporalAccessor: " + temporal.getClass());
 339         }
 340         return offset;
 341     }
 342 
 343     //-----------------------------------------------------------------------
 344     /**
 345      * Validates the offset fields.
 346      *
 347      * @param hours  the time-zone offset in hours, from -18 to +18
 348      * @param minutes  the time-zone offset in minutes, from 0 to &plusmn;59
 349      * @param seconds  the time-zone offset in seconds, from 0 to &plusmn;59
 350      * @throws DateTimeException if the offset is not in the required range
 351      */
 352     private static void validate(int hours, int minutes, int seconds) {
 353         if (hours < -18 || hours > 18) {
 354             throw new DateTimeException("Zone offset hours not in valid range: value " + hours +
 355                     " is not in the range -18 to 18");
 356         }


 517      */
 518     @Override
 519     public boolean isSupported(TemporalField field) {
 520         if (field instanceof ChronoField) {
 521             return field == OFFSET_SECONDS;
 522         }
 523         return field != null && field.isSupportedBy(this);
 524     }
 525 
 526     /**
 527      * Gets the range of valid values for the specified field.
 528      * <p>
 529      * The range object expresses the minimum and maximum valid values for a field.
 530      * This offset is used to enhance the accuracy of the returned range.
 531      * If it is not possible to return the range, because the field is not supported
 532      * or for some other reason, an exception is thrown.
 533      * <p>
 534      * If the field is a {@link ChronoField} then the query is implemented here.
 535      * The {@link #isSupported(TemporalField) supported fields} will return
 536      * appropriate range instances.
 537      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 538      * <p>
 539      * If the field is not a {@code ChronoField}, then the result of this method
 540      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
 541      * passing {@code this} as the argument.
 542      * Whether the range can be obtained is determined by the field.
 543      *
 544      * @param field  the field to query the range for, not null
 545      * @return the range of valid values for the field, not null
 546      * @throws DateTimeException if the range for the field cannot be obtained
 547      * @throws UnsupportedTemporalTypeException if the field is not supported
 548      */
 549     @Override  // override for Javadoc
 550     public ValueRange range(TemporalField field) {
 551         return TemporalAccessor.super.range(field);
 552     }
 553 
 554     /**
 555      * Gets the value of the specified field from this offset as an {@code int}.
 556      * <p>
 557      * This queries this offset for the value for the specified field.
 558      * The returned value will always be within the valid range of values for the field.
 559      * If it is not possible to return the value, because the field is not supported
 560      * or for some other reason, an exception is thrown.
 561      * <p>
 562      * If the field is a {@link ChronoField} then the query is implemented here.
 563      * The {@code OFFSET_SECONDS} field returns the value of the offset.
 564      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 565      * <p>
 566      * If the field is not a {@code ChronoField}, then the result of this method
 567      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 568      * passing {@code this} as the argument. Whether the value can be obtained,
 569      * and what the value represents, is determined by the field.
 570      *
 571      * @param field  the field to get, not null
 572      * @return the value for the field
 573      * @throws DateTimeException if a value for the field cannot be obtained or
 574      *         the value is outside the range of valid values for the field
 575      * @throws UnsupportedTemporalTypeException if the field is not supported or
 576      *         the range of values exceeds an {@code int}
 577      * @throws ArithmeticException if numeric overflow occurs
 578      */
 579     @Override  // override for Javadoc and performance
 580     public int get(TemporalField field) {
 581         if (field == OFFSET_SECONDS) {
 582             return totalSeconds;
 583         } else if (field instanceof ChronoField) {
 584             throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
 585         }
 586         return range(field).checkValidIntValue(getLong(field), field);
 587     }
 588 
 589     /**
 590      * Gets the value of the specified field from this offset as a {@code long}.
 591      * <p>
 592      * This queries this offset for the value for the specified field.
 593      * If it is not possible to return the value, because the field is not supported
 594      * or for some other reason, an exception is thrown.
 595      * <p>
 596      * If the field is a {@link ChronoField} then the query is implemented here.
 597      * The {@code OFFSET_SECONDS} field returns the value of the offset.
 598      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
 599      * <p>
 600      * If the field is not a {@code ChronoField}, then the result of this method
 601      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
 602      * passing {@code this} as the argument. Whether the value can be obtained,
 603      * and what the value represents, is determined by the field.
 604      *
 605      * @param field  the field to get, not null
 606      * @return the value for the field
 607      * @throws DateTimeException if a value for the field cannot be obtained
 608      * @throws UnsupportedTemporalTypeException if the field is not supported
 609      * @throws ArithmeticException if numeric overflow occurs
 610      */
 611     @Override
 612     public long getLong(TemporalField field) {
 613         if (field == OFFSET_SECONDS) {
 614             return totalSeconds;
 615         } else if (field instanceof ChronoField) {
 616             throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
 617         }
 618         return field.getFrom(this);
 619     }
 620 
 621     //-----------------------------------------------------------------------
 622     /**
 623      * Queries this offset using the specified query.
 624      * <p>
 625      * This queries this offset using the specified query strategy object.
 626      * The {@code TemporalQuery} object defines the logic to be used to
 627      * obtain the result. Read the documentation of the query to understand
 628      * what the result of this method will be.
 629      * <p>
 630      * The result of this method is obtained by invoking the
 631      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
 632      * specified query passing {@code this} as the argument.
 633      *
 634      * @param <R> the type of the result
 635      * @param query  the query to invoke, not null
 636      * @return the query result, null may be returned (defined by the query)
 637      * @throws DateTimeException if unable to query (defined by the query)
 638      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
 639      */
 640     @SuppressWarnings("unchecked")
 641     @Override
 642     public <R> R query(TemporalQuery<R> query) {
 643         if (query == TemporalQuery.offset() || query == TemporalQuery.zone()) {
 644             return (R) this;
 645         }
 646         return TemporalAccessor.super.query(query);
 647     }
 648 
 649     /**
 650      * Adjusts the specified temporal object to have the same offset as this object.
 651      * <p>
 652      * This returns a temporal object of the same observable type as the input
 653      * with the offset changed to be the same as this.
 654      * <p>
 655      * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
 656      * passing {@link ChronoField#OFFSET_SECONDS} as the field.
 657      * <p>
 658      * In most cases, it is clearer to reverse the calling pattern by using
 659      * {@link Temporal#with(TemporalAdjuster)}:
 660      * <pre>
 661      *   // these two lines are equivalent, but the second approach is recommended
 662      *   temporal = thisOffset.adjustInto(temporal);
 663      *   temporal = temporal.with(thisOffset);