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.LocalTime;
13 
14 import hunt.time.temporal.ChronoField;
15 import hunt.time.temporal.ChronoUnit;
16 
17 import hunt.stream.DataInput;
18 import hunt.stream.DataOutput;
19 import hunt.Exceptions;
20 import hunt.stream.Common;
21 // import hunt.time.format.DateTimeFormatter;
22 import hunt.time.format.DateTimeParseException;
23 import hunt.time.temporal.ChronoField;
24 import hunt.time.temporal.ChronoUnit;
25 import hunt.time.temporal.Temporal;
26 import hunt.time.temporal.TemporalAccessor;
27 import hunt.time.temporal.TemporalAdjuster;
28 import hunt.time.temporal.TemporalAmount;
29 import hunt.time.temporal.TemporalField;
30 import hunt.time.temporal.TemporalQueries;
31 import hunt.time.temporal.TemporalQuery;
32 import hunt.time.temporal.TemporalUnit;
33 import hunt.time.Exceptions;
34 import hunt.time.temporal.ValueRange;
35 import hunt.Functions;
36 import hunt.Long;
37 import hunt.math.Helper;
38 import hunt.time.ZoneId;
39 import hunt.time.Clock;
40 import hunt.time.Instant;
41 import hunt.time.LocalDateTime;
42 import hunt.time.LocalDate;
43 import hunt.time.OffsetTime;
44 import hunt.time.ZoneOffset;
45 import hunt.time.Exceptions;
46 import hunt.util.StringBuilder;
47 import hunt.time.Ser;
48 import hunt.time.Duration;
49 import std.conv;
50 import hunt.text.Common;
51 import hunt.time.util.QueryHelper;
52 import hunt.time.util.Common;
53 import hunt.util.Common;
54 import hunt.util.Comparator;
55 // import hunt.serialization.JsonSerializer;
56 
57 import std.concurrency : initOnce;
58 
59 /**
60  * A time without a time-zone _in the ISO-8601 calendar system,
61  * such as {@code 10:15:30}.
62  * !(p)
63  * {@code LocalTime} is an immutable date-time object that represents a time,
64  * often viewed as hour-minute-second.
65  * Time is represented to nanosecond precision.
66  * For example, the value "13:45.30.123456789" can be stored _in a {@code LocalTime}.
67  * !(p)
68  * This class does not store or represent a date or time-zone.
69  * Instead, it is a description of the local time as seen on a wall clock.
70  * It cannot represent an instant on the time-line without additional information
71  * such as an offset or time-zone.
72  * !(p)
73  * The ISO-8601 calendar system is the modern civil calendar system used today
74  * _in most of the world. This API assumes that all calendar systems use the same
75  * representation, this class, for time-of-day.
76  *
77  * !(p)
78  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
79  * class; use of identity-sensitive operations (including reference equality
80  * ({@code ==}), identity hash code, or synchronization) on instances of
81  * {@code LocalTime} may have unpredictable results and should be avoided.
82  * The {@code equals} method should be used for comparisons.
83  *
84  * @implSpec
85  * This class is immutable and thread-safe.
86  *
87  * @since 1.8
88  */
89 public final class LocalTime
90         : Temporal, TemporalAdjuster, Comparable!(LocalTime) { // , Serializable
91 
92     /**
93      * The minimum supported {@code LocalTime}, '00:00'.
94      * This is the time of midnight at the start of the day.
95      */
96     static LocalTime MIN() {
97         return HOURS()[0];
98     }
99 
100     /**
101      * The maximum supported {@code LocalTime}, '23:59:59.999999999'.
102      * This is the time just before midnight at the end of the day.
103      */
104     static LocalTime MAX() {
105         __gshared LocalTime _MAX;
106         return initOnce!(_MAX)(new LocalTime(23, 59, 59, 999_999_999));
107     }
108 
109     /**
110      * The time of midnight at the start of the day, '00:00'.
111      */
112     static LocalTime MIDNIGHT() {
113         return HOURS()[0];
114     }
115 
116     /**
117      * The time of noon _in the middle of the day, '12:00'.
118      */
119     static LocalTime NOON() {
120         return HOURS()[12];
121     }
122     /**
123      * Constants for the local time of each hour.
124      */
125     static LocalTime[] HOURS() {
126         __gshared LocalTime[] _HOURS;
127         return initOnce!(_HOURS)({
128             LocalTime[] hs = new LocalTime[24];
129             for(int i = 0; i < hs.length; i++) {
130                 hs[i] = new LocalTime(i, 0, 0, 0);
131             }
132             return hs;
133         }());
134     }
135 
136     /**
137      * Hours per day.
138      */
139     enum int HOURS_PER_DAY = 24;
140     /**
141      * Minutes per hour.
142      */
143     enum int MINUTES_PER_HOUR = 60;
144     /**
145      * Minutes per day.
146      */
147     enum int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
148     /**
149      * Seconds per minute.
150      */
151     enum int SECONDS_PER_MINUTE = 60;
152     /**
153      * Seconds per hour.
154      */
155     enum int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
156     /**
157      * Seconds per day.
158      */
159     enum int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
160     /**
161      * Milliseconds per day.
162      */
163     enum long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
164     /**
165      * Microseconds per day.
166      */
167     enum long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
168     /**
169      * Nanos per millisecond.
170      */
171     enum long NANOS_PER_MILLI = 1000_000L;
172     /**
173      * Nanos per second.
174      */
175     enum long NANOS_PER_SECOND =  1000_000_000L;
176     /**
177      * Nanos per minute.
178      */
179     enum long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
180     /**
181      * Nanos per hour.
182      */
183     enum long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
184     /**
185      * Nanos per day.
186      */
187     enum long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;
188 
189     /**
190      * The hour.
191      */
192     private  byte hour;
193     /**
194      * The minute.
195      */
196     private  byte minute;
197     /**
198      * The second.
199      */
200     private  byte second;
201     /**
202      * The nanosecond.
203      */
204     private  int nano;
205 
206     //-----------------------------------------------------------------------
207     /**
208      * Obtains the current time from the system clock _in the default time-zone.
209      * !(p)
210      * This will query the {@link Clock#systemDefaultZone() system clock} _in the default
211      * time-zone to obtain the current time.
212      * !(p)
213      * Using this method will prevent the ability to use an alternate clock for testing
214      * because the clock is hard-coded.
215      *
216      * @return the current time using the system clock and default time-zone, not null
217      */
218     public static LocalTime now() {
219         return now(Clock.systemDefaultZone());
220     }
221 
222     /**
223      * Obtains the current time from the system clock _in the specified time-zone.
224      * !(p)
225      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current time.
226      * Specifying the time-zone avoids dependence on the default time-zone.
227      * !(p)
228      * Using this method will prevent the ability to use an alternate clock for testing
229      * because the clock is hard-coded.
230      *
231      * @param zone  the zone ID to use, not null
232      * @return the current time using the system clock, not null
233      */
234     public static LocalTime now(ZoneId zone) {
235         return now(Clock.system(zone));
236     }
237 
238     /**
239      * Obtains the current time from the specified clock.
240      * !(p)
241      * This will query the specified clock to obtain the current time.
242      * Using this method allows the use of an alternate clock for testing.
243      * The alternate clock may be introduced using {@link Clock dependency injection}.
244      *
245      * @param clock  the clock to use, not null
246      * @return the current time, not null
247      */
248     public static LocalTime now(Clock clock) {
249         assert(clock, "clock");
250         Instant now = clock.instant();  // called once
251         return ofInstant(now, clock.getZone());
252     }
253 
254     //-----------------------------------------------------------------------
255     /**
256      * Obtains an instance of {@code LocalTime} from an hour and minute.
257      * !(p)
258      * This returns a {@code LocalTime} with the specified hour and minute.
259      * The second and nanosecond fields will be set to zero.
260      *
261      * @param hour  the hour-of-day to represent, from 0 to 23
262      * @param minute  the minute-of-hour to represent, from 0 to 59
263      * @return the local time, not null
264      * @throws DateTimeException if the value of any field is _out of range
265      */
266     public static LocalTime of(int hour, int minute) {
267         ChronoField.HOUR_OF_DAY.checkValidValue(hour);
268         if (minute == 0) {
269             return HOURS[hour];  // for performance
270         }
271         ChronoField.MINUTE_OF_HOUR.checkValidValue(minute);
272         return new LocalTime(hour, minute, 0, 0);
273     }
274 
275     /**
276      * Obtains an instance of {@code LocalTime} from an hour, minute and second.
277      * !(p)
278      * This returns a {@code LocalTime} with the specified hour, minute and second.
279      * The nanosecond field will be set to zero.
280      *
281      * @param hour  the hour-of-day to represent, from 0 to 23
282      * @param minute  the minute-of-hour to represent, from 0 to 59
283      * @param second  the second-of-minute to represent, from 0 to 59
284      * @return the local time, not null
285      * @throws DateTimeException if the value of any field is _out of range
286      */
287     public static LocalTime of(int hour, int minute, int second) {
288         ChronoField.HOUR_OF_DAY.checkValidValue(hour);
289         if ((minute | second) == 0) {
290             return HOURS[hour];  // for performance
291         }
292         ChronoField.MINUTE_OF_HOUR.checkValidValue(minute);
293         ChronoField.SECOND_OF_MINUTE.checkValidValue(second);
294         return new LocalTime(hour, minute, second, 0);
295     }
296 
297     /**
298      * Obtains an instance of {@code LocalTime} from an hour, minute, second and nanosecond.
299      * !(p)
300      * This returns a {@code LocalTime} with the specified hour, minute, second and nanosecond.
301      *
302      * @param hour  the hour-of-day to represent, from 0 to 23
303      * @param minute  the minute-of-hour to represent, from 0 to 59
304      * @param second  the second-of-minute to represent, from 0 to 59
305      * @param nanoOfSecond  the nano-of-second to represent, from 0 to 999,999,999
306      * @return the local time, not null
307      * @throws DateTimeException if the value of any field is _out of range
308      */
309     public static LocalTime of(int hour, int minute, int second, int nanoOfSecond) {
310         ChronoField.HOUR_OF_DAY.checkValidValue(hour);
311         ChronoField.MINUTE_OF_HOUR.checkValidValue(minute);
312         ChronoField.SECOND_OF_MINUTE.checkValidValue(second);
313         ChronoField.NANO_OF_SECOND.checkValidValue(nanoOfSecond);
314         return create(hour, minute, second, nanoOfSecond);
315     }
316 
317     /**
318      * Obtains an instance of {@code LocalTime} from an {@code Instant} and zone ID.
319      * !(p)
320      * This creates a local time based on the specified instant.
321      * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
322      * which is simple as there is only one valid offset for each instant.
323      * Then, the instant and offset are used to calculate the local time.
324      *
325      * @param instant  the instant to create the time from, not null
326      * @param zone  the time-zone, which may be an offset, not null
327      * @return the local time, not null
328      * @since 9
329      */
330     public static LocalTime ofInstant(Instant instant, ZoneId zone) {
331         assert(instant, "instant");
332         assert(zone, "zone");
333         ZoneOffset offset = zone.getRules().getOffset(instant);
334         long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
335         int secsOfDay = MathHelper.floorMod(localSecond, SECONDS_PER_DAY);
336         import hunt.logging;
337         import std.string;
338         version(HUNT_DEBUG) trace("--- > (%s , %s , %s)".format(localSecond,secsOfDay,offset.getId()));
339         return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
340     }
341 
342     //-----------------------------------------------------------------------
343     /**
344      * Obtains an instance of {@code LocalTime} from a second-of-day value.
345      * !(p)
346      * This returns a {@code LocalTime} with the specified second-of-day.
347      * The nanosecond field will be set to zero.
348      *
349      * @param secondOfDay  the second-of-day, from {@code 0} to {@code 24 * 60 * 60 - 1}
350      * @return the local time, not null
351      * @throws DateTimeException if the second-of-day value is invalid
352      */
353     public static LocalTime ofSecondOfDay(long secondOfDay) {
354         ChronoField.SECOND_OF_DAY.checkValidValue(secondOfDay);
355         int hours = cast(int) (secondOfDay / LocalTime.SECONDS_PER_HOUR);
356         secondOfDay -= hours * LocalTime.SECONDS_PER_HOUR;
357         int minutes = cast(int) (secondOfDay / LocalTime.SECONDS_PER_MINUTE);
358         secondOfDay -= minutes * LocalTime.SECONDS_PER_MINUTE;
359         return create(hours, minutes, cast(int) secondOfDay, 0);
360     }
361 
362     /**
363      * Obtains an instance of {@code LocalTime} from a nanos-of-day value.
364      * !(p)
365      * This returns a {@code LocalTime} with the specified nanosecond-of-day.
366      *
367      * @param nanoOfDay  the nano of day, from {@code 0} to {@code 24 * 60 * 60 * 1,000,000,000 - 1}
368      * @return the local time, not null
369      * @throws DateTimeException if the nanos of day value is invalid
370      */
371     public static LocalTime ofNanoOfDay(long nanoOfDay) {
372         ChronoField.NANO_OF_DAY.checkValidValue(nanoOfDay);
373         int hours = cast(int) (nanoOfDay / LocalTime.NANOS_PER_HOUR);
374         nanoOfDay -= hours * LocalTime.NANOS_PER_HOUR;
375         int minutes = cast(int) (nanoOfDay / LocalTime.NANOS_PER_MINUTE);
376         nanoOfDay -= minutes * LocalTime.NANOS_PER_MINUTE;
377         int seconds = cast(int) (nanoOfDay / LocalTime.NANOS_PER_SECOND);
378         nanoOfDay -= seconds * LocalTime.NANOS_PER_SECOND;
379         import hunt.logging;
380         import std.string;
381         // version(HUNT_DEBUG) logDebug("--- > (%s ,%s , %s , %s , %s)".format(nanoOfDay,hours,minutes,seconds,nanoOfDay));
382         return create(hours, minutes, seconds, cast(int) nanoOfDay);
383     }
384 
385     //-----------------------------------------------------------------------
386     /**
387      * Obtains an instance of {@code LocalTime} from a temporal object.
388      * !(p)
389      * This obtains a local time based on the specified temporal.
390      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
391      * which this factory converts to an instance of {@code LocalTime}.
392      * !(p)
393      * The conversion uses the {@link TemporalQueries#localTime()} query, which relies
394      * on extracting the {@link ChronoField#NANO_OF_DAY NANO_OF_DAY} field.
395      * !(p)
396      * This method matches the signature of the functional interface {@link TemporalQuery}
397      * allowing it to be used as a query via method reference, {@code LocalTime.from}.
398      *
399      * @param temporal  the temporal object to convert, not null
400      * @return the local time, not null
401      * @throws DateTimeException if unable to convert to a {@code LocalTime}
402      */
403     public static LocalTime from(TemporalAccessor temporal) {
404         assert(temporal, "temporal");
405         LocalTime time = queryFrom(temporal); // QueryHelper.query!LocalTime(temporal , TemporalQueries.localTime());
406         if (time is null) {
407             throw new DateTimeException("Unable to obtain LocalTime from TemporalAccessor: " ~
408                     typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof);
409         }
410         return time;
411     }
412 
413     private static LocalTime queryFrom(TemporalAccessor temporal) {
414         if (temporal.isSupported(ChronoField.NANO_OF_DAY)) {
415                 return LocalTime.ofNanoOfDay(temporal.getLong(ChronoField.NANO_OF_DAY));
416         }
417         return null;
418     }
419 
420     //-----------------------------------------------------------------------
421     /**
422      * Obtains an instance of {@code LocalTime} from a text string such as {@code 10:15}.
423      * !(p)
424      * The string must represent a valid time and is parsed using
425      * {@link hunt.time.format.DateTimeFormatter#ISO_LOCAL_TIME}.
426      *
427      * @param text  the text to parse such as "10:15:30", not null
428      * @return the parsed local time, not null
429      * @throws DateTimeParseException if the text cannot be parsed
430      */
431     // public static LocalTime parse(string text) {
432     //     return parse(text, DateTimeFormatter.ISO_LOCAL_TIME);
433     // }
434 
435     // /**
436     //  * Obtains an instance of {@code LocalTime} from a text string using a specific formatter.
437     //  * !(p)
438     //  * The text is parsed using the formatter, returning a time.
439     //  *
440     //  * @param text  the text to parse, not null
441     //  * @param formatter  the formatter to use, not null
442     //  * @return the parsed local time, not null
443     //  * @throws DateTimeParseException if the text cannot be parsed
444     //  */
445     // public static LocalTime parse(string text, DateTimeFormatter formatter) {
446     //     assert(formatter, "formatter");
447     //     return formatter.parse(text, new class TemporalQuery!LocalTime{
448     //         LocalTime queryFrom(TemporalAccessor temporal)
449     //         {
450     //             assert(temporal, "temporal");
451     //             LocalTime time =QueryHelper.query!LocalTime(temporal , TemporalQueries.localTime());
452     //             if (time is null) {
453     //                 throw new DateTimeException("Unable to obtain LocalTime from TemporalAccessor: " ~
454     //                         typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof);
455     //             }
456     //             return time;
457     //         }
458     //     });
459     // }
460 
461     //-----------------------------------------------------------------------
462     /**
463      * Creates a local time from the hour, minute, second and nanosecond fields.
464      * !(p)
465      * This factory may return a cached value, but applications must not rely on this.
466      *
467      * @param hour  the hour-of-day to represent, validated from 0 to 23
468      * @param minute  the minute-of-hour to represent, validated from 0 to 59
469      * @param second  the second-of-minute to represent, validated from 0 to 59
470      * @param nanoOfSecond  the nano-of-second to represent, validated from 0 to 999,999,999
471      * @return the local time, not null
472      */
473     private static LocalTime create(int hour, int minute, int second, int nanoOfSecond) {
474         if ((minute | second | nanoOfSecond) == 0) {
475             return HOURS[hour];
476         }
477         return new LocalTime(hour, minute, second, nanoOfSecond);
478     }
479 
480     /**
481      * Constructor, previously validated.
482      *
483      * @param hour  the hour-of-day to represent, validated from 0 to 23
484      * @param minute  the minute-of-hour to represent, validated from 0 to 59
485      * @param second  the second-of-minute to represent, validated from 0 to 59
486      * @param nanoOfSecond  the nano-of-second to represent, validated from 0 to 999,999,999
487      */
488     this(int hour, int minute, int second, int nanoOfSecond) {
489         this.hour = cast(byte) hour;
490         this.minute = cast(byte) minute;
491         this.second = cast(byte) second;
492         this.nano = nanoOfSecond;
493     }
494 
495     //-----------------------------------------------------------------------
496     /**
497      * Checks if the specified field is supported.
498      * !(p)
499      * This checks if this time can be queried for the specified field.
500      * If false, then calling the {@link #range(TemporalField) range},
501      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
502      * methods will throw an exception.
503      * !(p)
504      * If the field is a {@link ChronoField} then the query is implemented here.
505      * The supported fields are:
506      * !(ul)
507      * !(li){@code NANO_OF_SECOND}
508      * !(li){@code NANO_OF_DAY}
509      * !(li){@code MICRO_OF_SECOND}
510      * !(li){@code MICRO_OF_DAY}
511      * !(li){@code MILLI_OF_SECOND}
512      * !(li){@code MILLI_OF_DAY}
513      * !(li){@code SECOND_OF_MINUTE}
514      * !(li){@code SECOND_OF_DAY}
515      * !(li){@code MINUTE_OF_HOUR}
516      * !(li){@code MINUTE_OF_DAY}
517      * !(li){@code HOUR_OF_AMPM}
518      * !(li){@code CLOCK_HOUR_OF_AMPM}
519      * !(li){@code HOUR_OF_DAY}
520      * !(li){@code CLOCK_HOUR_OF_DAY}
521      * !(li){@code AMPM_OF_DAY}
522      * </ul>
523      * All other {@code ChronoField} instances will return false.
524      * !(p)
525      * If the field is not a {@code ChronoField}, then the result of this method
526      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
527      * passing {@code this} as the argument.
528      * Whether the field is supported is determined by the field.
529      *
530      * @param field  the field to check, null returns false
531      * @return true if the field is supported on this time, false if not
532      */
533     override
534     public bool isSupported(TemporalField field) {
535         if (cast(ChronoField)(field) !is null) {
536             return field.isTimeBased();
537         }
538         return field !is null && field.isSupportedBy(this);
539     }
540 
541     /**
542      * Checks if the specified unit is supported.
543      * !(p)
544      * This checks if the specified unit can be added to, or subtracted from, this time.
545      * If false, then calling the {@link #plus(long, TemporalUnit)} and
546      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
547      * !(p)
548      * If the unit is a {@link ChronoUnit} then the query is implemented here.
549      * The supported units are:
550      * !(ul)
551      * !(li){@code NANOS}
552      * !(li){@code MICROS}
553      * !(li){@code MILLIS}
554      * !(li){@code SECONDS}
555      * !(li){@code MINUTES}
556      * !(li){@code HOURS}
557      * !(li){@code HALF_DAYS}
558      * </ul>
559      * All other {@code ChronoUnit} instances will return false.
560      * !(p)
561      * If the unit is not a {@code ChronoUnit}, then the result of this method
562      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
563      * passing {@code this} as the argument.
564      * Whether the unit is supported is determined by the unit.
565      *
566      * @param unit  the unit to check, null returns false
567      * @return true if the unit can be added/subtracted, false if not
568      */
569     override  // override for Javadoc
570     public bool isSupported(TemporalUnit unit) {
571         if (cast(ChronoUnit)(unit) !is null) {
572             return unit.isTimeBased();
573         }
574         return unit !is null && unit.isSupportedBy(this);
575     }
576 
577     //-----------------------------------------------------------------------
578     /**
579      * Gets the range of valid values for the specified field.
580      * !(p)
581      * The range object expresses the minimum and maximum valid values for a field.
582      * This time is used to enhance the accuracy of the returned range.
583      * If it is not possible to return the range, because the field is not supported
584      * or for some other reason, an exception is thrown.
585      * !(p)
586      * If the field is a {@link ChronoField} then the query is implemented here.
587      * The {@link #isSupported(TemporalField) supported fields} will return
588      * appropriate range instances.
589      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
590      * !(p)
591      * If the field is not a {@code ChronoField}, then the result of this method
592      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
593      * passing {@code this} as the argument.
594      * Whether the range can be obtained is determined by the field.
595      *
596      * @param field  the field to query the range for, not null
597      * @return the range of valid values for the field, not null
598      * @throws DateTimeException if the range for the field cannot be obtained
599      * @throws UnsupportedTemporalTypeException if the field is not supported
600      */
601     override  // override for Javadoc
602     public ValueRange range(TemporalField field) {
603         return /* Temporal. super.*/super_range(field);
604     }
605 
606       ValueRange super_range(TemporalField field) {
607         if (cast(ChronoField)(field) !is null) {
608             if (isSupported(field)) {
609                 return field.range();
610             }
611             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ typeid(field).name);
612         }
613         assert(field, "field");
614         return field.rangeRefinedBy(this);
615     }
616 
617     /**
618      * Gets the value of the specified field from this time as an {@code int}.
619      * !(p)
620      * This queries this time for the value of the specified field.
621      * The returned value will always be within the valid range of values for the field.
622      * If it is not possible to return the value, because the field is not supported
623      * or for some other reason, an exception is thrown.
624      * !(p)
625      * If the field is a {@link ChronoField} then the query is implemented here.
626      * The {@link #isSupported(TemporalField) supported fields} will return valid
627      * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
628      * which are too large to fit _in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
629      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
630      * !(p)
631      * If the field is not a {@code ChronoField}, then the result of this method
632      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
633      * passing {@code this} as the argument. Whether the value can be obtained,
634      * and what the value represents, is determined by the field.
635      *
636      * @param field  the field to get, not null
637      * @return the value for the field
638      * @throws DateTimeException if a value for the field cannot be obtained or
639      *         the value is outside the range of valid values for the field
640      * @throws UnsupportedTemporalTypeException if the field is not supported or
641      *         the range of values exceeds an {@code int}
642      * @throws ArithmeticException if numeric overflow occurs
643      */
644     override  // override for Javadoc and performance
645     public int get(TemporalField field) {
646         if (cast(ChronoField)(field) !is null) {
647             return get0(field);
648         }
649         return /* Temporal. super.*/super_get(field);
650     }
651      int super_get(TemporalField field) {
652         ValueRange range = range(field);
653         if (range.isIntValue() == false) {
654             throw new UnsupportedTemporalTypeException("Invalid field " ~ typeid(field).name ~ " for get() method, use getLong() instead");
655         }
656         long value = getLong(field);
657         if (range.isValidValue(value) == false) {
658             throw new DateTimeException("Invalid value for " ~ typeid(field).name ~ " (valid values " ~ range.toString ~ "): " ~ value.to!string);
659         }
660         return cast(int) value;
661     }
662     /**
663      * Gets the value of the specified field from this time as a {@code long}.
664      * !(p)
665      * This queries this time for the value of the specified field.
666      * If it is not possible to return the value, because the field is not supported
667      * or for some other reason, an exception is thrown.
668      * !(p)
669      * If the field is a {@link ChronoField} then the query is implemented here.
670      * The {@link #isSupported(TemporalField) supported fields} will return valid
671      * values based on this time.
672      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
673      * !(p)
674      * If the field is not a {@code ChronoField}, then the result of this method
675      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
676      * passing {@code this} as the argument. Whether the value can be obtained,
677      * and what the value represents, is determined by the field.
678      *
679      * @param field  the field to get, not null
680      * @return the value for the field
681      * @throws DateTimeException if a value for the field cannot be obtained
682      * @throws UnsupportedTemporalTypeException if the field is not supported
683      * @throws ArithmeticException if numeric overflow occurs
684      */
685     override
686     public long getLong(TemporalField field) {
687         if (cast(ChronoField)(field) !is null) {
688             if (field == ChronoField.NANO_OF_DAY) {
689                 return toNanoOfDay();
690             }
691             if (field == ChronoField.MICRO_OF_DAY) {
692                 return toNanoOfDay() / 1000;
693             }
694             return get0(field);
695         }
696         return field.getFrom(this);
697     }
698 
699     private int get0(TemporalField field) {
700         auto f = cast(ChronoField) field;
701         {
702             if(f == ChronoField.NANO_OF_SECOND) return nano;
703             if(f == ChronoField.NANO_OF_DAY) throw new UnsupportedTemporalTypeException("Invalid field 'NanoOfDay' for get() method, use getLong() instead");
704             if(f == ChronoField.MICRO_OF_SECOND) return nano / 1000;
705             if(f == ChronoField.MICRO_OF_DAY) throw new UnsupportedTemporalTypeException("Invalid field 'MicroOfDay' for get() method, use getLong() instead");
706             if(f == ChronoField.MILLI_OF_SECOND) return nano / 1000_000;
707             if(f == ChronoField.MILLI_OF_DAY) return cast(int) (toNanoOfDay() / 1000_000);
708             if(f == ChronoField.SECOND_OF_MINUTE) return second;
709             if(f == ChronoField.SECOND_OF_DAY) return toSecondOfDay();
710             if(f == ChronoField.MINUTE_OF_HOUR) return minute;
711             if(f == ChronoField.MINUTE_OF_DAY) return hour * 60 + minute;
712             if(f == ChronoField.HOUR_OF_AMPM) return hour % 12;
713             if(f == ChronoField.CLOCK_HOUR_OF_AMPM) {int ham = hour % 12; return (ham % 12 == 0 ? 12 : ham);}
714             if(f == ChronoField.HOUR_OF_DAY) return hour;
715             if(f == ChronoField.CLOCK_HOUR_OF_DAY) return (hour == 0 ? 24 : hour);
716             if(f == ChronoField.AMPM_OF_DAY) return hour / 12;
717         }
718         throw new UnsupportedTemporalTypeException("Unsupported field: " ~ typeid(field).name);
719     }
720 
721     //-----------------------------------------------------------------------
722     /**
723      * Gets the hour-of-day field.
724      *
725      * @return the hour-of-day, from 0 to 23
726      */
727     public int getHour() {
728         return hour;
729     }
730 
731     /**
732      * Gets the minute-of-hour field.
733      *
734      * @return the minute-of-hour, from 0 to 59
735      */
736     public int getMinute() {
737         return minute;
738     }
739 
740     /**
741      * Gets the second-of-minute field.
742      *
743      * @return the second-of-minute, from 0 to 59
744      */
745     public int getSecond() {
746         return second;
747     }
748 
749     int getMillisecond() {
750         return cast(int)nano/NANOS_PER_MILLI;
751     }
752 
753     /**
754      * Gets the nano-of-second field.
755      *
756      * @return the nano-of-second, from 0 to 999,999,999
757      */
758     public int getNano() {
759         return nano;
760     }
761 
762     //-----------------------------------------------------------------------
763     /**
764      * Returns an adjusted copy of this time.
765      * !(p)
766      * This returns a {@code LocalTime}, based on this one, with the time adjusted.
767      * The adjustment takes place using the specified adjuster strategy object.
768      * Read the documentation of the adjuster to understand what adjustment will be made.
769      * !(p)
770      * A simple adjuster might simply set the one of the fields, such as the hour field.
771      * A more complex adjuster might set the time to the last hour of the day.
772      * !(p)
773      * The result of this method is obtained by invoking the
774      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
775      * specified adjuster passing {@code this} as the argument.
776      * !(p)
777      * This instance is immutable and unaffected by this method call.
778      *
779      * @param adjuster the adjuster to use, not null
780      * @return a {@code LocalTime} based on {@code this} with the adjustment made, not null
781      * @throws DateTimeException if the adjustment cannot be made
782      * @throws ArithmeticException if numeric overflow occurs
783      */
784     override
785     public LocalTime _with(TemporalAdjuster adjuster) {
786         // optimizations
787         if (cast(LocalTime)(adjuster) !is null) {
788             return cast(LocalTime) adjuster;
789         }
790         return cast(LocalTime) adjuster.adjustInto(this);
791     }
792 
793     /**
794      * Returns a copy of this time with the specified field set to a new value.
795      * !(p)
796      * This returns a {@code LocalTime}, based on this one, with the value
797      * for the specified field changed.
798      * This can be used to change any supported field, such as the hour, minute or second.
799      * If it is not possible to set the value, because the field is not supported or for
800      * some other reason, an exception is thrown.
801      * !(p)
802      * If the field is a {@link ChronoField} then the adjustment is implemented here.
803      * The supported fields behave as follows:
804      * !(ul)
805      * !(li){@code NANO_OF_SECOND} -
806      *  Returns a {@code LocalTime} with the specified nano-of-second.
807      *  The hour, minute and second will be unchanged.
808      * !(li){@code NANO_OF_DAY} -
809      *  Returns a {@code LocalTime} with the specified nano-of-day.
810      *  This completely replaces the time and is equivalent to {@link #ofNanoOfDay(long)}.
811      * !(li){@code MICRO_OF_SECOND} -
812      *  Returns a {@code LocalTime} with the nano-of-second replaced by the specified
813      *  micro-of-second multiplied by 1,000.
814      *  The hour, minute and second will be unchanged.
815      * !(li){@code MICRO_OF_DAY} -
816      *  Returns a {@code LocalTime} with the specified micro-of-day.
817      *  This completely replaces the time and is equivalent to using {@link #ofNanoOfDay(long)}
818      *  with the micro-of-day multiplied by 1,000.
819      * !(li){@code MILLI_OF_SECOND} -
820      *  Returns a {@code LocalTime} with the nano-of-second replaced by the specified
821      *  milli-of-second multiplied by 1,000,000.
822      *  The hour, minute and second will be unchanged.
823      * !(li){@code MILLI_OF_DAY} -
824      *  Returns a {@code LocalTime} with the specified milli-of-day.
825      *  This completely replaces the time and is equivalent to using {@link #ofNanoOfDay(long)}
826      *  with the milli-of-day multiplied by 1,000,000.
827      * !(li){@code SECOND_OF_MINUTE} -
828      *  Returns a {@code LocalTime} with the specified second-of-minute.
829      *  The hour, minute and nano-of-second will be unchanged.
830      * !(li){@code SECOND_OF_DAY} -
831      *  Returns a {@code LocalTime} with the specified second-of-day.
832      *  The nano-of-second will be unchanged.
833      * !(li){@code MINUTE_OF_HOUR} -
834      *  Returns a {@code LocalTime} with the specified minute-of-hour.
835      *  The hour, second-of-minute and nano-of-second will be unchanged.
836      * !(li){@code MINUTE_OF_DAY} -
837      *  Returns a {@code LocalTime} with the specified minute-of-day.
838      *  The second-of-minute and nano-of-second will be unchanged.
839      * !(li){@code HOUR_OF_AMPM} -
840      *  Returns a {@code LocalTime} with the specified hour-of-am-pm.
841      *  The AM/PM, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
842      * !(li){@code CLOCK_HOUR_OF_AMPM} -
843      *  Returns a {@code LocalTime} with the specified clock-hour-of-am-pm.
844      *  The AM/PM, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
845      * !(li){@code HOUR_OF_DAY} -
846      *  Returns a {@code LocalTime} with the specified hour-of-day.
847      *  The minute-of-hour, second-of-minute and nano-of-second will be unchanged.
848      * !(li){@code CLOCK_HOUR_OF_DAY} -
849      *  Returns a {@code LocalTime} with the specified clock-hour-of-day.
850      *  The minute-of-hour, second-of-minute and nano-of-second will be unchanged.
851      * !(li){@code AMPM_OF_DAY} -
852      *  Returns a {@code LocalTime} with the specified AM/PM.
853      *  The hour-of-am-pm, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
854      * </ul>
855      * !(p)
856      * In all cases, if the new value is outside the valid range of values for the field
857      * then a {@code DateTimeException} will be thrown.
858      * !(p)
859      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
860      * !(p)
861      * If the field is not a {@code ChronoField}, then the result of this method
862      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
863      * passing {@code this} as the argument. In this case, the field determines
864      * whether and how to adjust the instant.
865      * !(p)
866      * This instance is immutable and unaffected by this method call.
867      *
868      * @param field  the field to set _in the result, not null
869      * @param newValue  the new value of the field _in the result
870      * @return a {@code LocalTime} based on {@code this} with the specified field set, not null
871      * @throws DateTimeException if the field cannot be set
872      * @throws UnsupportedTemporalTypeException if the field is not supported
873      * @throws ArithmeticException if numeric overflow occurs
874      */
875     override
876     public LocalTime _with(TemporalField field, long newValue) {
877         if (cast(ChronoField)(field) !is null) {
878             ChronoField f = cast(ChronoField) field;
879             f.checkValidValue(newValue);
880             {
881                 if(f == ChronoField.NANO_OF_SECOND) return withNano(cast(int) newValue);
882                 if(f == ChronoField.NANO_OF_DAY) return LocalTime.ofNanoOfDay(newValue);
883                 if(f == ChronoField.MICRO_OF_SECOND) return withNano(cast(int) newValue * 1000);
884                 if(f == ChronoField.MICRO_OF_DAY) return LocalTime.ofNanoOfDay(newValue * 1000);
885                 if(f == ChronoField.MILLI_OF_SECOND) return withNano(cast(int) newValue * 1000_000);
886                 if(f == ChronoField.MILLI_OF_DAY) return LocalTime.ofNanoOfDay(newValue * 1000_000);
887                 if(f == ChronoField.SECOND_OF_MINUTE) return withSecond(cast(int) newValue);
888                 if(f == ChronoField.SECOND_OF_DAY) return plusSeconds(newValue - toSecondOfDay());
889                 if(f == ChronoField.MINUTE_OF_HOUR) return withMinute(cast(int) newValue);
890                 if(f == ChronoField.MINUTE_OF_DAY) return plusMinutes(newValue - (hour * 60 + minute));
891                 if(f == ChronoField.HOUR_OF_AMPM) return plusHours(newValue - (hour % 12));
892                 if(f == ChronoField.CLOCK_HOUR_OF_AMPM) return plusHours((newValue == 12 ? 0 : newValue) - (hour % 12));
893                 if(f == ChronoField.HOUR_OF_DAY) return withHour(cast(int) newValue);
894                 if(f == ChronoField.CLOCK_HOUR_OF_DAY) return withHour(cast(int) (newValue == 24 ? 0 : newValue));
895                 if(f == ChronoField.AMPM_OF_DAY) return plusHours((newValue - (hour / 12)) * 12);
896             }
897             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ f.toString);
898         }
899         return cast(LocalTime)(field.adjustInto(this, newValue));
900     }
901 
902     //-----------------------------------------------------------------------
903     /**
904      * Returns a copy of this {@code LocalTime} with the hour-of-day altered.
905      * !(p)
906      * This instance is immutable and unaffected by this method call.
907      *
908      * @param hour  the hour-of-day to set _in the result, from 0 to 23
909      * @return a {@code LocalTime} based on this time with the requested hour, not null
910      * @throws DateTimeException if the hour value is invalid
911      */
912     public LocalTime withHour(int hour) {
913         if (this.hour == hour) {
914             return this;
915         }
916         ChronoField.HOUR_OF_DAY.checkValidValue(hour);
917         return create(hour, minute, second, nano);
918     }
919 
920     /**
921      * Returns a copy of this {@code LocalTime} with the minute-of-hour altered.
922      * !(p)
923      * This instance is immutable and unaffected by this method call.
924      *
925      * @param minute  the minute-of-hour to set _in the result, from 0 to 59
926      * @return a {@code LocalTime} based on this time with the requested minute, not null
927      * @throws DateTimeException if the minute value is invalid
928      */
929     public LocalTime withMinute(int minute) {
930         if (this.minute == minute) {
931             return this;
932         }
933         ChronoField.MINUTE_OF_HOUR.checkValidValue(minute);
934         return create(hour, minute, second, nano);
935     }
936 
937     /**
938      * Returns a copy of this {@code LocalTime} with the second-of-minute altered.
939      * !(p)
940      * This instance is immutable and unaffected by this method call.
941      *
942      * @param second  the second-of-minute to set _in the result, from 0 to 59
943      * @return a {@code LocalTime} based on this time with the requested second, not null
944      * @throws DateTimeException if the second value is invalid
945      */
946     public LocalTime withSecond(int second) {
947         if (this.second == second) {
948             return this;
949         }
950         ChronoField.SECOND_OF_MINUTE.checkValidValue(second);
951         return create(hour, minute, second, nano);
952     }
953 
954     /**
955      * Returns a copy of this {@code LocalTime} with the nano-of-second altered.
956      * !(p)
957      * This instance is immutable and unaffected by this method call.
958      *
959      * @param nanoOfSecond  the nano-of-second to set _in the result, from 0 to 999,999,999
960      * @return a {@code LocalTime} based on this time with the requested nanosecond, not null
961      * @throws DateTimeException if the nanos value is invalid
962      */
963     public LocalTime withNano(int nanoOfSecond) {
964         if (this.nano == nanoOfSecond) {
965             return this;
966         }
967         ChronoField.NANO_OF_SECOND.checkValidValue(nanoOfSecond);
968         return create(hour, minute, second, nanoOfSecond);
969     }
970 
971     //-----------------------------------------------------------------------
972     /**
973      * Returns a copy of this {@code LocalTime} with the time truncated.
974      * !(p)
975      * Truncation returns a copy of the original time with fields
976      * smaller than the specified unit set to zero.
977      * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
978      * will set the second-of-minute and nano-of-second field to zero.
979      * !(p)
980      * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
981      * that divides into the length of a standard day without remainder.
982      * This includes all supplied time units on {@link ChronoUnit} and
983      * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
984      * !(p)
985      * This instance is immutable and unaffected by this method call.
986      *
987      * @param unit  the unit to truncate to, not null
988      * @return a {@code LocalTime} based on this time with the time truncated, not null
989      * @throws DateTimeException if unable to truncate
990      * @throws UnsupportedTemporalTypeException if the unit is not supported
991      */
992     public LocalTime truncatedTo(TemporalUnit unit) {
993         if (unit == ChronoUnit.NANOS) {
994             return this;
995         }
996         Duration unitDur = unit.getDuration();
997         if (unitDur.getSeconds() > SECONDS_PER_DAY) {
998             throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
999         }
1000         long dur = unitDur.toNanos();
1001         if ((NANOS_PER_DAY % dur) != 0) {
1002             throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
1003         }
1004         long nod = toNanoOfDay();
1005         return ofNanoOfDay((nod / dur) * dur);
1006     }
1007 
1008     //-----------------------------------------------------------------------
1009     /**
1010      * Returns a copy of this time with the specified amount added.
1011      * !(p)
1012      * This returns a {@code LocalTime}, based on this one, with the specified amount added.
1013      * The amount is typically {@link Duration} but may be any other type implementing
1014      * the {@link TemporalAmount} interface.
1015      * !(p)
1016      * The calculation is delegated to the amount object by calling
1017      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
1018      * to implement the addition _in any way it wishes, however it typically
1019      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
1020      * of the amount implementation to determine if it can be successfully added.
1021      * !(p)
1022      * This instance is immutable and unaffected by this method call.
1023      *
1024      * @param amountToAdd  the amount to add, not null
1025      * @return a {@code LocalTime} based on this time with the addition made, not null
1026      * @throws DateTimeException if the addition cannot be made
1027      * @throws ArithmeticException if numeric overflow occurs
1028      */
1029     override
1030     public LocalTime plus(TemporalAmount amountToAdd) {
1031         return cast(LocalTime) amountToAdd.addTo(this);
1032     }
1033 
1034     /**
1035      * Returns a copy of this time with the specified amount added.
1036      * !(p)
1037      * This returns a {@code LocalTime}, based on this one, with the amount
1038      * _in terms of the unit added. If it is not possible to add the amount, because the
1039      * unit is not supported or for some other reason, an exception is thrown.
1040      * !(p)
1041      * If the field is a {@link ChronoUnit} then the addition is implemented here.
1042      * The supported fields behave as follows:
1043      * !(ul)
1044      * !(li){@code NANOS} -
1045      *  Returns a {@code LocalTime} with the specified number of nanoseconds added.
1046      *  This is equivalent to {@link #plusNanos(long)}.
1047      * !(li){@code MICROS} -
1048      *  Returns a {@code LocalTime} with the specified number of microseconds added.
1049      *  This is equivalent to {@link #plusNanos(long)} with the amount
1050      *  multiplied by 1,000.
1051      * !(li){@code MILLIS} -
1052      *  Returns a {@code LocalTime} with the specified number of milliseconds added.
1053      *  This is equivalent to {@link #plusNanos(long)} with the amount
1054      *  multiplied by 1,000,000.
1055      * !(li){@code SECONDS} -
1056      *  Returns a {@code LocalTime} with the specified number of seconds added.
1057      *  This is equivalent to {@link #plusSeconds(long)}.
1058      * !(li){@code MINUTES} -
1059      *  Returns a {@code LocalTime} with the specified number of minutes added.
1060      *  This is equivalent to {@link #plusMinutes(long)}.
1061      * !(li){@code HOURS} -
1062      *  Returns a {@code LocalTime} with the specified number of hours added.
1063      *  This is equivalent to {@link #plusHours(long)}.
1064      * !(li){@code HALF_DAYS} -
1065      *  Returns a {@code LocalTime} with the specified number of half-days added.
1066      *  This is equivalent to {@link #plusHours(long)} with the amount
1067      *  multiplied by 12.
1068      * </ul>
1069      * !(p)
1070      * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
1071      * !(p)
1072      * If the field is not a {@code ChronoUnit}, then the result of this method
1073      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
1074      * passing {@code this} as the argument. In this case, the unit determines
1075      * whether and how to perform the addition.
1076      * !(p)
1077      * This instance is immutable and unaffected by this method call.
1078      *
1079      * @param amountToAdd  the amount of the unit to add to the result, may be negative
1080      * @param unit  the unit of the amount to add, not null
1081      * @return a {@code LocalTime} based on this time with the specified amount added, not null
1082      * @throws DateTimeException if the addition cannot be made
1083      * @throws UnsupportedTemporalTypeException if the unit is not supported
1084      * @throws ArithmeticException if numeric overflow occurs
1085      */
1086     override
1087     public LocalTime plus(long amountToAdd, TemporalUnit unit) {
1088         if (cast(ChronoUnit)(unit) !is null) {
1089             auto f = cast(ChronoUnit) unit;
1090             {
1091                 if( f == ChronoUnit.NANOS) return plusNanos(amountToAdd);
1092                 if( f == ChronoUnit.MICROS) return plusNanos((amountToAdd % LocalTime.MICROS_PER_DAY) * 1000);
1093                 if( f == ChronoUnit.MILLIS) return plusNanos((amountToAdd % LocalTime.MILLIS_PER_DAY) * 1000_000);
1094                 if( f == ChronoUnit.SECONDS) return plusSeconds(amountToAdd);
1095                 if( f == ChronoUnit.MINUTES) return plusMinutes(amountToAdd);
1096                 if( f == ChronoUnit.HOURS) return plusHours(amountToAdd);
1097                 if( f == ChronoUnit.HALF_DAYS) return plusHours((amountToAdd % 2) * 12);
1098             }
1099             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
1100         }
1101         return cast(LocalTime)(unit.addTo(this, amountToAdd));
1102     }
1103 
1104     //-----------------------------------------------------------------------
1105     /**
1106      * Returns a copy of this {@code LocalTime} with the specified number of hours added.
1107      * !(p)
1108      * This adds the specified number of hours to this time, returning a new time.
1109      * The calculation wraps around midnight.
1110      * !(p)
1111      * This instance is immutable and unaffected by this method call.
1112      *
1113      * @param hoursToAdd  the hours to add, may be negative
1114      * @return a {@code LocalTime} based on this time with the hours added, not null
1115      */
1116     public LocalTime plusHours(long hoursToAdd) {
1117         if (hoursToAdd == 0) {
1118             return this;
1119         }
1120         int newHour = (cast(int) (hoursToAdd % HOURS_PER_DAY) + hour + HOURS_PER_DAY) % HOURS_PER_DAY;
1121         return create(newHour, minute, second, nano);
1122     }
1123 
1124     /**
1125      * Returns a copy of this {@code LocalTime} with the specified number of minutes added.
1126      * !(p)
1127      * This adds the specified number of minutes to this time, returning a new time.
1128      * The calculation wraps around midnight.
1129      * !(p)
1130      * This instance is immutable and unaffected by this method call.
1131      *
1132      * @param minutesToAdd  the minutes to add, may be negative
1133      * @return a {@code LocalTime} based on this time with the minutes added, not null
1134      */
1135     public LocalTime plusMinutes(long minutesToAdd) {
1136         if (minutesToAdd == 0) {
1137             return this;
1138         }
1139         int mofd = hour * MINUTES_PER_HOUR + minute;
1140         int newMofd = (cast(int) (minutesToAdd % MINUTES_PER_DAY) + mofd + MINUTES_PER_DAY) % MINUTES_PER_DAY;
1141         if (mofd == newMofd) {
1142             return this;
1143         }
1144         int newHour = newMofd / MINUTES_PER_HOUR;
1145         int newMinute = newMofd % MINUTES_PER_HOUR;
1146         return create(newHour, newMinute, second, nano);
1147     }
1148 
1149     /**
1150      * Returns a copy of this {@code LocalTime} with the specified number of seconds added.
1151      * !(p)
1152      * This adds the specified number of seconds to this time, returning a new time.
1153      * The calculation wraps around midnight.
1154      * !(p)
1155      * This instance is immutable and unaffected by this method call.
1156      *
1157      * @param secondstoAdd  the seconds to add, may be negative
1158      * @return a {@code LocalTime} based on this time with the seconds added, not null
1159      */
1160     public LocalTime plusSeconds(long secondstoAdd) {
1161         if (secondstoAdd == 0) {
1162             return this;
1163         }
1164         int sofd = hour * SECONDS_PER_HOUR +
1165                     minute * SECONDS_PER_MINUTE + second;
1166         int newSofd = (cast(int) (secondstoAdd % SECONDS_PER_DAY) + sofd + SECONDS_PER_DAY) % SECONDS_PER_DAY;
1167         if (sofd == newSofd) {
1168             return this;
1169         }
1170         int newHour = newSofd / SECONDS_PER_HOUR;
1171         int newMinute = (newSofd / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
1172         int newSecond = newSofd % SECONDS_PER_MINUTE;
1173         return create(newHour, newMinute, newSecond, nano);
1174     }
1175 
1176     /**
1177      * Returns a copy of this {@code LocalTime} with the specified number of nanoseconds added.
1178      * !(p)
1179      * This adds the specified number of nanoseconds to this time, returning a new time.
1180      * The calculation wraps around midnight.
1181      * !(p)
1182      * This instance is immutable and unaffected by this method call.
1183      *
1184      * @param nanosToAdd  the nanos to add, may be negative
1185      * @return a {@code LocalTime} based on this time with the nanoseconds added, not null
1186      */
1187     public LocalTime plusNanos(long nanosToAdd) {
1188         if (nanosToAdd == 0) {
1189             return this;
1190         }
1191         long nofd = toNanoOfDay();
1192         long newNofd = ((nanosToAdd % NANOS_PER_DAY) + nofd + NANOS_PER_DAY) % NANOS_PER_DAY;
1193         if (nofd == newNofd) {
1194             return this;
1195         }
1196         int newHour = cast(int) (newNofd / NANOS_PER_HOUR);
1197         int newMinute = cast(int) ((newNofd / NANOS_PER_MINUTE) % MINUTES_PER_HOUR);
1198         int newSecond = cast(int) ((newNofd / NANOS_PER_SECOND) % SECONDS_PER_MINUTE);
1199         int newNano = cast(int) (newNofd % NANOS_PER_SECOND);
1200         return create(newHour, newMinute, newSecond, newNano);
1201     }
1202 
1203     //-----------------------------------------------------------------------
1204     /**
1205      * Returns a copy of this time with the specified amount subtracted.
1206      * !(p)
1207      * This returns a {@code LocalTime}, based on this one, with the specified amount subtracted.
1208      * The amount is typically {@link Duration} but may be any other type implementing
1209      * the {@link TemporalAmount} interface.
1210      * !(p)
1211      * The calculation is delegated to the amount object by calling
1212      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
1213      * to implement the subtraction _in any way it wishes, however it typically
1214      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
1215      * of the amount implementation to determine if it can be successfully subtracted.
1216      * !(p)
1217      * This instance is immutable and unaffected by this method call.
1218      *
1219      * @param amountToSubtract  the amount to subtract, not null
1220      * @return a {@code LocalTime} based on this time with the subtraction made, not null
1221      * @throws DateTimeException if the subtraction cannot be made
1222      * @throws ArithmeticException if numeric overflow occurs
1223      */
1224     override
1225     public LocalTime minus(TemporalAmount amountToSubtract) {
1226         return cast(LocalTime) amountToSubtract.subtractFrom(this);
1227     }
1228 
1229     /**
1230      * Returns a copy of this time with the specified amount subtracted.
1231      * !(p)
1232      * This returns a {@code LocalTime}, based on this one, with the amount
1233      * _in terms of the unit subtracted. If it is not possible to subtract the amount,
1234      * because the unit is not supported or for some other reason, an exception is thrown.
1235      * !(p)
1236      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
1237      * See that method for a full description of how addition, and thus subtraction, works.
1238      * !(p)
1239      * This instance is immutable and unaffected by this method call.
1240      *
1241      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
1242      * @param unit  the unit of the amount to subtract, not null
1243      * @return a {@code LocalTime} based on this time with the specified amount subtracted, not null
1244      * @throws DateTimeException if the subtraction cannot be made
1245      * @throws UnsupportedTemporalTypeException if the unit is not supported
1246      * @throws ArithmeticException if numeric overflow occurs
1247      */
1248     override
1249     public LocalTime minus(long amountToSubtract, TemporalUnit unit) {
1250         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
1251     }
1252 
1253     //-----------------------------------------------------------------------
1254     /**
1255      * Returns a copy of this {@code LocalTime} with the specified number of hours subtracted.
1256      * !(p)
1257      * This subtracts the specified number of hours from this time, returning a new time.
1258      * The calculation wraps around midnight.
1259      * !(p)
1260      * This instance is immutable and unaffected by this method call.
1261      *
1262      * @param hoursToSubtract  the hours to subtract, may be negative
1263      * @return a {@code LocalTime} based on this time with the hours subtracted, not null
1264      */
1265     public LocalTime minusHours(long hoursToSubtract) {
1266         return plusHours(-(hoursToSubtract % HOURS_PER_DAY));
1267     }
1268 
1269     /**
1270      * Returns a copy of this {@code LocalTime} with the specified number of minutes subtracted.
1271      * !(p)
1272      * This subtracts the specified number of minutes from this time, returning a new time.
1273      * The calculation wraps around midnight.
1274      * !(p)
1275      * This instance is immutable and unaffected by this method call.
1276      *
1277      * @param minutesToSubtract  the minutes to subtract, may be negative
1278      * @return a {@code LocalTime} based on this time with the minutes subtracted, not null
1279      */
1280     public LocalTime minusMinutes(long minutesToSubtract) {
1281         return plusMinutes(-(minutesToSubtract % MINUTES_PER_DAY));
1282     }
1283 
1284     /**
1285      * Returns a copy of this {@code LocalTime} with the specified number of seconds subtracted.
1286      * !(p)
1287      * This subtracts the specified number of seconds from this time, returning a new time.
1288      * The calculation wraps around midnight.
1289      * !(p)
1290      * This instance is immutable and unaffected by this method call.
1291      *
1292      * @param secondsToSubtract  the seconds to subtract, may be negative
1293      * @return a {@code LocalTime} based on this time with the seconds subtracted, not null
1294      */
1295     public LocalTime minusSeconds(long secondsToSubtract) {
1296         return plusSeconds(-(secondsToSubtract % SECONDS_PER_DAY));
1297     }
1298 
1299     /**
1300      * Returns a copy of this {@code LocalTime} with the specified number of nanoseconds subtracted.
1301      * !(p)
1302      * This subtracts the specified number of nanoseconds from this time, returning a new time.
1303      * The calculation wraps around midnight.
1304      * !(p)
1305      * This instance is immutable and unaffected by this method call.
1306      *
1307      * @param nanosToSubtract  the nanos to subtract, may be negative
1308      * @return a {@code LocalTime} based on this time with the nanoseconds subtracted, not null
1309      */
1310     public LocalTime minusNanos(long nanosToSubtract) {
1311         return plusNanos(-(nanosToSubtract % NANOS_PER_DAY));
1312     }
1313 
1314     //-----------------------------------------------------------------------
1315     /**
1316      * Queries this time using the specified query.
1317      * !(p)
1318      * This queries this time using the specified query strategy object.
1319      * The {@code TemporalQuery} object defines the logic to be used to
1320      * obtain the result. Read the documentation of the query to understand
1321      * what the result of this method will be.
1322      * !(p)
1323      * The result of this method is obtained by invoking the
1324      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
1325      * specified query passing {@code this} as the argument.
1326      *
1327      * @param !(R) the type of the result
1328      * @param query  the query to invoke, not null
1329      * @return the query result, null may be returned (defined by the query)
1330      * @throws DateTimeException if unable to query (defined by the query)
1331      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
1332      */
1333     /*@SuppressWarnings("unchecked")*/
1334     // override
1335     public R query(R)(TemporalQuery!(R) query) {
1336         if (query == TemporalQueries.chronology() || query == TemporalQueries.zoneId() ||
1337                 query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
1338             return null;
1339         } else if (query == TemporalQueries.localTime()) {
1340             return cast(R) this;
1341         } else if (query == TemporalQueries.localDate()) {
1342             return null;
1343         } else if (query == TemporalQueries.precision()) {
1344             return cast(R) (ChronoUnit.NANOS);
1345         }
1346         // inline TemporalAccessor.super.query(query) as an optimization
1347         // non-JDK classes are not permitted to make this optimization
1348         return query.queryFrom(this);
1349     }
1350 
1351     /**
1352      * Adjusts the specified temporal object to have the same time as this object.
1353      * !(p)
1354      * This returns a temporal object of the same observable type as the input
1355      * with the time changed to be the same as this.
1356      * !(p)
1357      * The adjustment is equivalent to using {@link Temporal#_with(TemporalField, long)}
1358      * passing {@link ChronoField#NANO_OF_DAY} as the field.
1359      * !(p)
1360      * In most cases, it is clearer to reverse the calling pattern by using
1361      * {@link Temporal#_with(TemporalAdjuster)}:
1362      * !(pre)
1363      *   // these two lines are equivalent, but the second approach is recommended
1364      *   temporal = thisLocalTime.adjustInto(temporal);
1365      *   temporal = temporal._with(thisLocalTime);
1366      * </pre>
1367      * !(p)
1368      * This instance is immutable and unaffected by this method call.
1369      *
1370      * @param temporal  the target object to be adjusted, not null
1371      * @return the adjusted object, not null
1372      * @throws DateTimeException if unable to make the adjustment
1373      * @throws ArithmeticException if numeric overflow occurs
1374      */
1375     override
1376     public Temporal adjustInto(Temporal temporal) {
1377         return temporal._with(ChronoField.NANO_OF_DAY, toNanoOfDay());
1378     }
1379 
1380     /**
1381      * Calculates the amount of time until another time _in terms of the specified unit.
1382      * !(p)
1383      * This calculates the amount of time between two {@code LocalTime}
1384      * objects _in terms of a single {@code TemporalUnit}.
1385      * The start and end points are {@code this} and the specified time.
1386      * The result will be negative if the end is before the start.
1387      * The {@code Temporal} passed to this method is converted to a
1388      * {@code LocalTime} using {@link #from(TemporalAccessor)}.
1389      * For example, the amount _in hours between two times can be calculated
1390      * using {@code startTime.until(endTime, HOURS)}.
1391      * !(p)
1392      * The calculation returns a whole number, representing the number of
1393      * complete units between the two times.
1394      * For example, the amount _in hours between 11:30 and 13:29 will only
1395      * be one hour as it is one minute short of two hours.
1396      * !(p)
1397      * There are two equivalent ways of using this method.
1398      * The first is to invoke this method.
1399      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1400      * !(pre)
1401      *   // these two lines are equivalent
1402      *   amount = start.until(end, MINUTES);
1403      *   amount = MINUTES.between(start, end);
1404      * </pre>
1405      * The choice should be made based on which makes the code more readable.
1406      * !(p)
1407      * The calculation is implemented _in this method for {@link ChronoUnit}.
1408      * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
1409      * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} are supported.
1410      * Other {@code ChronoUnit} values will throw an exception.
1411      * !(p)
1412      * If the unit is not a {@code ChronoUnit}, then the result of this method
1413      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1414      * passing {@code this} as the first argument and the converted input temporal
1415      * as the second argument.
1416      * !(p)
1417      * This instance is immutable and unaffected by this method call.
1418      *
1419      * @param endExclusive  the end time, exclusive, which is converted to a {@code LocalTime}, not null
1420      * @param unit  the unit to measure the amount _in, not null
1421      * @return the amount of time between this time and the end time
1422      * @throws DateTimeException if the amount cannot be calculated, or the end
1423      *  temporal cannot be converted to a {@code LocalTime}
1424      * @throws UnsupportedTemporalTypeException if the unit is not supported
1425      * @throws ArithmeticException if numeric overflow occurs
1426      */
1427     override
1428     public long until(Temporal endExclusive, TemporalUnit unit) {
1429         LocalTime end = LocalTime.from(endExclusive);
1430         if (cast(ChronoUnit)(unit) !is null) {
1431             long nanosUntil = end.toNanoOfDay() - toNanoOfDay();  // no overflow
1432             auto f = cast(ChronoUnit) unit;
1433             {
1434                 if( f == ChronoUnit.NANOS) return nanosUntil;
1435                 if( f == ChronoUnit.MICROS) return nanosUntil / 1000;
1436                 if( f == ChronoUnit.MILLIS) return nanosUntil / 1000_000;
1437                 if( f == ChronoUnit.SECONDS) return nanosUntil / NANOS_PER_SECOND;
1438                 if( f == ChronoUnit.MINUTES) return nanosUntil / NANOS_PER_MINUTE;
1439                 if( f == ChronoUnit.HOURS) return nanosUntil / NANOS_PER_HOUR;
1440                 if( f == ChronoUnit.HALF_DAYS) return nanosUntil / (12 * NANOS_PER_HOUR);
1441             }
1442             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
1443         }
1444         return unit.between(this, end);
1445     }
1446 
1447     /**
1448      * Formats this time using the specified formatter.
1449      * !(p)
1450      * This time will be passed to the formatter to produce a string.
1451      *
1452      * @param formatter  the formatter to use, not null
1453      * @return the formatted time string, not null
1454      * @throws DateTimeException if an error occurs during printing
1455      */
1456     // public string format(DateTimeFormatter formatter) {
1457     //     assert(formatter, "formatter");
1458     //     return formatter.format(this);
1459     // }
1460 
1461     //-----------------------------------------------------------------------
1462     /**
1463      * Combines this time with a date to create a {@code LocalDateTime}.
1464      * !(p)
1465      * This returns a {@code LocalDateTime} formed from this time at the specified date.
1466      * All possible combinations of date and time are valid.
1467      *
1468      * @param date  the date to combine with, not null
1469      * @return the local date-time formed from this time and the specified date, not null
1470      */
1471     public LocalDateTime atDate(LocalDate date) {
1472         return LocalDateTime.of(date, this);
1473     }
1474 
1475     /**
1476      * Combines this time with an offset to create an {@code OffsetTime}.
1477      * !(p)
1478      * This returns an {@code OffsetTime} formed from this time at the specified offset.
1479      * All possible combinations of time and offset are valid.
1480      *
1481      * @param offset  the offset to combine with, not null
1482      * @return the offset time formed from this time and the specified offset, not null
1483      */
1484     public OffsetTime atOffset(ZoneOffset offset) {
1485         return OffsetTime.of(this, offset);
1486     }
1487 
1488     //-----------------------------------------------------------------------
1489     /**
1490      * Extracts the time as seconds of day,
1491      * from {@code 0} to {@code 24 * 60 * 60 - 1}.
1492      *
1493      * @return the second-of-day equivalent to this time
1494      */
1495     public int toSecondOfDay() {
1496         int total = hour * SECONDS_PER_HOUR;
1497         total += minute * SECONDS_PER_MINUTE;
1498         total += second;
1499         return total;
1500     }
1501 
1502     /**
1503      * Extracts the time as nanos of day,
1504      * from {@code 0} to {@code 24 * 60 * 60 * 1,000,000,000 - 1}.
1505      *
1506      * @return the nano of day equivalent to this time
1507      */
1508     public long toNanoOfDay() {
1509         long total = hour * NANOS_PER_HOUR;
1510         total += minute * NANOS_PER_MINUTE;
1511         total += second * NANOS_PER_SECOND;
1512         total += nano;
1513         return total;
1514     }
1515 
1516     /**
1517      * Converts this {@code LocalTime} to the number of seconds since the epoch
1518      * of 1970-01-01T00:00:00Z.
1519      * !(p)
1520      * This combines this local time with the specified date and
1521      * offset to calculate the epoch-second value, which is the
1522      * number of elapsed seconds from 1970-01-01T00:00:00Z.
1523      * Instants on the time-line after the epoch are positive, earlier
1524      * are negative.
1525      *
1526      * @param date the local date, not null
1527      * @param offset the zone offset, not null
1528      * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
1529      * @since 9
1530      */
1531     public long toEpochSecond(LocalDate date, ZoneOffset offset) {
1532         assert(date, "date");
1533         assert(offset, "offset");
1534         long epochDay = date.toEpochDay();
1535         long secs = epochDay * 86400 + toSecondOfDay();
1536         secs -= offset.getTotalSeconds();
1537         return secs;
1538     }
1539 
1540     //-----------------------------------------------------------------------
1541     /**
1542      * Compares this time to another time.
1543      * !(p)
1544      * The comparison is based on the time-line position of the local times within a day.
1545      * It is "consistent with equals", as defined by {@link Comparable}.
1546      *
1547      * @param other  the other time to compare to, not null
1548      * @return the comparator value, negative if less, positive if greater
1549      */
1550     // override
1551     public int compareTo(LocalTime other) {
1552         int cmp = compare(hour, other.hour);
1553         if (cmp == 0) {
1554             cmp = compare(minute, other.minute);
1555             if (cmp == 0) {
1556                 cmp = compare(second, other.second);
1557                 if (cmp == 0) {
1558                     cmp = compare(nano, other.nano);
1559                 }
1560             }
1561         }
1562         return cmp;
1563     }
1564 
1565     override
1566     public int opCmp(LocalTime other) {
1567         int cmp = compare(hour, other.hour);
1568         if (cmp == 0) {
1569             cmp = compare(minute, other.minute);
1570             if (cmp == 0) {
1571                 cmp = compare(second, other.second);
1572                 if (cmp == 0) {
1573                     cmp = compare(nano, other.nano);
1574                 }
1575             }
1576         }
1577         return cmp;
1578     }
1579 
1580     /**
1581      * Checks if this time is after the specified time.
1582      * !(p)
1583      * The comparison is based on the time-line position of the time within a day.
1584      *
1585      * @param other  the other time to compare to, not null
1586      * @return true if this is after the specified time
1587      */
1588     public bool isAfter(LocalTime other) {
1589         return compareTo(other) > 0;
1590     }
1591 
1592     /**
1593      * Checks if this time is before the specified time.
1594      * !(p)
1595      * The comparison is based on the time-line position of the time within a day.
1596      *
1597      * @param other  the other time to compare to, not null
1598      * @return true if this point is before the specified time
1599      */
1600     public bool isBefore(LocalTime other) {
1601         return compareTo(other) < 0;
1602     }
1603 
1604     //-----------------------------------------------------------------------
1605     /**
1606      * Checks if this time is equal to another time.
1607      * !(p)
1608      * The comparison is based on the time-line position of the time within a day.
1609      * !(p)
1610      * Only objects of type {@code LocalTime} are compared, other types return false.
1611      * To compare the date of two {@code TemporalAccessor} instances, use
1612      * {@link ChronoField#NANO_OF_DAY} as a comparator.
1613      *
1614      * @param obj  the object to check, null returns false
1615      * @return true if this is equal to the other time
1616      */
1617     override
1618     public bool opEquals(Object obj) {
1619         if (this is obj) {
1620             return true;
1621         }
1622         if (cast(LocalTime)(obj) !is null) {
1623             LocalTime other = cast(LocalTime) obj;
1624             return hour == other.hour && minute == other.minute &&
1625                     second == other.second && nano == other.nano;
1626         }
1627         return false;
1628     }
1629 
1630     /**
1631      * A hash code for this time.
1632      *
1633      * @return a suitable hash code
1634      */
1635     override
1636     public size_t toHash() @trusted nothrow {
1637         try
1638         {
1639             long nod = toNanoOfDay();
1640             return cast(int) (nod ^ (nod >>> 32));
1641         }
1642         catch(Exception e)
1643         {}
1644         return int.init;
1645     }
1646 
1647     //-----------------------------------------------------------------------
1648     /**
1649      * Outputs this time as a {@code string}, such as {@code 10:15}.
1650      * !(p)
1651      * The output will be one of the following ISO-8601 formats:
1652      * !(ul)
1653      * !(li){@code HH:mm}</li>
1654      * !(li){@code HH:mm:ss}</li>
1655      * !(li){@code HH:mm:ss.SSS}</li>
1656      * !(li){@code HH:mm:ss.SSSSSS}</li>
1657      * !(li){@code HH:mm:ss.SSSSSSSSS}</li>
1658      * </ul>
1659      * The format used will be the shortest that outputs the full value of
1660      * the time where the omitted parts are implied to be zero.
1661      *
1662      * @return a string representation of this time, not null
1663      */
1664     override
1665     public string toString() {
1666         StringBuilder buf = new StringBuilder(18);
1667         int hourValue = hour;
1668         int minuteValue = minute;
1669         int secondValue = second;
1670         int nanoValue = nano;
1671         buf.append(hourValue < 10 ? "0" : "").append(hourValue)
1672             .append(minuteValue < 10 ? ":0" : ":").append(minuteValue);
1673         if (secondValue > 0 || nanoValue > 0) {
1674             buf.append(secondValue < 10 ? ":0" : ":").append(secondValue);
1675             if (nanoValue > 0) {
1676                 buf.append('.');
1677                 if (nanoValue % 1000_000 == 0) {
1678                     buf.append(to!string((nanoValue / 1000_000) + 1000).substring(1));
1679                 } else if (nanoValue % 1000 == 0) {
1680                     buf.append(to!string((nanoValue / 1000) + 1000_000).substring(1));
1681                 } else {
1682                     buf.append(to!string((nanoValue) + 1000_000_000).substring(1));
1683                 }
1684             }
1685         }
1686         return buf.toString();
1687     }
1688 
1689     //-----------------------------------------------------------------------
1690     /**
1691      * Writes the object using a
1692      * <a href="{@docRoot}/serialized-form.html#hunt.time.Ser">dedicated serialized form</a>.
1693      * @serialData
1694      * A twos-complement value indicates the remaining values are not _in the stream
1695      * and should be set to zero.
1696      * !(pre)
1697      *  _out.writeByte(4);  // identifies a LocalTime
1698      *  if (nano == 0) {
1699      *    if (second == 0) {
1700      *      if (minute == 0) {
1701      *        _out.writeByte(~hour);
1702      *      } else {
1703      *        _out.writeByte(hour);
1704      *        _out.writeByte(~minute);
1705      *      }
1706      *    } else {
1707      *      _out.writeByte(hour);
1708      *      _out.writeByte(minute);
1709      *      _out.writeByte(~second);
1710      *    }
1711      *  } else {
1712      *    _out.writeByte(hour);
1713      *    _out.writeByte(minute);
1714      *    _out.writeByte(second);
1715      *    _out.writeInt(nano);
1716      *  }
1717      * </pre>
1718      *
1719      * @return the instance of {@code Ser}, not null
1720      */
1721     private Object writeReplace() {
1722         return new Ser(Ser.LOCAL_TIME_TYPE, this);
1723     }
1724 
1725     /**
1726      * Defend against malicious streams.
1727      *
1728      * @param s the stream to read
1729      * @throws InvalidObjectException always
1730      */
1731      ///@gxc
1732     // private void readObject(ObjectInputStream s) /*throws InvalidObjectException*/ {
1733     //     throw new InvalidObjectException("Deserialization via serialization delegate");
1734     // }
1735 
1736     void writeExternal(DataOutput _out) /*throws IOException*/ {
1737         if (nano == 0) {
1738             if (second == 0) {
1739                 if (minute == 0) {
1740                     _out.writeByte(cast(int)hour);
1741                 } else {
1742                     _out.writeByte(cast(int)hour);
1743                     _out.writeByte(cast(int)minute);
1744                 }
1745             } else {
1746                 _out.writeByte(cast(int)hour);
1747                 _out.writeByte(cast(int)minute);
1748                 _out.writeByte(cast(int)second);
1749             }
1750         } else {
1751             _out.writeByte(cast(int)hour);
1752             _out.writeByte(cast(int)minute);
1753             _out.writeByte(cast(int)second);
1754             _out.writeInt(nano);
1755         }
1756     }
1757 
1758     static LocalTime readExternal(DataInput _in) /*throws IOException*/ {
1759         int hour = _in.readByte();
1760         int minute = 0;
1761         int second = 0;
1762         int nano = 0;
1763         if (hour < 0) {
1764             hour = ~hour;
1765         } else {
1766             minute = _in.readByte();
1767             if (minute < 0) {
1768                 minute = ~minute;
1769             } else {
1770                 second = _in.readByte();
1771                 if (second < 0) {
1772                     second = ~second;
1773                 } else {
1774                     nano = _in.readInt();
1775                 }
1776             }
1777         }
1778         return LocalTime.of(hour, minute, second, nano);
1779     }
1780 
1781 
1782     // mixin SerializationMember!(typeof(this));
1783 }