1 /*
2 * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * This file is available under and governed by the GNU General Public
28 * License version 2 only, as published by the Free Software Foundation.
29 * However, the following notice accompanied the original version of this
30 * file:
31 *
32 * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos
33 *
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions are met:
38 *
39 * * Redistributions of source code must retain the above copyright notice,
40 * this list of conditions and the following disclaimer.
41 *
42 * * Redistributions in binary form must reproduce the above copyright notice,
43 * this list of conditions and the following disclaimer in the documentation
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.NANOS_PER_HOUR;
65 import static java.time.LocalTime.NANOS_PER_MINUTE;
66 import static java.time.LocalTime.NANOS_PER_SECOND;
67 import static java.time.LocalTime.SECONDS_PER_DAY;
68 import static java.time.temporal.ChronoField.NANO_OF_DAY;
69 import static java.time.temporal.ChronoField.OFFSET_SECONDS;
70 import static java.time.temporal.ChronoUnit.NANOS;
71
72 import java.io.IOException;
73 import java.io.ObjectInput;
74 import java.io.ObjectOutput;
75 import java.io.InvalidObjectException;
76 import java.io.ObjectInputStream;
77 import java.io.Serializable;
78 import java.time.format.DateTimeFormatter;
79 import java.time.format.DateTimeParseException;
80 import java.time.temporal.ChronoField;
81 import java.time.temporal.ChronoUnit;
82 import java.time.temporal.Temporal;
83 import java.time.temporal.TemporalAccessor;
84 import java.time.temporal.TemporalAdjuster;
85 import java.time.temporal.TemporalAmount;
86 import java.time.temporal.TemporalField;
87 import java.time.temporal.TemporalQueries;
88 import java.time.temporal.TemporalQuery;
89 import java.time.temporal.TemporalUnit;
90 import java.time.temporal.UnsupportedTemporalTypeException;
91 import java.time.temporal.ValueRange;
92 import java.time.zone.ZoneRules;
93 import java.util.Objects;
94
95 import jdk.internal.util.DateTimeHelper;
96
97 /**
98 * A time with an offset from UTC/Greenwich in the ISO-8601 calendar system,
99 * such as {@code 10:15:30+01:00}.
100 * <p>
101 * {@code OffsetTime} is an immutable date-time object that represents a time, often
102 * viewed as hour-minute-second-offset.
103 * This class stores all time fields, to a precision of nanoseconds,
104 * as well as a zone offset.
105 * For example, the value "13:45:30.123456789+02:00" can be stored
106 * in an {@code OffsetTime}.
107 * <p>
108 * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
109 * class; programmers should treat instances that are {@linkplain #equals(Object) equal}
110 * as interchangeable and should not use instances for synchronization, mutexes, or
111 * with {@linkplain java.lang.ref.Reference object references}.
112 *
113 * <div class="preview-block">
114 * <div class="preview-comment">
115 * When preview features are enabled, {@code OffsetTime} is a {@linkplain Class#isValue value class}.
116 * Use of value class instances for synchronization, mutexes, or with
117 * {@linkplain java.lang.ref.Reference object references} result in
118 * {@link IdentityException}.
119 * </div>
120 * </div>
121 *
122 * @implSpec
123 * This class is immutable and thread-safe.
124 *
125 * @since 1.8
126 */
127 @jdk.internal.ValueBased
128 @jdk.internal.MigratedValueClass
129 public final class OffsetTime
130 implements Temporal, TemporalAdjuster, Comparable<OffsetTime>, Serializable {
131
132 /**
133 * The minimum supported {@code OffsetTime}, '00:00:00+18:00'.
134 * This is the time of midnight at the start of the day in the maximum offset
135 * (larger offsets are earlier on the time-line).
136 * This combines {@link LocalTime#MIN} and {@link ZoneOffset#MAX}.
137 * This could be used by an application as a "far past" date.
138 */
139 public static final OffsetTime MIN = LocalTime.MIN.atOffset(ZoneOffset.MAX);
140 /**
141 * The maximum supported {@code OffsetTime}, '23:59:59.999999999-18:00'.
142 * This is the time just before midnight at the end of the day in the minimum offset
143 * (larger negative offsets are later on the time-line).
144 * This combines {@link LocalTime#MAX} and {@link ZoneOffset#MIN}.
145 * This could be used by an application as a "far future" date.
146 */
147 public static final OffsetTime MAX = LocalTime.MAX.atOffset(ZoneOffset.MIN);
148
149 /**
150 * Serialization version.
151 */
152 @java.io.Serial
153 private static final long serialVersionUID = 7264499704384272492L;
154
155 /**
156 * @serial The local date-time.
157 */
158 private final LocalTime time;
159 /**
160 * @serial The offset from UTC/Greenwich.
161 */
162 private final ZoneOffset offset;
163
164 //-----------------------------------------------------------------------
165 /**
166 * Obtains the current time from the system clock in the default time-zone.
167 * <p>
168 * This will query the {@link Clock#systemDefaultZone() system clock} in the default
169 * time-zone to obtain the current time.
170 * The offset will be calculated from the time-zone in the clock.
171 * <p>
172 * Using this method will prevent the ability to use an alternate clock for testing
173 * because the clock is hard-coded.
174 *
175 * @return the current time using the system clock and default time-zone, not null
176 */
177 public static OffsetTime now() {
178 return now(Clock.systemDefaultZone());
179 }
180
181 /**
182 * Obtains the current time from the system clock in the specified time-zone.
183 * <p>
184 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current time.
185 * Specifying the time-zone avoids dependence on the default time-zone.
186 * The offset will be calculated from the specified time-zone.
187 * <p>
188 * Using this method will prevent the ability to use an alternate clock for testing
189 * because the clock is hard-coded.
190 *
191 * @param zone the zone ID to use, not null
192 * @return the current time using the system clock, not null
193 */
194 public static OffsetTime now(ZoneId zone) {
195 return now(Clock.system(zone));
196 }
197
198 /**
199 * Obtains the current time from the specified clock.
200 * <p>
201 * This will query the specified clock to obtain the current time.
202 * The offset will be calculated from the time-zone in the clock.
203 * <p>
204 * Using this method allows the use of an alternate clock for testing.
205 * The alternate clock may be introduced using {@link Clock dependency injection}.
206 *
207 * @param clock the clock to use, not null
208 * @return the current time, not null
209 */
210 public static OffsetTime now(Clock clock) {
211 Objects.requireNonNull(clock, "clock");
212 final Instant now = clock.instant(); // called once
213 return ofInstant(now, clock.getZone().getRules().getOffset(now));
214 }
215
216 //-----------------------------------------------------------------------
217 /**
218 * Obtains an instance of {@code OffsetTime} from a local time and an offset.
219 *
220 * @param time the local time, not null
221 * @param offset the zone offset, not null
222 * @return the offset time, not null
223 */
224 public static OffsetTime of(LocalTime time, ZoneOffset offset) {
225 return new OffsetTime(time, offset);
226 }
227
228 /**
229 * Obtains an instance of {@code OffsetTime} from an hour, minute, second and nanosecond.
230 * <p>
231 * This creates an offset time with the four specified fields.
232 * <p>
233 * This method exists primarily for writing test cases.
234 * Non test-code will typically use other methods to create an offset time.
235 * {@code LocalTime} has two additional convenience variants of the
236 * equivalent factory method taking fewer arguments.
237 * They are not provided here to reduce the footprint of the API.
238 *
239 * @param hour the hour-of-day to represent, from 0 to 23
240 * @param minute the minute-of-hour to represent, from 0 to 59
241 * @param second the second-of-minute to represent, from 0 to 59
242 * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
243 * @param offset the zone offset, not null
244 * @return the offset time, not null
245 * @throws DateTimeException if the value of any field is out of range
246 */
247 public static OffsetTime of(int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) {
248 return new OffsetTime(LocalTime.of(hour, minute, second, nanoOfSecond), offset);
249 }
250
251 //-----------------------------------------------------------------------
252 /**
253 * Obtains an instance of {@code OffsetTime} from an {@code Instant} and zone ID.
254 * <p>
255 * This creates an offset time with the same instant as that specified.
256 * Finding the offset from UTC/Greenwich is simple as there is only one valid
257 * offset for each instant.
258 * <p>
259 * The date component of the instant is dropped during the conversion.
260 * This means that the conversion can never fail due to the instant being
261 * out of the valid range of dates.
262 *
263 * @param instant the instant to create the time from, not null
264 * @param zone the time-zone, which may be an offset, not null
265 * @return the offset time, not null
266 */
267 public static OffsetTime ofInstant(Instant instant, ZoneId zone) {
268 Objects.requireNonNull(instant, "instant");
269 Objects.requireNonNull(zone, "zone");
270 ZoneRules rules = zone.getRules();
271 ZoneOffset offset = rules.getOffset(instant);
272 long localSecond = instant.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
273 int secsOfDay = Math.floorMod(localSecond, SECONDS_PER_DAY);
274 LocalTime time = LocalTime.ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
275 return new OffsetTime(time, offset);
276 }
277
278 //-----------------------------------------------------------------------
279 /**
280 * Obtains an instance of {@code OffsetTime} from a temporal object.
281 * <p>
282 * This obtains an offset time based on the specified temporal.
283 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
284 * which this factory converts to an instance of {@code OffsetTime}.
285 * <p>
286 * The conversion extracts and combines the {@code ZoneOffset} and the
287 * {@code LocalTime} from the temporal object.
288 * Implementations are permitted to perform optimizations such as accessing
289 * those fields that are equivalent to the relevant objects.
290 * <p>
291 * This method matches the signature of the functional interface {@link TemporalQuery}
292 * allowing it to be used as a query via method reference, {@code OffsetTime::from}.
293 *
294 * @param temporal the temporal object to convert, not null
295 * @return the offset time, not null
296 * @throws DateTimeException if unable to convert to an {@code OffsetTime}
297 */
298 public static OffsetTime from(TemporalAccessor temporal) {
299 if (temporal instanceof OffsetTime) {
300 return (OffsetTime) temporal;
301 }
302 try {
303 LocalTime time = LocalTime.from(temporal);
304 ZoneOffset offset = ZoneOffset.from(temporal);
305 return new OffsetTime(time, offset);
306 } catch (DateTimeException ex) {
307 throw new DateTimeException("Unable to obtain OffsetTime from TemporalAccessor: " +
308 temporal + " of type " + temporal.getClass().getName(), ex);
309 }
310 }
311
312 //-----------------------------------------------------------------------
313 /**
314 * Obtains an instance of {@code OffsetTime} from a text string such as {@code 10:15:30+01:00}.
315 * <p>
316 * The string must represent a valid time and is parsed using
317 * {@link java.time.format.DateTimeFormatter#ISO_OFFSET_TIME}.
318 *
319 * @param text the text to parse such as "10:15:30+01:00", not null
320 * @return the parsed local time, not null
321 * @throws DateTimeParseException if the text cannot be parsed
322 */
323 public static OffsetTime parse(CharSequence text) {
324 return parse(text, DateTimeFormatter.ISO_OFFSET_TIME);
325 }
326
327 /**
328 * Obtains an instance of {@code OffsetTime} from a text string using a specific formatter.
329 * <p>
330 * The text is parsed using the formatter, returning a time.
331 *
332 * @param text the text to parse, not null
333 * @param formatter the formatter to use, not null
334 * @return the parsed offset time, not null
335 * @throws DateTimeParseException if the text cannot be parsed
336 */
337 public static OffsetTime parse(CharSequence text, DateTimeFormatter formatter) {
338 Objects.requireNonNull(formatter, "formatter");
339 return formatter.parse(text, OffsetTime::from);
340 }
341
342 //-----------------------------------------------------------------------
343 /**
344 * Constructor.
345 *
346 * @param time the local time, not null
347 * @param offset the zone offset, not null
348 */
349 private OffsetTime(LocalTime time, ZoneOffset offset) {
350 this.time = Objects.requireNonNull(time, "time");
351 this.offset = Objects.requireNonNull(offset, "offset");
352 }
353
354 /**
355 * Returns a new time based on this one, returning {@code this} where possible.
356 *
357 * @param time the time to create with, not null
358 * @param offset the zone offset to create with, not null
359 */
360 private OffsetTime with(LocalTime time, ZoneOffset offset) {
361 if (this.time == time && this.offset.equals(offset)) {
362 return this;
363 }
364 return new OffsetTime(time, offset);
365 }
366
367 //-----------------------------------------------------------------------
368 /**
369 * Checks if the specified field is supported.
370 * <p>
371 * This checks if this time can be queried for the specified field.
372 * If false, then calling the {@link #range(TemporalField) range},
373 * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
374 * methods will throw an exception.
375 * <p>
376 * If the field is a {@link ChronoField} then the query is implemented here.
377 * The supported fields are:
378 * <ul>
379 * <li>{@code NANO_OF_SECOND}
380 * <li>{@code NANO_OF_DAY}
381 * <li>{@code MICRO_OF_SECOND}
382 * <li>{@code MICRO_OF_DAY}
383 * <li>{@code MILLI_OF_SECOND}
384 * <li>{@code MILLI_OF_DAY}
385 * <li>{@code SECOND_OF_MINUTE}
386 * <li>{@code SECOND_OF_DAY}
387 * <li>{@code MINUTE_OF_HOUR}
388 * <li>{@code MINUTE_OF_DAY}
389 * <li>{@code HOUR_OF_AMPM}
390 * <li>{@code CLOCK_HOUR_OF_AMPM}
391 * <li>{@code HOUR_OF_DAY}
392 * <li>{@code CLOCK_HOUR_OF_DAY}
393 * <li>{@code AMPM_OF_DAY}
394 * <li>{@code OFFSET_SECONDS}
395 * </ul>
396 * All other {@code ChronoField} instances will return false.
397 * <p>
398 * If the field is not a {@code ChronoField}, then the result of this method
399 * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
400 * passing {@code this} as the argument.
401 * Whether the field is supported is determined by the field.
402 *
403 * @param field the field to check, null returns false
404 * @return true if the field is supported on this time, false if not
405 */
406 @Override
407 public boolean isSupported(TemporalField field) {
408 if (field instanceof ChronoField) {
409 return field.isTimeBased() || field == OFFSET_SECONDS;
410 }
411 return field != null && field.isSupportedBy(this);
412 }
413
414 /**
415 * Checks if the specified unit is supported.
416 * <p>
417 * This checks if the specified unit can be added to, or subtracted from, this offset-time.
418 * If false, then calling the {@link #plus(long, TemporalUnit)} and
419 * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
420 * <p>
421 * If the unit is a {@link ChronoUnit} then the query is implemented here.
422 * The supported units are:
423 * <ul>
424 * <li>{@code NANOS}
425 * <li>{@code MICROS}
426 * <li>{@code MILLIS}
427 * <li>{@code SECONDS}
428 * <li>{@code MINUTES}
429 * <li>{@code HOURS}
430 * <li>{@code HALF_DAYS}
431 * </ul>
432 * All other {@code ChronoUnit} instances will return false.
433 * <p>
434 * If the unit is not a {@code ChronoUnit}, then the result of this method
435 * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
436 * passing {@code this} as the argument.
437 * Whether the unit is supported is determined by the unit.
438 *
439 * @param unit the unit to check, null returns false
440 * @return true if the unit can be added/subtracted, false if not
441 */
442 @Override // override for Javadoc
443 public boolean isSupported(TemporalUnit unit) {
444 if (unit instanceof ChronoUnit) {
445 return unit.isTimeBased();
446 }
447 return unit != null && unit.isSupportedBy(this);
448 }
449
450 //-----------------------------------------------------------------------
451 /**
452 * Gets the range of valid values for the specified field.
453 * <p>
454 * The range object expresses the minimum and maximum valid values for a field.
455 * This time is used to enhance the accuracy of the returned range.
456 * If it is not possible to return the range, because the field is not supported
457 * or for some other reason, an exception is thrown.
458 * <p>
459 * If the field is a {@link ChronoField} then the query is implemented here.
460 * The {@link #isSupported(TemporalField) supported fields} will return
461 * appropriate range instances.
462 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
463 * <p>
464 * If the field is not a {@code ChronoField}, then the result of this method
465 * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
466 * passing {@code this} as the argument.
467 * Whether the range can be obtained is determined by the field.
468 *
469 * @param field the field to query the range for, not null
470 * @return the range of valid values for the field, not null
471 * @throws DateTimeException if the range for the field cannot be obtained
472 * @throws UnsupportedTemporalTypeException if the field is not supported
473 */
474 @Override
475 public ValueRange range(TemporalField field) {
476 if (field instanceof ChronoField) {
477 if (field == OFFSET_SECONDS) {
478 return field.range();
479 }
480 return time.range(field);
481 }
482 return field.rangeRefinedBy(this);
483 }
484
485 /**
486 * Gets the value of the specified field from this time as an {@code int}.
487 * <p>
488 * This queries this time for the value of the specified field.
489 * The returned value will always be within the valid range of values for the field.
490 * If it is not possible to return the value, because the field is not supported
491 * or for some other reason, an exception is thrown.
492 * <p>
493 * If the field is a {@link ChronoField} then the query is implemented here.
494 * The {@link #isSupported(TemporalField) supported fields} will return valid
495 * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
496 * which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
497 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
498 * <p>
499 * If the field is not a {@code ChronoField}, then the result of this method
500 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
501 * passing {@code this} as the argument. Whether the value can be obtained,
502 * and what the value represents, is determined by the field.
503 *
504 * @param field the field to get, not null
505 * @return the value for the field
506 * @throws DateTimeException if a value for the field cannot be obtained or
507 * the value is outside the range of valid values for the field
508 * @throws UnsupportedTemporalTypeException if the field is not supported or
509 * the range of values exceeds an {@code int}
510 * @throws ArithmeticException if numeric overflow occurs
511 */
512 @Override // override for Javadoc
513 public int get(TemporalField field) {
514 return Temporal.super.get(field);
515 }
516
517 /**
518 * Gets the value of the specified field from this time as a {@code long}.
519 * <p>
520 * This queries this time for the value of the specified field.
521 * If it is not possible to return the value, because the field is not supported
522 * or for some other reason, an exception is thrown.
523 * <p>
524 * If the field is a {@link ChronoField} then the query is implemented here.
525 * The {@link #isSupported(TemporalField) supported fields} will return valid
526 * values based on this time.
527 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
528 * <p>
529 * If the field is not a {@code ChronoField}, then the result of this method
530 * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
531 * passing {@code this} as the argument. Whether the value can be obtained,
532 * and what the value represents, is determined by the field.
533 *
534 * @param field the field to get, not null
535 * @return the value for the field
536 * @throws DateTimeException if a value for the field cannot be obtained
537 * @throws UnsupportedTemporalTypeException if the field is not supported
538 * @throws ArithmeticException if numeric overflow occurs
539 */
540 @Override
541 public long getLong(TemporalField field) {
542 if (field instanceof ChronoField) {
543 if (field == OFFSET_SECONDS) {
544 return offset.getTotalSeconds();
545 }
546 return time.getLong(field);
547 }
548 return field.getFrom(this);
549 }
550
551 //-----------------------------------------------------------------------
552 /**
553 * Gets the zone offset, such as '+01:00'.
554 * <p>
555 * This is the offset of the local time from UTC/Greenwich.
556 *
557 * @return the zone offset, not null
558 */
559 public ZoneOffset getOffset() {
560 return offset;
561 }
562
563 /**
564 * Returns a copy of this {@code OffsetTime} with the specified offset ensuring
565 * that the result has the same local time.
566 * <p>
567 * This method returns an object with the same {@code LocalTime} and the specified {@code ZoneOffset}.
568 * No calculation is needed or performed.
569 * For example, if this time represents {@code 10:30+02:00} and the offset specified is
570 * {@code +03:00}, then this method will return {@code 10:30+03:00}.
571 * <p>
572 * To take into account the difference between the offsets, and adjust the time fields,
573 * use {@link #withOffsetSameInstant}.
574 * <p>
575 * This instance is immutable and unaffected by this method call.
576 *
577 * @param offset the zone offset to change to, not null
578 * @return an {@code OffsetTime} based on this time with the requested offset, not null
579 */
580 public OffsetTime withOffsetSameLocal(ZoneOffset offset) {
581 return offset != null && offset.equals(this.offset) ? this : new OffsetTime(time, offset);
582 }
583
584 /**
585 * Returns a copy of this {@code OffsetTime} with the specified offset ensuring
586 * that the result is at the same instant on an implied day.
587 * <p>
588 * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalTime}
589 * adjusted by the difference between the two offsets.
590 * This will result in the old and new objects representing the same instant on an implied day.
591 * This is useful for finding the local time in a different offset.
592 * For example, if this time represents {@code 10:30+02:00} and the offset specified is
593 * {@code +03:00}, then this method will return {@code 11:30+03:00}.
594 * <p>
595 * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}.
596 * <p>
597 * This instance is immutable and unaffected by this method call.
598 *
599 * @param offset the zone offset to change to, not null
600 * @return an {@code OffsetTime} based on this time with the requested offset, not null
601 */
602 public OffsetTime withOffsetSameInstant(ZoneOffset offset) {
603 if (offset.equals(this.offset)) {
604 return this;
605 }
606 int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds();
607 LocalTime adjusted = time.plusSeconds(difference);
608 return new OffsetTime(adjusted, offset);
609 }
610
611 //-----------------------------------------------------------------------
612 /**
613 * Gets the {@code LocalTime} part of this date-time.
614 * <p>
615 * This returns a {@code LocalTime} with the same hour, minute, second and
616 * nanosecond as this date-time.
617 *
618 * @return the time part of this date-time, not null
619 */
620 public LocalTime toLocalTime() {
621 return time;
622 }
623
624 //-----------------------------------------------------------------------
625 /**
626 * Gets the hour-of-day field.
627 *
628 * @return the hour-of-day, from 0 to 23
629 */
630 public int getHour() {
631 return time.getHour();
632 }
633
634 /**
635 * Gets the minute-of-hour field.
636 *
637 * @return the minute-of-hour, from 0 to 59
638 */
639 public int getMinute() {
640 return time.getMinute();
641 }
642
643 /**
644 * Gets the second-of-minute field.
645 *
646 * @return the second-of-minute, from 0 to 59
647 */
648 public int getSecond() {
649 return time.getSecond();
650 }
651
652 /**
653 * Gets the nano-of-second field.
654 *
655 * @return the nano-of-second, from 0 to 999,999,999
656 */
657 public int getNano() {
658 return time.getNano();
659 }
660
661 //-----------------------------------------------------------------------
662 /**
663 * Returns an adjusted copy of this time.
664 * <p>
665 * This returns an {@code OffsetTime}, based on this one, with the time adjusted.
666 * The adjustment takes place using the specified adjuster strategy object.
667 * Read the documentation of the adjuster to understand what adjustment will be made.
668 * <p>
669 * A simple adjuster might simply set the one of the fields, such as the hour field.
670 * A more complex adjuster might set the time to the last hour of the day.
671 * <p>
672 * The classes {@link LocalTime} and {@link ZoneOffset} implement {@code TemporalAdjuster},
673 * thus this method can be used to change the time or offset:
674 * <pre>
675 * result = offsetTime.with(time);
676 * result = offsetTime.with(offset);
677 * </pre>
678 * <p>
679 * The result of this method is obtained by invoking the
680 * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
681 * specified adjuster passing {@code this} as the argument.
682 * <p>
683 * This instance is immutable and unaffected by this method call.
684 *
685 * @param adjuster the adjuster to use, not null
686 * @return an {@code OffsetTime} based on {@code this} with the adjustment made, not null
687 * @throws DateTimeException if the adjustment cannot be made
688 * @throws ArithmeticException if numeric overflow occurs
689 */
690 @Override
691 public OffsetTime with(TemporalAdjuster adjuster) {
692 // optimizations
693 if (adjuster instanceof LocalTime) {
694 return with((LocalTime) adjuster, offset);
695 } else if (adjuster instanceof ZoneOffset) {
696 return with(time, (ZoneOffset) adjuster);
697 } else if (adjuster instanceof OffsetTime) {
698 return (OffsetTime) adjuster;
699 }
700 return (OffsetTime) adjuster.adjustInto(this);
701 }
702
703 /**
704 * Returns a copy of this time with the specified field set to a new value.
705 * <p>
706 * This returns an {@code OffsetTime}, based on this one, with the value
707 * for the specified field changed.
708 * This can be used to change any supported field, such as the hour, minute or second.
709 * If it is not possible to set the value, because the field is not supported or for
710 * some other reason, an exception is thrown.
711 * <p>
712 * If the field is a {@link ChronoField} then the adjustment is implemented here.
713 * <p>
714 * The {@code OFFSET_SECONDS} field will return a time with the specified offset.
715 * The local time is unaltered. If the new offset value is outside the valid range
716 * then a {@code DateTimeException} will be thrown.
717 * <p>
718 * The other {@link #isSupported(TemporalField) supported fields} will behave as per
719 * the matching method on {@link LocalTime#with(TemporalField, long)} LocalTime}.
720 * In this case, the offset is not part of the calculation and will be unchanged.
721 * <p>
722 * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
723 * <p>
724 * If the field is not a {@code ChronoField}, then the result of this method
725 * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
726 * passing {@code this} as the argument. In this case, the field determines
727 * whether and how to adjust the instant.
728 * <p>
729 * This instance is immutable and unaffected by this method call.
730 *
731 * @param field the field to set in the result, not null
732 * @param newValue the new value of the field in the result
733 * @return an {@code OffsetTime} based on {@code this} with the specified field set, not null
734 * @throws DateTimeException if the field cannot be set
735 * @throws UnsupportedTemporalTypeException if the field is not supported
736 * @throws ArithmeticException if numeric overflow occurs
737 */
738 @Override
739 public OffsetTime with(TemporalField field, long newValue) {
740 if (field instanceof ChronoField) {
741 if (field == OFFSET_SECONDS) {
742 ChronoField f = (ChronoField) field;
743 return with(time, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue)));
744 }
745 return with(time.with(field, newValue), offset);
746 }
747 return field.adjustInto(this, newValue);
748 }
749
750 //-----------------------------------------------------------------------
751 /**
752 * Returns a copy of this {@code OffsetTime} with the hour-of-day altered.
753 * <p>
754 * The offset does not affect the calculation and will be the same in the result.
755 * <p>
756 * This instance is immutable and unaffected by this method call.
757 *
758 * @param hour the hour-of-day to set in the result, from 0 to 23
759 * @return an {@code OffsetTime} based on this time with the requested hour, not null
760 * @throws DateTimeException if the hour value is invalid
761 */
762 public OffsetTime withHour(int hour) {
763 return with(time.withHour(hour), offset);
764 }
765
766 /**
767 * Returns a copy of this {@code OffsetTime} with the minute-of-hour altered.
768 * <p>
769 * The offset does not affect the calculation and will be the same in the result.
770 * <p>
771 * This instance is immutable and unaffected by this method call.
772 *
773 * @param minute the minute-of-hour to set in the result, from 0 to 59
774 * @return an {@code OffsetTime} based on this time with the requested minute, not null
775 * @throws DateTimeException if the minute value is invalid
776 */
777 public OffsetTime withMinute(int minute) {
778 return with(time.withMinute(minute), offset);
779 }
780
781 /**
782 * Returns a copy of this {@code OffsetTime} with the second-of-minute altered.
783 * <p>
784 * The offset does not affect the calculation and will be the same in the result.
785 * <p>
786 * This instance is immutable and unaffected by this method call.
787 *
788 * @param second the second-of-minute to set in the result, from 0 to 59
789 * @return an {@code OffsetTime} based on this time with the requested second, not null
790 * @throws DateTimeException if the second value is invalid
791 */
792 public OffsetTime withSecond(int second) {
793 return with(time.withSecond(second), offset);
794 }
795
796 /**
797 * Returns a copy of this {@code OffsetTime} with the nano-of-second altered.
798 * <p>
799 * The offset does not affect the calculation and will be the same in the result.
800 * <p>
801 * This instance is immutable and unaffected by this method call.
802 *
803 * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
804 * @return an {@code OffsetTime} based on this time with the requested nanosecond, not null
805 * @throws DateTimeException if the nanos value is invalid
806 */
807 public OffsetTime withNano(int nanoOfSecond) {
808 return with(time.withNano(nanoOfSecond), offset);
809 }
810
811 //-----------------------------------------------------------------------
812 /**
813 * Returns a copy of this {@code OffsetTime} with the time truncated.
814 * <p>
815 * Truncation returns a copy of the original time with fields
816 * smaller than the specified unit set to zero.
817 * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
818 * will set the second-of-minute and nano-of-second field to zero.
819 * <p>
820 * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
821 * that divides into the length of a standard day without remainder.
822 * This includes all supplied time units on {@link ChronoUnit} and
823 * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
824 * <p>
825 * The offset does not affect the calculation and will be the same in the result.
826 * <p>
827 * This instance is immutable and unaffected by this method call.
828 *
829 * @param unit the unit to truncate to, not null
830 * @return an {@code OffsetTime} based on this time with the time truncated, not null
831 * @throws DateTimeException if unable to truncate
832 * @throws UnsupportedTemporalTypeException if the unit is not supported
833 */
834 public OffsetTime truncatedTo(TemporalUnit unit) {
835 return with(time.truncatedTo(unit), offset);
836 }
837
838 //-----------------------------------------------------------------------
839 /**
840 * Returns a copy of this time with the specified amount added.
841 * <p>
842 * This returns an {@code OffsetTime}, based on this one, with the specified amount added.
843 * The amount is typically {@link Duration} but may be any other type implementing
844 * the {@link TemporalAmount} interface.
845 * <p>
846 * The calculation is delegated to the amount object by calling
847 * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
848 * to implement the addition in any way it wishes, however it typically
849 * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
850 * of the amount implementation to determine if it can be successfully added.
851 * <p>
852 * This instance is immutable and unaffected by this method call.
853 *
854 * @param amountToAdd the amount to add, not null
855 * @return an {@code OffsetTime} based on this time with the addition made, not null
856 * @throws DateTimeException if the addition cannot be made
857 * @throws ArithmeticException if numeric overflow occurs
858 */
859 @Override
860 public OffsetTime plus(TemporalAmount amountToAdd) {
861 return (OffsetTime) amountToAdd.addTo(this);
862 }
863
864 /**
865 * Returns a copy of this time with the specified amount added.
866 * <p>
867 * This returns an {@code OffsetTime}, based on this one, with the amount
868 * in terms of the unit added. If it is not possible to add the amount, because the
869 * unit is not supported or for some other reason, an exception is thrown.
870 * <p>
871 * If the field is a {@link ChronoUnit} then the addition is implemented by
872 * {@link LocalTime#plus(long, TemporalUnit)}.
873 * The offset is not part of the calculation and will be unchanged in the result.
874 * <p>
875 * If the field is not a {@code ChronoUnit}, then the result of this method
876 * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
877 * passing {@code this} as the argument. In this case, the unit determines
878 * whether and how to perform the addition.
879 * <p>
880 * This instance is immutable and unaffected by this method call.
881 *
882 * @param amountToAdd the amount of the unit to add to the result, may be negative
883 * @param unit the unit of the amount to add, not null
884 * @return an {@code OffsetTime} based on this time with the specified amount added, not null
885 * @throws DateTimeException if the addition cannot be made
886 * @throws UnsupportedTemporalTypeException if the unit is not supported
887 * @throws ArithmeticException if numeric overflow occurs
888 */
889 @Override
890 public OffsetTime plus(long amountToAdd, TemporalUnit unit) {
891 if (unit instanceof ChronoUnit) {
892 return with(time.plus(amountToAdd, unit), offset);
893 }
894 return unit.addTo(this, amountToAdd);
895 }
896
897 //-----------------------------------------------------------------------
898 /**
899 * Returns a copy of this {@code OffsetTime} with the specified number of hours added.
900 * <p>
901 * This adds the specified number of hours to this time, returning a new time.
902 * The calculation wraps around midnight.
903 * <p>
904 * This instance is immutable and unaffected by this method call.
905 *
906 * @param hours the hours to add, may be negative
907 * @return an {@code OffsetTime} based on this time with the hours added, not null
908 */
909 public OffsetTime plusHours(long hours) {
910 return with(time.plusHours(hours), offset);
911 }
912
913 /**
914 * Returns a copy of this {@code OffsetTime} with the specified number of minutes added.
915 * <p>
916 * This adds the specified number of minutes to this time, returning a new time.
917 * The calculation wraps around midnight.
918 * <p>
919 * This instance is immutable and unaffected by this method call.
920 *
921 * @param minutes the minutes to add, may be negative
922 * @return an {@code OffsetTime} based on this time with the minutes added, not null
923 */
924 public OffsetTime plusMinutes(long minutes) {
925 return with(time.plusMinutes(minutes), offset);
926 }
927
928 /**
929 * Returns a copy of this {@code OffsetTime} with the specified number of seconds added.
930 * <p>
931 * This adds the specified number of seconds to this time, returning a new time.
932 * The calculation wraps around midnight.
933 * <p>
934 * This instance is immutable and unaffected by this method call.
935 *
936 * @param seconds the seconds to add, may be negative
937 * @return an {@code OffsetTime} based on this time with the seconds added, not null
938 */
939 public OffsetTime plusSeconds(long seconds) {
940 return with(time.plusSeconds(seconds), offset);
941 }
942
943 /**
944 * Returns a copy of this {@code OffsetTime} with the specified number of nanoseconds added.
945 * <p>
946 * This adds the specified number of nanoseconds to this time, returning a new time.
947 * The calculation wraps around midnight.
948 * <p>
949 * This instance is immutable and unaffected by this method call.
950 *
951 * @param nanos the nanos to add, may be negative
952 * @return an {@code OffsetTime} based on this time with the nanoseconds added, not null
953 */
954 public OffsetTime plusNanos(long nanos) {
955 return with(time.plusNanos(nanos), offset);
956 }
957
958 //-----------------------------------------------------------------------
959 /**
960 * Returns a copy of this time with the specified amount subtracted.
961 * <p>
962 * This returns an {@code OffsetTime}, based on this one, with the specified amount subtracted.
963 * The amount is typically {@link Duration} but may be any other type implementing
964 * the {@link TemporalAmount} interface.
965 * <p>
966 * The calculation is delegated to the amount object by calling
967 * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
968 * to implement the subtraction in any way it wishes, however it typically
969 * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
970 * of the amount implementation to determine if it can be successfully subtracted.
971 * <p>
972 * This instance is immutable and unaffected by this method call.
973 *
974 * @param amountToSubtract the amount to subtract, not null
975 * @return an {@code OffsetTime} based on this time with the subtraction made, not null
976 * @throws DateTimeException if the subtraction cannot be made
977 * @throws ArithmeticException if numeric overflow occurs
978 */
979 @Override
980 public OffsetTime minus(TemporalAmount amountToSubtract) {
981 return (OffsetTime) amountToSubtract.subtractFrom(this);
982 }
983
984 /**
985 * Returns a copy of this time with the specified amount subtracted.
986 * <p>
987 * This returns an {@code OffsetTime}, based on this one, with the amount
988 * in terms of the unit subtracted. If it is not possible to subtract the amount,
989 * because the unit is not supported or for some other reason, an exception is thrown.
990 * <p>
991 * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
992 * See that method for a full description of how addition, and thus subtraction, works.
993 * <p>
994 * This instance is immutable and unaffected by this method call.
995 *
996 * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
997 * @param unit the unit of the amount to subtract, not null
998 * @return an {@code OffsetTime} based on this time with the specified amount subtracted, not null
999 * @throws DateTimeException if the subtraction cannot be made
1000 * @throws UnsupportedTemporalTypeException if the unit is not supported
1001 * @throws ArithmeticException if numeric overflow occurs
1002 */
1003 @Override
1004 public OffsetTime minus(long amountToSubtract, TemporalUnit unit) {
1005 return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
1006 }
1007
1008 //-----------------------------------------------------------------------
1009 /**
1010 * Returns a copy of this {@code OffsetTime} with the specified number of hours subtracted.
1011 * <p>
1012 * This subtracts the specified number of hours from this time, returning a new time.
1013 * The calculation wraps around midnight.
1014 * <p>
1015 * This instance is immutable and unaffected by this method call.
1016 *
1017 * @param hours the hours to subtract, may be negative
1018 * @return an {@code OffsetTime} based on this time with the hours subtracted, not null
1019 */
1020 public OffsetTime minusHours(long hours) {
1021 return with(time.minusHours(hours), offset);
1022 }
1023
1024 /**
1025 * Returns a copy of this {@code OffsetTime} with the specified number of minutes subtracted.
1026 * <p>
1027 * This subtracts the specified number of minutes from this time, returning a new time.
1028 * The calculation wraps around midnight.
1029 * <p>
1030 * This instance is immutable and unaffected by this method call.
1031 *
1032 * @param minutes the minutes to subtract, may be negative
1033 * @return an {@code OffsetTime} based on this time with the minutes subtracted, not null
1034 */
1035 public OffsetTime minusMinutes(long minutes) {
1036 return with(time.minusMinutes(minutes), offset);
1037 }
1038
1039 /**
1040 * Returns a copy of this {@code OffsetTime} with the specified number of seconds subtracted.
1041 * <p>
1042 * This subtracts the specified number of seconds from this time, returning a new time.
1043 * The calculation wraps around midnight.
1044 * <p>
1045 * This instance is immutable and unaffected by this method call.
1046 *
1047 * @param seconds the seconds to subtract, may be negative
1048 * @return an {@code OffsetTime} based on this time with the seconds subtracted, not null
1049 */
1050 public OffsetTime minusSeconds(long seconds) {
1051 return with(time.minusSeconds(seconds), offset);
1052 }
1053
1054 /**
1055 * Returns a copy of this {@code OffsetTime} with the specified number of nanoseconds subtracted.
1056 * <p>
1057 * This subtracts the specified number of nanoseconds from this time, returning a new time.
1058 * The calculation wraps around midnight.
1059 * <p>
1060 * This instance is immutable and unaffected by this method call.
1061 *
1062 * @param nanos the nanos to subtract, may be negative
1063 * @return an {@code OffsetTime} based on this time with the nanoseconds subtracted, not null
1064 */
1065 public OffsetTime minusNanos(long nanos) {
1066 return with(time.minusNanos(nanos), offset);
1067 }
1068
1069 //-----------------------------------------------------------------------
1070 /**
1071 * Queries this time using the specified query.
1072 * <p>
1073 * This queries this time using the specified query strategy object.
1074 * The {@code TemporalQuery} object defines the logic to be used to
1075 * obtain the result. Read the documentation of the query to understand
1076 * what the result of this method will be.
1077 * <p>
1078 * The result of this method is obtained by invoking the
1079 * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
1080 * specified query passing {@code this} as the argument.
1081 *
1082 * @param <R> the type of the result
1083 * @param query the query to invoke, not null
1084 * @return the query result, null may be returned (defined by the query)
1085 * @throws DateTimeException if unable to query (defined by the query)
1086 * @throws ArithmeticException if numeric overflow occurs (defined by the query)
1087 */
1088 @SuppressWarnings("unchecked")
1089 @Override
1090 public <R> R query(TemporalQuery<R> query) {
1091 if (query == TemporalQueries.offset() || query == TemporalQueries.zone()) {
1092 return (R) offset;
1093 } else if (query == TemporalQueries.zoneId() | query == TemporalQueries.chronology() || query == TemporalQueries.localDate()) {
1094 return null;
1095 } else if (query == TemporalQueries.localTime()) {
1096 return (R) time;
1097 } else if (query == TemporalQueries.precision()) {
1098 return (R) NANOS;
1099 }
1100 // inline TemporalAccessor.super.query(query) as an optimization
1101 // non-JDK classes are not permitted to make this optimization
1102 return query.queryFrom(this);
1103 }
1104
1105 /**
1106 * Adjusts the specified temporal object to have the same offset and time
1107 * as this object.
1108 * <p>
1109 * This returns a temporal object of the same observable type as the input
1110 * with the offset and time changed to be the same as this.
1111 * <p>
1112 * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
1113 * twice, passing {@link ChronoField#NANO_OF_DAY} and
1114 * {@link ChronoField#OFFSET_SECONDS} as the fields.
1115 * <p>
1116 * In most cases, it is clearer to reverse the calling pattern by using
1117 * {@link Temporal#with(TemporalAdjuster)}:
1118 * <pre>
1119 * // these two lines are equivalent, but the second approach is recommended
1120 * temporal = thisOffsetTime.adjustInto(temporal);
1121 * temporal = temporal.with(thisOffsetTime);
1122 * </pre>
1123 * <p>
1124 * This instance is immutable and unaffected by this method call.
1125 *
1126 * @param temporal the target object to be adjusted, not null
1127 * @return the adjusted object, not null
1128 * @throws DateTimeException if unable to make the adjustment
1129 * @throws ArithmeticException if numeric overflow occurs
1130 */
1131 @Override
1132 public Temporal adjustInto(Temporal temporal) {
1133 return temporal
1134 .with(NANO_OF_DAY, time.toNanoOfDay())
1135 .with(OFFSET_SECONDS, offset.getTotalSeconds());
1136 }
1137
1138 /**
1139 * Calculates the amount of time until another time in terms of the specified unit.
1140 * <p>
1141 * This calculates the amount of time between two {@code OffsetTime}
1142 * objects in terms of a single {@code TemporalUnit}.
1143 * The start and end points are {@code this} and the specified time.
1144 * The result will be negative if the end is before the start.
1145 * For example, the amount in hours between two times can be calculated
1146 * using {@code startTime.until(endTime, HOURS)}.
1147 * <p>
1148 * The {@code Temporal} passed to this method is converted to a
1149 * {@code OffsetTime} using {@link #from(TemporalAccessor)}.
1150 * If the offset differs between the two times, then the specified
1151 * end time is normalized to have the same offset as this time.
1152 * <p>
1153 * The calculation returns a whole number, representing the number of
1154 * complete units between the two times.
1155 * For example, the amount in hours between 11:30Z and 13:29Z will only
1156 * be one hour as it is one minute short of two hours.
1157 * <p>
1158 * There are two equivalent ways of using this method.
1159 * The first is to invoke this method.
1160 * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1161 * <pre>
1162 * // these two lines are equivalent
1163 * amount = start.until(end, MINUTES);
1164 * amount = MINUTES.between(start, end);
1165 * </pre>
1166 * The choice should be made based on which makes the code more readable.
1167 * <p>
1168 * The calculation is implemented in this method for {@link ChronoUnit}.
1169 * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
1170 * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} are supported.
1171 * Other {@code ChronoUnit} values will throw an exception.
1172 * <p>
1173 * If the unit is not a {@code ChronoUnit}, then the result of this method
1174 * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1175 * passing {@code this} as the first argument and the converted input temporal
1176 * as the second argument.
1177 * <p>
1178 * This instance is immutable and unaffected by this method call.
1179 *
1180 * @param endExclusive the end time, exclusive, which is converted to an {@code OffsetTime}, not null
1181 * @param unit the unit to measure the amount in, not null
1182 * @return the amount of time between this time and the end time
1183 * @throws DateTimeException if the amount cannot be calculated, or the end
1184 * temporal cannot be converted to an {@code OffsetTime}
1185 * @throws UnsupportedTemporalTypeException if the unit is not supported
1186 * @throws ArithmeticException if numeric overflow occurs
1187 */
1188 @Override
1189 public long until(Temporal endExclusive, TemporalUnit unit) {
1190 OffsetTime end = OffsetTime.from(endExclusive);
1191 if (unit instanceof ChronoUnit chronoUnit) {
1192 long nanosUntil = end.toEpochNano() - toEpochNano(); // no overflow
1193 return switch (chronoUnit) {
1194 case NANOS -> nanosUntil;
1195 case MICROS -> nanosUntil / 1000;
1196 case MILLIS -> nanosUntil / 1000_000;
1197 case SECONDS -> nanosUntil / NANOS_PER_SECOND;
1198 case MINUTES -> nanosUntil / NANOS_PER_MINUTE;
1199 case HOURS -> nanosUntil / NANOS_PER_HOUR;
1200 case HALF_DAYS -> nanosUntil / (12 * NANOS_PER_HOUR);
1201 default -> throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
1202 };
1203 }
1204 return unit.between(this, end);
1205 }
1206
1207 /**
1208 * Formats this time using the specified formatter.
1209 * <p>
1210 * This time will be passed to the formatter to produce a string.
1211 *
1212 * @param formatter the formatter to use, not null
1213 * @return the formatted time string, not null
1214 * @throws DateTimeException if an error occurs during printing
1215 */
1216 public String format(DateTimeFormatter formatter) {
1217 Objects.requireNonNull(formatter, "formatter");
1218 return formatter.format(this);
1219 }
1220
1221 //-----------------------------------------------------------------------
1222 /**
1223 * Combines this time with a date to create an {@code OffsetDateTime}.
1224 * <p>
1225 * This returns an {@code OffsetDateTime} formed from this time and the specified date.
1226 * All possible combinations of date and time are valid.
1227 *
1228 * @param date the date to combine with, not null
1229 * @return the offset date-time formed from this time and the specified date, not null
1230 */
1231 public OffsetDateTime atDate(LocalDate date) {
1232 return OffsetDateTime.of(date, time, offset);
1233 }
1234
1235 //-----------------------------------------------------------------------
1236 /**
1237 * Converts this time to epoch nanos based on 1970-01-01Z.
1238 *
1239 * @return the epoch nanos value
1240 */
1241 private long toEpochNano() {
1242 long nod = time.toNanoOfDay();
1243 long offsetNanos = offset.getTotalSeconds() * NANOS_PER_SECOND;
1244 return nod - offsetNanos;
1245 }
1246
1247 /**
1248 * Converts this {@code OffsetTime} to the number of seconds since the epoch
1249 * of 1970-01-01T00:00:00Z.
1250 * <p>
1251 * This combines this offset time with the specified date to calculate the
1252 * epoch-second value, which is the number of elapsed seconds from
1253 * 1970-01-01T00:00:00Z.
1254 * Instants on the time-line after the epoch are positive, earlier
1255 * are negative.
1256 *
1257 * @param date the localdate, not null
1258 * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
1259 * @since 9
1260 */
1261 public long toEpochSecond(LocalDate date) {
1262 Objects.requireNonNull(date, "date");
1263 long epochDay = date.toEpochDay();
1264 long secs = epochDay * 86400 + time.toSecondOfDay();
1265 secs -= offset.getTotalSeconds();
1266 return secs;
1267 }
1268
1269 //-----------------------------------------------------------------------
1270 /**
1271 * Compares this {@code OffsetTime} to another time.
1272 * <p>
1273 * The comparison is based first on the UTC equivalent instant, then on the local time.
1274 * It is "consistent with equals", as defined by {@link Comparable}.
1275 * <p>
1276 * For example, the following is the comparator order:
1277 * <ol>
1278 * <li>{@code 10:30+01:00}</li>
1279 * <li>{@code 11:00+01:00}</li>
1280 * <li>{@code 12:00+02:00}</li>
1281 * <li>{@code 11:30+01:00}</li>
1282 * <li>{@code 12:00+01:00}</li>
1283 * <li>{@code 12:30+01:00}</li>
1284 * </ol>
1285 * Values #2 and #3 represent the same instant on the time-line.
1286 * When two values represent the same instant, the local time is compared
1287 * to distinguish them. This step is needed to make the ordering
1288 * consistent with {@code equals()}.
1289 * <p>
1290 * To compare the underlying local time of two {@code TemporalAccessor} instances,
1291 * use {@link ChronoField#NANO_OF_DAY} as a comparator.
1292 *
1293 * @param other the other time to compare to, not null
1294 * @return the comparator value, that is the comparison of the UTC equivalent {@code other} instant,
1295 * if they are not equal, and if the UTC equivalent {@code other} instant is equal,
1296 * the comparison of this local time with {@code other} local time
1297 * @see #isBefore
1298 * @see #isAfter
1299 */
1300 @Override
1301 public int compareTo(OffsetTime other) {
1302 if (offset.equals(other.offset)) {
1303 return time.compareTo(other.time);
1304 }
1305 int compare = Long.compare(toEpochNano(), other.toEpochNano());
1306 if (compare == 0) {
1307 compare = time.compareTo(other.time);
1308 }
1309 return compare;
1310 }
1311
1312 //-----------------------------------------------------------------------
1313 /**
1314 * Checks if the instant of this {@code OffsetTime} is after that of the
1315 * specified time applying both times to a common date.
1316 * <p>
1317 * This method differs from the comparison in {@link #compareTo} in that it
1318 * only compares the instant of the time. This is equivalent to converting both
1319 * times to an instant using the same date and comparing the instants.
1320 *
1321 * @param other the other time to compare to, not null
1322 * @return true if this is after the instant of the specified time
1323 */
1324 public boolean isAfter(OffsetTime other) {
1325 return toEpochNano() > other.toEpochNano();
1326 }
1327
1328 /**
1329 * Checks if the instant of this {@code OffsetTime} is before that of the
1330 * specified time applying both times to a common date.
1331 * <p>
1332 * This method differs from the comparison in {@link #compareTo} in that it
1333 * only compares the instant of the time. This is equivalent to converting both
1334 * times to an instant using the same date and comparing the instants.
1335 *
1336 * @param other the other time to compare to, not null
1337 * @return true if this is before the instant of the specified time
1338 */
1339 public boolean isBefore(OffsetTime other) {
1340 return toEpochNano() < other.toEpochNano();
1341 }
1342
1343 /**
1344 * Checks if the instant of this {@code OffsetTime} is equal to that of the
1345 * specified time applying both times to a common date.
1346 * <p>
1347 * This method differs from the comparison in {@link #compareTo} and {@link #equals}
1348 * in that it only compares the instant of the time. This is equivalent to converting both
1349 * times to an instant using the same date and comparing the instants.
1350 *
1351 * @param other the other time to compare to, not null
1352 * @return true if this is equal to the instant of the specified time
1353 */
1354 public boolean isEqual(OffsetTime other) {
1355 return toEpochNano() == other.toEpochNano();
1356 }
1357
1358 //-----------------------------------------------------------------------
1359 /**
1360 * Checks if this time is equal to another time.
1361 * <p>
1362 * The comparison is based on the local-time and the offset.
1363 * To compare for the same instant on the time-line, use {@link #isEqual(OffsetTime)}.
1364 * <p>
1365 * Only objects of type {@code OffsetTime} are compared, other types return false.
1366 * To compare the underlying local time of two {@code TemporalAccessor} instances,
1367 * use {@link ChronoField#NANO_OF_DAY} as a comparator.
1368 *
1369 * @param obj the object to check, null returns false
1370 * @return true if this is equal to the other time
1371 */
1372 @Override
1373 public boolean equals(Object obj) {
1374 if (this == obj) {
1375 return true;
1376 }
1377 return (obj instanceof OffsetTime other)
1378 && time.equals(other.time)
1379 && offset.equals(other.offset);
1380 }
1381
1382 /**
1383 * A hash code for this time.
1384 *
1385 * @return a suitable hash code
1386 */
1387 @Override
1388 public int hashCode() {
1389 return time.hashCode() ^ offset.hashCode();
1390 }
1391
1392 //-----------------------------------------------------------------------
1393 /**
1394 * Outputs this time as a {@code String}, such as {@code 10:15:30+01:00}.
1395 * <p>
1396 * The output will be one of the following ISO-8601 formats:
1397 * <ul>
1398 * <li>{@code HH:mmXXXXX}</li>
1399 * <li>{@code HH:mm:ssXXXXX}</li>
1400 * <li>{@code HH:mm:ss.SSSXXXXX}</li>
1401 * <li>{@code HH:mm:ss.SSSSSSXXXXX}</li>
1402 * <li>{@code HH:mm:ss.SSSSSSSSSXXXXX}</li>
1403 * </ul>
1404 * The format used will be the shortest that outputs the full value of
1405 * the time where the omitted parts are implied to be zero.
1406 *
1407 * @return a string representation of this time, not null
1408 */
1409 @Override
1410 public String toString() {
1411 var offsetStr = offset.toString();
1412 var buf = new StringBuilder(18 + offsetStr.length());
1413 DateTimeHelper.formatTo(buf, time);
1414 return buf.append(offsetStr).toString();
1415 }
1416
1417 //-----------------------------------------------------------------------
1418 /**
1419 * Writes the object using a
1420 * <a href="{@docRoot}/serialized-form.html#java.time.Ser">dedicated serialized form</a>.
1421 * @serialData
1422 * <pre>
1423 * out.writeByte(9); // identifies an OffsetTime
1424 * // the <a href="{@docRoot}/serialized-form.html#java.time.LocalTime">time</a> excluding the one byte header
1425 * // the <a href="{@docRoot}/serialized-form.html#java.time.ZoneOffset">offset</a> excluding the one byte header
1426 * </pre>
1427 *
1428 * @return the instance of {@code Ser}, not null
1429 */
1430 @java.io.Serial
1431 private Object writeReplace() {
1432 return new Ser(Ser.OFFSET_TIME_TYPE, this);
1433 }
1434
1435 /**
1436 * Defend against malicious streams.
1437 *
1438 * @param s the stream to read
1439 * @throws InvalidObjectException always
1440 */
1441 @java.io.Serial
1442 private void readObject(ObjectInputStream s) throws InvalidObjectException {
1443 throw new InvalidObjectException("Deserialization via serialization delegate");
1444 }
1445
1446 void writeExternal(ObjectOutput out) throws IOException {
1447 time.writeExternal(out);
1448 offset.writeExternal(out);
1449 }
1450
1451 static OffsetTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
1452 LocalTime time = LocalTime.readExternal(in);
1453 ZoneOffset offset = ZoneOffset.readExternal(in);
1454 return OffsetTime.of(time, offset);
1455 }
1456
1457 }