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