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 ±59
349 * @param seconds the time-zone offset in seconds, from 0 to ±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 ±59
349 * @param seconds the time-zone offset in seconds, from 0 to ±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);
|