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.OffsetDateTime;
13 
14 import hunt.time.temporal.ChronoField;
15 import hunt.time.temporal.ChronoUnit;
16 import hunt.time.chrono.ChronoLocalDateTime;
17 import hunt.time.chrono.ChronoLocalDate;
18 
19 import hunt.Exceptions;
20 import hunt.stream.ObjectInput;
21 import hunt.stream.ObjectOutput;
22 
23 //import hunt.io.ObjectInputStream;
24 import hunt.stream.Common;
25 import hunt.time.chrono.IsoChronology;
26 // import hunt.time.format.DateTimeFormatter;
27 import hunt.time.format.DateTimeParseException;
28 import hunt.time.temporal.ChronoField;
29 import hunt.time.temporal.ChronoUnit;
30 import hunt.time.temporal.Temporal;
31 import hunt.time.temporal.TemporalAccessor;
32 import hunt.time.temporal.TemporalAdjuster;
33 import hunt.time.temporal.TemporalAmount;
34 import hunt.time.temporal.TemporalField;
35 import hunt.time.temporal.TemporalQueries;
36 import hunt.time.temporal.TemporalQuery;
37 import hunt.time.temporal.TemporalUnit;
38 import hunt.time.Exceptions;
39 import hunt.time.temporal.ValueRange;
40 import hunt.time.zone.ZoneRules;
41 import hunt.util.Comparator;
42 import hunt.Functions;
43 import hunt.time.ZoneId;
44 import hunt.time.Clock;
45 import hunt.time.LocalDate;
46 import hunt.time.LocalTime;
47 import hunt.time.ZoneOffset;
48 import hunt.time.LocalDateTime;
49 import hunt.time.Month;
50 import hunt.time.DayOfWeek;
51 import hunt.time.ZonedDateTime;
52 import hunt.time.Instant;
53 import hunt.time.OffsetTime;
54 import hunt.time.Exceptions;
55 import hunt.time.Ser;
56 import hunt.time.util.QueryHelper;
57 import hunt.time.util.Common;
58 import hunt.Long;
59 import hunt.util.Common;
60 import hunt.util.Comparator;
61 import std.conv;
62 /**
63  * A date-time with an offset from UTC/Greenwich _in the ISO-8601 calendar system,
64  * such as {@code 2007-12-03T10:15:30+01:00}.
65  * !(p)
66  * {@code OffsetDateTime} is an immutable representation of a date-time with an offset.
67  * This class stores all date and time fields, to a precision of nanoseconds,
68  * as well as the offset from UTC/Greenwich. For example, the value
69  * "2nd October 2007 at 13:45:30.123456789 +02:00" can be stored _in an {@code OffsetDateTime}.
70  * !(p)
71  * {@code OffsetDateTime}, {@link hunt.time.ZonedDateTime} and {@link hunt.time.Instant} all store an instant
72  * on the time-line to nanosecond precision.
73  * {@code Instant} is the simplest, simply representing the instant.
74  * {@code OffsetDateTime} adds to the instant the offset from UTC/Greenwich, which allows
75  * the local date-time to be obtained.
76  * {@code ZonedDateTime} adds full time-zone rules.
77  * !(p)
78  * It is intended that {@code ZonedDateTime} or {@code Instant} is used to model data
79  * _in simpler applications. This class may be used when modeling date-time concepts _in
80  * more detail, or when communicating to a database or _in a network protocol.
81  *
82  * !(p)
83  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
84  * class; use of identity-sensitive operations (including reference equality
85  * ({@code ==}), identity hash code, or synchronization) on instances of
86  * {@code OffsetDateTime} may have unpredictable results and should be avoided.
87  * The {@code equals} method should be used for comparisons.
88  *
89  * @implSpec
90  * This class is immutable and thread-safe.
91  *
92  * @since 1.8
93  */
94 public final class OffsetDateTime
95         : Temporal, TemporalAdjuster, Comparable!(OffsetDateTime) { // , Serializable
96 
97     /**
98      * The minimum supported {@code OffsetDateTime}, '-999999999-01-01T00:00:00+18:00'.
99      * This is the local date-time of midnight at the start of the minimum date
100      * _in the maximum offset (larger offsets are earlier on the time-line).
101      * This combines {@link LocalDateTime#MIN} and {@link ZoneOffset#MAX}.
102      * This could be used by an application as a "far past" date-time.
103      */
104     // public __gshared OffsetDateTime MIN ;
105     /**
106      * The maximum supported {@code OffsetDateTime}, '+999999999-12-31T23:59:59.999999999-18:00'.
107      * This is the local date-time just before midnight at the end of the maximum date
108      * _in the minimum offset (larger negative offsets are later on the time-line).
109      * This combines {@link LocalDateTime#MAX} and {@link ZoneOffset#MIN}.
110      * This could be used by an application as a "far future" date-time.
111      */
112     // public __gshared OffsetDateTime MAX ;
113 
114 
115     // shared static this()
116     // {
117         // MIN = LocalDateTime.MIN.atOffset(ZoneOffset.MAX);
118         // mixin(MakeGlobalVar!(OffsetDateTime)("MIN",`LocalDateTime.MIN.atOffset(ZoneOffset.MAX)`));
119         // MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN);
120         // mixin(MakeGlobalVar!(OffsetDateTime)("MAX",`LocalDateTime.MAX.atOffset(ZoneOffset.MIN)`));
121 
122     // }
123     /**
124      * Gets a comparator that compares two {@code OffsetDateTime} instances
125      * based solely on the instant.
126      * !(p)
127      * This method differs from the comparison _in {@link #compareTo} _in that it
128      * only compares the underlying instant.
129      *
130      * @return a comparator that compares _in time-line order
131      *
132      * @see #isAfter
133      * @see #isBefore
134      * @see #isEqual
135      */
136     public static Comparator!(OffsetDateTime) timeLineOrder() {
137         return new class Comparator!(OffsetDateTime) {
138             int compare(OffsetDateTime datetime1, OffsetDateTime datetime2) nothrow
139             {
140                 try {
141                     if (datetime1.getOffset() == (datetime2.getOffset())) {
142                         return datetime1.toLocalDateTime().compareTo(cast(ChronoLocalDateTime!(ChronoLocalDate))(datetime2.toLocalDateTime()));
143                     }
144                     int cmp = hunt.util.Comparator.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
145                     if (cmp == 0) {
146                         cmp = datetime1.toLocalTime().getNano() - datetime2.toLocalTime().getNano();
147                     }
148                     return cmp;
149                 } catch(Exception) {
150                     // FIXME: Needing refactor or cleanup -@zxp at 12/29/2018, 11:26:48 PM
151                     // 
152                     return 0;
153                 }
154             }
155         };
156     }
157 
158     /**
159      * Compares this {@code OffsetDateTime} to another date-time.
160      * The comparison is based on the instant.
161      *
162      * @param datetime1  the first date-time to compare, not null
163      * @param datetime2  the other date-time to compare to, not null
164      * @return the comparator value, negative if less, positive if greater
165      */
166     private static int compareInstant(OffsetDateTime datetime1, OffsetDateTime datetime2) {
167         if (datetime1.getOffset() == (datetime2.getOffset())) {
168             return datetime1.toLocalDateTime().compareTo(cast(ChronoLocalDateTime!(ChronoLocalDate))(datetime2.toLocalDateTime()));
169         }
170         int cmp = compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
171         if (cmp == 0) {
172             cmp = datetime1.toLocalTime().getNano() - datetime2.toLocalTime().getNano();
173         }
174         return cmp;
175     }
176 
177     /**
178      * Serialization version.
179      */
180     private enum long serialVersionUID = 2287754244819255394L;
181 
182     /**
183      * The local date-time.
184      */
185     private  LocalDateTime dateTime;
186     /**
187      * The offset from UTC/Greenwich.
188      */
189     private  ZoneOffset offset;
190 
191     //-----------------------------------------------------------------------
192     /**
193      * Obtains the current date-time from the system clock _in the default time-zone.
194      * !(p)
195      * This will query the {@link Clock#systemDefaultZone() system clock} _in the default
196      * time-zone to obtain the current date-time.
197      * The offset will be calculated from the time-zone _in the clock.
198      * !(p)
199      * Using this method will prevent the ability to use an alternate clock for testing
200      * because the clock is hard-coded.
201      *
202      * @return the current date-time using the system clock, not null
203      */
204     public static OffsetDateTime now() {
205         return now(Clock.systemDefaultZone());
206     }
207 
208     /**
209      * Obtains the current date-time from the system clock _in the specified time-zone.
210      * !(p)
211      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date-time.
212      * Specifying the time-zone avoids dependence on the default time-zone.
213      * The offset will be calculated from the specified time-zone.
214      * !(p)
215      * Using this method will prevent the ability to use an alternate clock for testing
216      * because the clock is hard-coded.
217      *
218      * @param zone  the zone ID to use, not null
219      * @return the current date-time using the system clock, not null
220      */
221     public static OffsetDateTime now(ZoneId zone) {
222         return now(Clock.system(zone));
223     }
224 
225     /**
226      * Obtains the current date-time from the specified clock.
227      * !(p)
228      * This will query the specified clock to obtain the current date-time.
229      * The offset will be calculated from the time-zone _in the clock.
230      * !(p)
231      * Using this method allows the use of an alternate clock for testing.
232      * The alternate clock may be introduced using {@link Clock dependency injection}.
233      *
234      * @param clock  the clock to use, not null
235      * @return the current date-time, not null
236      */
237     public static OffsetDateTime now(Clock clock) {
238         assert(clock, "clock");
239         Instant now = clock.instant();  // called once
240         return ofInstant(now, clock.getZone().getRules().getOffset(now));
241     }
242 
243     //-----------------------------------------------------------------------
244     /**
245      * Obtains an instance of {@code OffsetDateTime} from a date, time and offset.
246      * !(p)
247      * This creates an offset date-time with the specified local date, time and offset.
248      *
249      * @param date  the local date, not null
250      * @param time  the local time, not null
251      * @param offset  the zone offset, not null
252      * @return the offset date-time, not null
253      */
254     public static OffsetDateTime of(LocalDate date, LocalTime time, ZoneOffset offset) {
255         LocalDateTime dt = LocalDateTime.of(date, time);
256         return new OffsetDateTime(dt, offset);
257     }
258 
259     /**
260      * Obtains an instance of {@code OffsetDateTime} from a date-time and offset.
261      * !(p)
262      * This creates an offset date-time with the specified local date-time and offset.
263      *
264      * @param dateTime  the local date-time, not null
265      * @param offset  the zone offset, not null
266      * @return the offset date-time, not null
267      */
268     public static OffsetDateTime of(LocalDateTime dateTime, ZoneOffset offset) {
269         return new OffsetDateTime(dateTime, offset);
270     }
271 
272     /**
273      * Obtains an instance of {@code OffsetDateTime} from a year, month, day,
274      * hour, minute, second, nanosecond and offset.
275      * !(p)
276      * This creates an offset date-time with the seven specified fields.
277      * !(p)
278      * This method exists primarily for writing test cases.
279      * Non test-code will typically use other methods to create an offset time.
280      * {@code LocalDateTime} has five additional convenience variants of the
281      * equivalent factory method taking fewer arguments.
282      * They are not provided here to reduce the footprint of the API.
283      *
284      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
285      * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
286      * @param dayOfMonth  the day-of-month to represent, from 1 to 31
287      * @param hour  the hour-of-day to represent, from 0 to 23
288      * @param minute  the minute-of-hour to represent, from 0 to 59
289      * @param second  the second-of-minute to represent, from 0 to 59
290      * @param nanoOfSecond  the nano-of-second to represent, from 0 to 999,999,999
291      * @param offset  the zone offset, not null
292      * @return the offset date-time, not null
293      * @throws DateTimeException if the value of any field is _out of range, or
294      *  if the day-of-month is invalid for the month-year
295      */
296     public static OffsetDateTime of(
297             int year, int month, int dayOfMonth,
298             int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) {
299         LocalDateTime dt = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
300         return new OffsetDateTime(dt, offset);
301     }
302 
303     //-----------------------------------------------------------------------
304     /**
305      * Obtains an instance of {@code OffsetDateTime} from an {@code Instant} and zone ID.
306      * !(p)
307      * This creates an offset date-time with the same instant as that specified.
308      * Finding the offset from UTC/Greenwich is simple as there is only one valid
309      * offset for each instant.
310      *
311      * @param instant  the instant to create the date-time from, not null
312      * @param zone  the time-zone, which may be an offset, not null
313      * @return the offset date-time, not null
314      * @throws DateTimeException if the result exceeds the supported range
315      */
316     public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
317         assert(instant, "instant");
318         assert(zone, "zone");
319         ZoneRules rules = zone.getRules();
320         ZoneOffset offset = rules.getOffset(instant);
321         LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
322         return new OffsetDateTime(ldt, offset);
323     }
324 
325     //-----------------------------------------------------------------------
326     /**
327      * Obtains an instance of {@code OffsetDateTime} from a temporal object.
328      * !(p)
329      * This obtains an offset date-time based on the specified temporal.
330      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
331      * which this factory converts to an instance of {@code OffsetDateTime}.
332      * !(p)
333      * The conversion will first obtain a {@code ZoneOffset} from the temporal object.
334      * It will then try to obtain a {@code LocalDateTime}, falling back to an {@code Instant} if necessary.
335      * The result will be the combination of {@code ZoneOffset} with either
336      * with {@code LocalDateTime} or {@code Instant}.
337      * Implementations are permitted to perform optimizations such as accessing
338      * those fields that are equivalent to the relevant objects.
339      * !(p)
340      * This method matches the signature of the functional interface {@link TemporalQuery}
341      * allowing it to be used as a query via method reference, {@code OffsetDateTime.from}.
342      *
343      * @param temporal  the temporal object to convert, not null
344      * @return the offset date-time, not null
345      * @throws DateTimeException if unable to convert to an {@code OffsetDateTime}
346      */
347     public static OffsetDateTime from(TemporalAccessor temporal) {
348         if (cast(OffsetDateTime)(temporal) !is null) {
349             return cast(OffsetDateTime) temporal;
350         }
351         try {
352             ZoneOffset offset = ZoneOffset.from(temporal);
353             LocalDate date = QueryHelper.query!LocalDate(temporal,TemporalQueries.localDate());
354             LocalTime time = QueryHelper.query!LocalTime(temporal ,TemporalQueries.localTime());
355             if (date !is null && time !is null) {
356                 return OffsetDateTime.of(date, time, offset);
357             } else {
358                 Instant instant = Instant.from(temporal);
359                 return OffsetDateTime.ofInstant(instant, offset);
360             }
361         } catch (DateTimeException ex) {
362             throw new DateTimeException("Unable to obtain OffsetDateTime from TemporalAccessor: " ~
363                     typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
364         }
365     }
366 
367     //-----------------------------------------------------------------------
368     /**
369      * Obtains an instance of {@code OffsetDateTime} from a text string
370      * such as {@code 2007-12-03T10:15:30+01:00}.
371      * !(p)
372      * The string must represent a valid date-time and is parsed using
373      * {@link hunt.time.format.DateTimeFormatter#ISO_OFFSET_DATE_TIME}.
374      *
375      * @param text  the text to parse such as "2007-12-03T10:15:30+01:00", not null
376      * @return the parsed offset date-time, not null
377      * @throws DateTimeParseException if the text cannot be parsed
378      */
379     // public static OffsetDateTime parse(string text) {
380     //     return parse(text, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
381     // }
382 
383     /**
384      * Obtains an instance of {@code OffsetDateTime} from a text string using a specific formatter.
385      * !(p)
386      * The text is parsed using the formatter, returning a date-time.
387      *
388      * @param text  the text to parse, not null
389      * @param formatter  the formatter to use, not null
390      * @return the parsed offset date-time, not null
391      * @throws DateTimeParseException if the text cannot be parsed
392      */
393     // public static OffsetDateTime parse(string text, DateTimeFormatter formatter) {
394     //     assert(formatter, "formatter");
395     //     return formatter.parse(text, new class TemporalQuery!OffsetDateTime{
396     //         OffsetDateTime queryFrom(TemporalAccessor temporal)
397     //         {
398     //             if (cast(OffsetDateTime)(temporal) !is null) {
399     //                     return cast(OffsetDateTime) temporal;
400     //                 }
401     //                 try {
402     //                     ZoneOffset offset = ZoneOffset.from(temporal);
403     //                     LocalDate date = QueryHelper.query!LocalDate(temporal ,TemporalQueries.localDate());
404     //                     LocalTime time = QueryHelper.query!LocalTime(temporal ,TemporalQueries.localTime());
405     //                     if (date !is null && time !is null) {
406     //                         return OffsetDateTime.of(date, time, offset);
407     //                     } else {
408     //                         Instant instant = Instant.from(temporal);
409     //                         return OffsetDateTime.ofInstant(instant, offset);
410     //                     }
411     //                 } catch (DateTimeException ex) {
412     //                     throw new DateTimeException("Unable to obtain OffsetDateTime from TemporalAccessor: " ~
413     //                             typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
414     //                 }
415     //         }
416     //     });
417     // }
418 
419     //-----------------------------------------------------------------------
420     /**
421      * Constructor.
422      *
423      * @param dateTime  the local date-time, not null
424      * @param offset  the zone offset, not null
425      */
426     private this(LocalDateTime dateTime, ZoneOffset offset) {
427         this.dateTime = dateTime;
428         this.offset = offset;
429     }
430 
431     /**
432      * Returns a new date-time based on this one, returning {@code this} where possible.
433      *
434      * @param dateTime  the date-time to create with, not null
435      * @param offset  the zone offset to create with, not null
436      */
437     private OffsetDateTime _with(LocalDateTime dateTime, ZoneOffset offset) {
438         if (this.dateTime == dateTime && this.offset == (offset)) {
439             return this;
440         }
441         return new OffsetDateTime(dateTime, offset);
442     }
443 
444     //-----------------------------------------------------------------------
445     /**
446      * Checks if the specified field is supported.
447      * !(p)
448      * This checks if this date-time can be queried for the specified field.
449      * If false, then calling the {@link #range(TemporalField) range},
450      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
451      * methods will throw an exception.
452      * !(p)
453      * If the field is a {@link ChronoField} then the query is implemented here.
454      * The supported fields are:
455      * !(ul)
456      * !(li){@code NANO_OF_SECOND}
457      * !(li){@code NANO_OF_DAY}
458      * !(li){@code MICRO_OF_SECOND}
459      * !(li){@code MICRO_OF_DAY}
460      * !(li){@code MILLI_OF_SECOND}
461      * !(li){@code MILLI_OF_DAY}
462      * !(li){@code SECOND_OF_MINUTE}
463      * !(li){@code SECOND_OF_DAY}
464      * !(li){@code MINUTE_OF_HOUR}
465      * !(li){@code MINUTE_OF_DAY}
466      * !(li){@code HOUR_OF_AMPM}
467      * !(li){@code CLOCK_HOUR_OF_AMPM}
468      * !(li){@code HOUR_OF_DAY}
469      * !(li){@code CLOCK_HOUR_OF_DAY}
470      * !(li){@code AMPM_OF_DAY}
471      * !(li){@code DAY_OF_WEEK}
472      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
473      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
474      * !(li){@code DAY_OF_MONTH}
475      * !(li){@code DAY_OF_YEAR}
476      * !(li){@code EPOCH_DAY}
477      * !(li){@code ALIGNED_WEEK_OF_MONTH}
478      * !(li){@code ALIGNED_WEEK_OF_YEAR}
479      * !(li){@code MONTH_OF_YEAR}
480      * !(li){@code PROLEPTIC_MONTH}
481      * !(li){@code YEAR_OF_ERA}
482      * !(li){@code YEAR}
483      * !(li){@code ERA}
484      * !(li){@code INSTANT_SECONDS}
485      * !(li){@code OFFSET_SECONDS}
486      * </ul>
487      * All other {@code ChronoField} instances will return false.
488      * !(p)
489      * If the field is not a {@code ChronoField}, then the result of this method
490      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
491      * passing {@code this} as the argument.
492      * Whether the field is supported is determined by the field.
493      *
494      * @param field  the field to check, null returns false
495      * @return true if the field is supported on this date-time, false if not
496      */
497     override
498     public bool isSupported(TemporalField field) {
499         return cast(ChronoField)(field) !is null || (field !is null && field.isSupportedBy(this));
500     }
501 
502     /**
503      * Checks if the specified unit is supported.
504      * !(p)
505      * This checks if the specified unit can be added to, or subtracted from, this date-time.
506      * If false, then calling the {@link #plus(long, TemporalUnit)} and
507      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
508      * !(p)
509      * If the unit is a {@link ChronoUnit} then the query is implemented here.
510      * The supported units are:
511      * !(ul)
512      * !(li){@code NANOS}
513      * !(li){@code MICROS}
514      * !(li){@code MILLIS}
515      * !(li){@code SECONDS}
516      * !(li){@code MINUTES}
517      * !(li){@code HOURS}
518      * !(li){@code HALF_DAYS}
519      * !(li){@code DAYS}
520      * !(li){@code WEEKS}
521      * !(li){@code MONTHS}
522      * !(li){@code YEARS}
523      * !(li){@code DECADES}
524      * !(li){@code CENTURIES}
525      * !(li){@code MILLENNIA}
526      * !(li){@code ERAS}
527      * </ul>
528      * All other {@code ChronoUnit} instances will return false.
529      * !(p)
530      * If the unit is not a {@code ChronoUnit}, then the result of this method
531      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
532      * passing {@code this} as the argument.
533      * Whether the unit is supported is determined by the unit.
534      *
535      * @param unit  the unit to check, null returns false
536      * @return true if the unit can be added/subtracted, false if not
537      */
538     override  // override for Javadoc
539     public bool isSupported(TemporalUnit unit) {
540         if (cast(ChronoUnit)(unit) !is null) {
541             return unit != ChronoUnit.FOREVER;
542         }
543         return unit !is null && unit.isSupportedBy(this);
544     }
545 
546     //-----------------------------------------------------------------------
547     /**
548      * Gets the range of valid values for the specified field.
549      * !(p)
550      * The range object expresses the minimum and maximum valid values for a field.
551      * This date-time is used to enhance the accuracy of the returned range.
552      * If it is not possible to return the range, because the field is not supported
553      * or for some other reason, an exception is thrown.
554      * !(p)
555      * If the field is a {@link ChronoField} then the query is implemented here.
556      * The {@link #isSupported(TemporalField) supported fields} will return
557      * appropriate range instances.
558      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
559      * !(p)
560      * If the field is not a {@code ChronoField}, then the result of this method
561      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
562      * passing {@code this} as the argument.
563      * Whether the range can be obtained is determined by the field.
564      *
565      * @param field  the field to query the range for, not null
566      * @return the range of valid values for the field, not null
567      * @throws DateTimeException if the range for the field cannot be obtained
568      * @throws UnsupportedTemporalTypeException if the field is not supported
569      */
570     override
571     public ValueRange range(TemporalField field) {
572         if (cast(ChronoField)(field) !is null) {
573             if (field == ChronoField.INSTANT_SECONDS || field == ChronoField.OFFSET_SECONDS) {
574                 return field.range();
575             }
576             return dateTime.range(field);
577         }
578         return field.rangeRefinedBy(this);
579     }
580 
581     /**
582      * Gets the value of the specified field from this date-time as an {@code int}.
583      * !(p)
584      * This queries this date-time for the value of the specified field.
585      * The returned value will always be within the valid range of values for the field.
586      * If it is not possible to return the value, because the field is not supported
587      * or for some other reason, an exception is thrown.
588      * !(p)
589      * If the field is a {@link ChronoField} then the query is implemented here.
590      * The {@link #isSupported(TemporalField) supported fields} will return valid
591      * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
592      * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
593      * large to fit _in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
594      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
595      * !(p)
596      * If the field is not a {@code ChronoField}, then the result of this method
597      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
598      * passing {@code this} as the argument. Whether the value can be obtained,
599      * and what the value represents, is determined by the field.
600      *
601      * @param field  the field to get, not null
602      * @return the value for the field
603      * @throws DateTimeException if a value for the field cannot be obtained or
604      *         the value is outside the range of valid values for the field
605      * @throws UnsupportedTemporalTypeException if the field is not supported or
606      *         the range of values exceeds an {@code int}
607      * @throws ArithmeticException if numeric overflow occurs
608      */
609     override
610     public int get(TemporalField field) {
611         if (cast(ChronoField)(field) !is null) {
612             auto f = cast(ChronoField) field;
613             {
614                 if( f ==  ChronoField.INSTANT_SECONDS)
615                     throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
616                 if( f ==  ChronoField.OFFSET_SECONDS)
617                     return getOffset().getTotalSeconds();
618             }
619             return dateTime.get(field);
620         }
621         return /* Temporal. super.*/super_get(field);
622     }
623     int super_get(TemporalField field) {
624         ValueRange range = range(field);
625         if (range.isIntValue() == false) {
626             throw new UnsupportedTemporalTypeException("Invalid field " ~ typeid(field).name ~ " for get() method, use getLong() instead");
627         }
628         long value = getLong(field);
629         if (range.isValidValue(value) == false) {
630             throw new DateTimeException("Invalid value for " ~ typeid(field).name ~ " (valid values " ~ range.toString ~ "): " ~ value.to!string);
631         }
632         return cast(int) value;
633     }
634 
635     /**
636      * Gets the value of the specified field from this date-time as a {@code long}.
637      * !(p)
638      * This queries this date-time for the value of the specified field.
639      * If it is not possible to return the value, because the field is not supported
640      * or for some other reason, an exception is thrown.
641      * !(p)
642      * If the field is a {@link ChronoField} then the query is implemented here.
643      * The {@link #isSupported(TemporalField) supported fields} will return valid
644      * values based on this date-time.
645      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
646      * !(p)
647      * If the field is not a {@code ChronoField}, then the result of this method
648      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
649      * passing {@code this} as the argument. Whether the value can be obtained,
650      * and what the value represents, is determined by the field.
651      *
652      * @param field  the field to get, not null
653      * @return the value for the field
654      * @throws DateTimeException if a value for the field cannot be obtained
655      * @throws UnsupportedTemporalTypeException if the field is not supported
656      * @throws ArithmeticException if numeric overflow occurs
657      */
658     override
659     public long getLong(TemporalField field) {
660         if (cast(ChronoField)(field) !is null) {
661             auto f =cast(ChronoField) field;
662             {
663                 if( f== ChronoField.INSTANT_SECONDS) return toEpochSecond();
664                 if( f== ChronoField.OFFSET_SECONDS) return getOffset().getTotalSeconds();
665             }
666             return dateTime.getLong(field);
667         }
668         return field.getFrom(this);
669     }
670 
671     //-----------------------------------------------------------------------
672     /**
673      * Gets the zone offset, such as '+01:00'.
674      * !(p)
675      * This is the offset of the local date-time from UTC/Greenwich.
676      *
677      * @return the zone offset, not null
678      */
679     public ZoneOffset getOffset() {
680         return offset;
681     }
682 
683     /**
684      * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring
685      * that the result has the same local date-time.
686      * !(p)
687      * This method returns an object with the same {@code LocalDateTime} and the specified {@code ZoneOffset}.
688      * No calculation is needed or performed.
689      * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is
690      * {@code +03:00}, then this method will return {@code 2007-12-03T10:30+03:00}.
691      * !(p)
692      * To take into account the difference between the offsets, and adjust the time fields,
693      * use {@link #withOffsetSameInstant}.
694      * !(p)
695      * This instance is immutable and unaffected by this method call.
696      *
697      * @param offset  the zone offset to change to, not null
698      * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null
699      */
700     public OffsetDateTime withOffsetSameLocal(ZoneOffset offset) {
701         return _with(dateTime, offset);
702     }
703 
704     /**
705      * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring
706      * that the result is at the same instant.
707      * !(p)
708      * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalDateTime}
709      * adjusted by the difference between the two offsets.
710      * This will result _in the old and new objects representing the same instant.
711      * This is useful for finding the local time _in a different offset.
712      * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is
713      * {@code +03:00}, then this method will return {@code 2007-12-03T11:30+03:00}.
714      * !(p)
715      * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}.
716      * !(p)
717      * This instance is immutable and unaffected by this method call.
718      *
719      * @param offset  the zone offset to change to, not null
720      * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null
721      * @throws DateTimeException if the result exceeds the supported date range
722      */
723     public OffsetDateTime withOffsetSameInstant(ZoneOffset offset) {
724         if (offset == (this.offset)) {
725             return this;
726         }
727         int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds();
728         LocalDateTime adjusted = dateTime.plusSeconds(difference);
729         return new OffsetDateTime(adjusted, offset);
730     }
731 
732     //-----------------------------------------------------------------------
733     /**
734      * Gets the {@code LocalDateTime} part of this date-time.
735      * !(p)
736      * This returns a {@code LocalDateTime} with the same year, month, day and time
737      * as this date-time.
738      *
739      * @return the local date-time part of this date-time, not null
740      */
741     public LocalDateTime toLocalDateTime() {
742         return dateTime;
743     }
744 
745     //-----------------------------------------------------------------------
746     /**
747      * Gets the {@code LocalDate} part of this date-time.
748      * !(p)
749      * This returns a {@code LocalDate} with the same year, month and day
750      * as this date-time.
751      *
752      * @return the date part of this date-time, not null
753      */
754     public LocalDate toLocalDate() {
755         return dateTime.toLocalDate();
756     }
757 
758     /**
759      * Gets the year field.
760      * !(p)
761      * This method returns the primitive {@code int} value for the year.
762      * !(p)
763      * The year returned by this method is proleptic as per {@code get(YEAR)}.
764      * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
765      *
766      * @return the year, from MIN_YEAR to MAX_YEAR
767      */
768     public int getYear() {
769         return dateTime.getYear();
770     }
771 
772     /**
773      * Gets the month-of-year field from 1 to 12.
774      * !(p)
775      * This method returns the month as an {@code int} from 1 to 12.
776      * Application code is frequently clearer if the enum {@link Month}
777      * is used by calling {@link #getMonth()}.
778      *
779      * @return the month-of-year, from 1 to 12
780      * @see #getMonth()
781      */
782     public int getMonthValue() {
783         return dateTime.getMonthValue();
784     }
785 
786     /**
787      * Gets the month-of-year field using the {@code Month} enum.
788      * !(p)
789      * This method returns the enum {@link Month} for the month.
790      * This avoids confusion as to what {@code int} values mean.
791      * If you need access to the primitive {@code int} value then the enum
792      * provides the {@link Month#getValue() int value}.
793      *
794      * @return the month-of-year, not null
795      * @see #getMonthValue()
796      */
797     public Month getMonth() {
798         return dateTime.getMonth();
799     }
800 
801     /**
802      * Gets the day-of-month field.
803      * !(p)
804      * This method returns the primitive {@code int} value for the day-of-month.
805      *
806      * @return the day-of-month, from 1 to 31
807      */
808     public int getDayOfMonth() {
809         return dateTime.getDayOfMonth();
810     }
811 
812     /**
813      * Gets the day-of-year field.
814      * !(p)
815      * This method returns the primitive {@code int} value for the day-of-year.
816      *
817      * @return the day-of-year, from 1 to 365, or 366 _in a leap year
818      */
819     public int getDayOfYear() {
820         return dateTime.getDayOfYear();
821     }
822 
823     /**
824      * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
825      * !(p)
826      * This method returns the enum {@link DayOfWeek} for the day-of-week.
827      * This avoids confusion as to what {@code int} values mean.
828      * If you need access to the primitive {@code int} value then the enum
829      * provides the {@link DayOfWeek#getValue() int value}.
830      * !(p)
831      * Additional information can be obtained from the {@code DayOfWeek}.
832      * This includes textual names of the values.
833      *
834      * @return the day-of-week, not null
835      */
836     public DayOfWeek getDayOfWeek() {
837         return dateTime.getDayOfWeek();
838     }
839 
840     //-----------------------------------------------------------------------
841     /**
842      * Gets the {@code LocalTime} part of this date-time.
843      * !(p)
844      * This returns a {@code LocalTime} with the same hour, minute, second and
845      * nanosecond as this date-time.
846      *
847      * @return the time part of this date-time, not null
848      */
849     public LocalTime toLocalTime() {
850         return dateTime.toLocalTime();
851     }
852 
853     /**
854      * Gets the hour-of-day field.
855      *
856      * @return the hour-of-day, from 0 to 23
857      */
858     public int getHour() {
859         return dateTime.getHour();
860     }
861 
862     /**
863      * Gets the minute-of-hour field.
864      *
865      * @return the minute-of-hour, from 0 to 59
866      */
867     public int getMinute() {
868         return dateTime.getMinute();
869     }
870 
871     /**
872      * Gets the second-of-minute field.
873      *
874      * @return the second-of-minute, from 0 to 59
875      */
876     public int getSecond() {
877         return dateTime.getSecond();
878     }
879 
880     /**
881      * Gets the nano-of-second field.
882      *
883      * @return the nano-of-second, from 0 to 999,999,999
884      */
885     public int getNano() {
886         return dateTime.getNano();
887     }
888 
889     //-----------------------------------------------------------------------
890     /**
891      * Returns an adjusted copy of this date-time.
892      * !(p)
893      * This returns an {@code OffsetDateTime}, based on this one, with the date-time adjusted.
894      * The adjustment takes place using the specified adjuster strategy object.
895      * Read the documentation of the adjuster to understand what adjustment will be made.
896      * !(p)
897      * A simple adjuster might simply set the one of the fields, such as the year field.
898      * A more complex adjuster might set the date to the last day of the month.
899      * A selection of common adjustments is provided _in
900      * {@link hunt.time.temporal.TemporalAdjusters TemporalAdjusters}.
901      * These include finding the "last day of the month" and "next Wednesday".
902      * Key date-time classes also implement the {@code TemporalAdjuster} interface,
903      * such as {@link Month} and {@link hunt.time.MonthDay MonthDay}.
904      * The adjuster is responsible for handling special cases, such as the varying
905      * lengths of month and leap years.
906      * !(p)
907      * For example this code returns a date on the last day of July:
908      * !(pre)
909      *  import hunt.time.Month.*;
910      *  import hunt.time.temporal.TemporalAdjusters.*;
911      *
912      *  result = offsetDateTime._with(JULY)._with(lastDayOfMonth());
913      * </pre>
914      * !(p)
915      * The classes {@link LocalDate}, {@link LocalTime} and {@link ZoneOffset} implement
916      * {@code TemporalAdjuster}, thus this method can be used to change the date, time or offset:
917      * !(pre)
918      *  result = offsetDateTime._with(date);
919      *  result = offsetDateTime._with(time);
920      *  result = offsetDateTime._with(offset);
921      * </pre>
922      * !(p)
923      * The result of this method is obtained by invoking the
924      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
925      * specified adjuster passing {@code this} as the argument.
926      * !(p)
927      * This instance is immutable and unaffected by this method call.
928      *
929      * @param adjuster the adjuster to use, not null
930      * @return an {@code OffsetDateTime} based on {@code this} with the adjustment made, not null
931      * @throws DateTimeException if the adjustment cannot be made
932      * @throws ArithmeticException if numeric overflow occurs
933      */
934     override
935     public OffsetDateTime _with(TemporalAdjuster adjuster) {
936         // optimizations
937         if (cast(LocalDate)(adjuster) !is null || cast(LocalTime)(adjuster) !is null || cast(LocalDateTime)(adjuster) !is null) {
938             return _with(dateTime._with(adjuster), offset);
939         } else if (cast(Instant)(adjuster) !is null) {
940             return ofInstant(cast(Instant) adjuster, offset);
941         } else if (cast(ZoneOffset)(adjuster) !is null) {
942             return _with(dateTime, cast(ZoneOffset) adjuster);
943         } else if (cast(OffsetDateTime)(adjuster) !is null) {
944             return cast(OffsetDateTime) adjuster;
945         }
946         return cast(OffsetDateTime) adjuster.adjustInto(this);
947     }
948 
949     /**
950      * Returns a copy of this date-time with the specified field set to a new value.
951      * !(p)
952      * This returns an {@code OffsetDateTime}, based on this one, with the value
953      * for the specified field changed.
954      * This can be used to change any supported field, such as the year, month or day-of-month.
955      * If it is not possible to set the value, because the field is not supported or for
956      * some other reason, an exception is thrown.
957      * !(p)
958      * In some cases, changing the specified field can cause the resulting date-time to become invalid,
959      * such as changing the month from 31st January to February would make the day-of-month invalid.
960      * In cases like this, the field is responsible for resolving the date. Typically it will choose
961      * the previous valid date, which would be the last valid day of February _in this example.
962      * !(p)
963      * If the field is a {@link ChronoField} then the adjustment is implemented here.
964      * !(p)
965      * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
966      * The offset and nano-of-second are unchanged.
967      * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
968      * !(p)
969      * The {@code OFFSET_SECONDS} field will return a date-time with the specified offset.
970      * The local date-time is unaltered. If the new offset value is outside the valid range
971      * then a {@code DateTimeException} will be thrown.
972      * !(p)
973      * The other {@link #isSupported(TemporalField) supported fields} will behave as per
974      * the matching method on {@link LocalDateTime#_with(TemporalField, long) LocalDateTime}.
975      * In this case, the offset is not part of the calculation and will be unchanged.
976      * !(p)
977      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
978      * !(p)
979      * If the field is not a {@code ChronoField}, then the result of this method
980      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
981      * passing {@code this} as the argument. In this case, the field determines
982      * whether and how to adjust the instant.
983      * !(p)
984      * This instance is immutable and unaffected by this method call.
985      *
986      * @param field  the field to set _in the result, not null
987      * @param newValue  the new value of the field _in the result
988      * @return an {@code OffsetDateTime} based on {@code this} with the specified field set, not null
989      * @throws DateTimeException if the field cannot be set
990      * @throws UnsupportedTemporalTypeException if the field is not supported
991      * @throws ArithmeticException if numeric overflow occurs
992      */
993     override
994     public OffsetDateTime _with(TemporalField field, long newValue) {
995         if (cast(ChronoField)(field) !is null) {
996             ChronoField f = cast(ChronoField) field;
997             {
998                  if( f == ChronoField.INSTANT_SECONDS) return ofInstant(Instant.ofEpochSecond(newValue, getNano()), offset);
999                  if( f == ChronoField.OFFSET_SECONDS) {
1000                     return _with(dateTime, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue)));
1001                 }
1002             }
1003             return _with(dateTime._with(field, newValue), offset);
1004         }
1005         return cast(OffsetDateTime)(field.adjustInto(this, newValue));
1006     }
1007 
1008     //-----------------------------------------------------------------------
1009     /**
1010      * Returns a copy of this {@code OffsetDateTime} with the year altered.
1011      * !(p)
1012      * The time and offset do not affect the calculation and will be the same _in the result.
1013      * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
1014      * !(p)
1015      * This instance is immutable and unaffected by this method call.
1016      *
1017      * @param year  the year to set _in the result, from MIN_YEAR to MAX_YEAR
1018      * @return an {@code OffsetDateTime} based on this date-time with the requested year, not null
1019      * @throws DateTimeException if the year value is invalid
1020      */
1021     public OffsetDateTime withYear(int year) {
1022         return _with(dateTime.withYear(year), offset);
1023     }
1024 
1025     /**
1026      * Returns a copy of this {@code OffsetDateTime} with the month-of-year altered.
1027      * !(p)
1028      * The time and offset do not affect the calculation and will be the same _in the result.
1029      * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
1030      * !(p)
1031      * This instance is immutable and unaffected by this method call.
1032      *
1033      * @param month  the month-of-year to set _in the result, from 1 (January) to 12 (December)
1034      * @return an {@code OffsetDateTime} based on this date-time with the requested month, not null
1035      * @throws DateTimeException if the month-of-year value is invalid
1036      */
1037     public OffsetDateTime withMonth(int month) {
1038         return _with(dateTime.withMonth(month), offset);
1039     }
1040 
1041     /**
1042      * Returns a copy of this {@code OffsetDateTime} with the day-of-month altered.
1043      * !(p)
1044      * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown.
1045      * The time and offset do not affect the calculation and will be the same _in the result.
1046      * !(p)
1047      * This instance is immutable and unaffected by this method call.
1048      *
1049      * @param dayOfMonth  the day-of-month to set _in the result, from 1 to 28-31
1050      * @return an {@code OffsetDateTime} based on this date-time with the requested day, not null
1051      * @throws DateTimeException if the day-of-month value is invalid,
1052      *  or if the day-of-month is invalid for the month-year
1053      */
1054     public OffsetDateTime withDayOfMonth(int dayOfMonth) {
1055         return _with(dateTime.withDayOfMonth(dayOfMonth), offset);
1056     }
1057 
1058     /**
1059      * Returns a copy of this {@code OffsetDateTime} with the day-of-year altered.
1060      * !(p)
1061      * The time and offset do not affect the calculation and will be the same _in the result.
1062      * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown.
1063      * !(p)
1064      * This instance is immutable and unaffected by this method call.
1065      *
1066      * @param dayOfYear  the day-of-year to set _in the result, from 1 to 365-366
1067      * @return an {@code OffsetDateTime} based on this date with the requested day, not null
1068      * @throws DateTimeException if the day-of-year value is invalid,
1069      *  or if the day-of-year is invalid for the year
1070      */
1071     public OffsetDateTime withDayOfYear(int dayOfYear) {
1072         return _with(dateTime.withDayOfYear(dayOfYear), offset);
1073     }
1074 
1075     //-----------------------------------------------------------------------
1076     /**
1077      * Returns a copy of this {@code OffsetDateTime} with the hour-of-day altered.
1078      * !(p)
1079      * The date and offset do not affect the calculation and will be the same _in the result.
1080      * !(p)
1081      * This instance is immutable and unaffected by this method call.
1082      *
1083      * @param hour  the hour-of-day to set _in the result, from 0 to 23
1084      * @return an {@code OffsetDateTime} based on this date-time with the requested hour, not null
1085      * @throws DateTimeException if the hour value is invalid
1086      */
1087     public OffsetDateTime withHour(int hour) {
1088         return _with(dateTime.withHour(hour), offset);
1089     }
1090 
1091     /**
1092      * Returns a copy of this {@code OffsetDateTime} with the minute-of-hour altered.
1093      * !(p)
1094      * The date and offset do not affect the calculation and will be the same _in the result.
1095      * !(p)
1096      * This instance is immutable and unaffected by this method call.
1097      *
1098      * @param minute  the minute-of-hour to set _in the result, from 0 to 59
1099      * @return an {@code OffsetDateTime} based on this date-time with the requested minute, not null
1100      * @throws DateTimeException if the minute value is invalid
1101      */
1102     public OffsetDateTime withMinute(int minute) {
1103         return _with(dateTime.withMinute(minute), offset);
1104     }
1105 
1106     /**
1107      * Returns a copy of this {@code OffsetDateTime} with the second-of-minute altered.
1108      * !(p)
1109      * The date and offset do not affect the calculation and will be the same _in the result.
1110      * !(p)
1111      * This instance is immutable and unaffected by this method call.
1112      *
1113      * @param second  the second-of-minute to set _in the result, from 0 to 59
1114      * @return an {@code OffsetDateTime} based on this date-time with the requested second, not null
1115      * @throws DateTimeException if the second value is invalid
1116      */
1117     public OffsetDateTime withSecond(int second) {
1118         return _with(dateTime.withSecond(second), offset);
1119     }
1120 
1121     /**
1122      * Returns a copy of this {@code OffsetDateTime} with the nano-of-second altered.
1123      * !(p)
1124      * The date and offset do not affect the calculation and will be the same _in the result.
1125      * !(p)
1126      * This instance is immutable and unaffected by this method call.
1127      *
1128      * @param nanoOfSecond  the nano-of-second to set _in the result, from 0 to 999,999,999
1129      * @return an {@code OffsetDateTime} based on this date-time with the requested nanosecond, not null
1130      * @throws DateTimeException if the nano value is invalid
1131      */
1132     public OffsetDateTime withNano(int nanoOfSecond) {
1133         return _with(dateTime.withNano(nanoOfSecond), offset);
1134     }
1135 
1136     //-----------------------------------------------------------------------
1137     /**
1138      * Returns a copy of this {@code OffsetDateTime} with the time truncated.
1139      * !(p)
1140      * Truncation returns a copy of the original date-time with fields
1141      * smaller than the specified unit set to zero.
1142      * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
1143      * will set the second-of-minute and nano-of-second field to zero.
1144      * !(p)
1145      * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
1146      * that divides into the length of a standard day without remainder.
1147      * This includes all supplied time units on {@link ChronoUnit} and
1148      * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
1149      * !(p)
1150      * The offset does not affect the calculation and will be the same _in the result.
1151      * !(p)
1152      * This instance is immutable and unaffected by this method call.
1153      *
1154      * @param unit  the unit to truncate to, not null
1155      * @return an {@code OffsetDateTime} based on this date-time with the time truncated, not null
1156      * @throws DateTimeException if unable to truncate
1157      * @throws UnsupportedTemporalTypeException if the unit is not supported
1158      */
1159     public OffsetDateTime truncatedTo(TemporalUnit unit) {
1160         return _with(dateTime.truncatedTo(unit), offset);
1161     }
1162 
1163     //-----------------------------------------------------------------------
1164     /**
1165      * Returns a copy of this date-time with the specified amount added.
1166      * !(p)
1167      * This returns an {@code OffsetDateTime}, based on this one, with the specified amount added.
1168      * The amount is typically {@link Period} or {@link Duration} but may be
1169      * any other type implementing the {@link TemporalAmount} interface.
1170      * !(p)
1171      * The calculation is delegated to the amount object by calling
1172      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
1173      * to implement the addition _in any way it wishes, however it typically
1174      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
1175      * of the amount implementation to determine if it can be successfully added.
1176      * !(p)
1177      * This instance is immutable and unaffected by this method call.
1178      *
1179      * @param amountToAdd  the amount to add, not null
1180      * @return an {@code OffsetDateTime} based on this date-time with the addition made, not null
1181      * @throws DateTimeException if the addition cannot be made
1182      * @throws ArithmeticException if numeric overflow occurs
1183      */
1184     override
1185     public OffsetDateTime plus(TemporalAmount amountToAdd) {
1186         return cast(OffsetDateTime) amountToAdd.addTo(this);
1187     }
1188 
1189     /**
1190      * Returns a copy of this date-time with the specified amount added.
1191      * !(p)
1192      * This returns an {@code OffsetDateTime}, based on this one, with the amount
1193      * _in terms of the unit added. If it is not possible to add the amount, because the
1194      * unit is not supported or for some other reason, an exception is thrown.
1195      * !(p)
1196      * If the field is a {@link ChronoUnit} then the addition is implemented by
1197      * {@link LocalDateTime#plus(long, TemporalUnit)}.
1198      * The offset is not part of the calculation and will be unchanged _in the result.
1199      * !(p)
1200      * If the field is not a {@code ChronoUnit}, then the result of this method
1201      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
1202      * passing {@code this} as the argument. In this case, the unit determines
1203      * whether and how to perform the addition.
1204      * !(p)
1205      * This instance is immutable and unaffected by this method call.
1206      *
1207      * @param amountToAdd  the amount of the unit to add to the result, may be negative
1208      * @param unit  the unit of the amount to add, not null
1209      * @return an {@code OffsetDateTime} based on this date-time with the specified amount added, not null
1210      * @throws DateTimeException if the addition cannot be made
1211      * @throws UnsupportedTemporalTypeException if the unit is not supported
1212      * @throws ArithmeticException if numeric overflow occurs
1213      */
1214     override
1215     public OffsetDateTime plus(long amountToAdd, TemporalUnit unit) {
1216         if (cast(ChronoUnit)(unit) !is null) {
1217             return _with(dateTime.plus(amountToAdd, unit), offset);
1218         }
1219         return cast(OffsetDateTime)(unit.addTo(this, amountToAdd));
1220     }
1221 
1222     //-----------------------------------------------------------------------
1223     /**
1224      * Returns a copy of this {@code OffsetDateTime} with the specified number of years added.
1225      * !(p)
1226      * This method adds the specified amount to the years field _in three steps:
1227      * !(ol)
1228      * !(li)Add the input years to the year field</li>
1229      * !(li)Check if the resulting date would be invalid</li>
1230      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1231      * </ol>
1232      * !(p)
1233      * For example, 2008-02-29 (leap year) plus one year would result _in the
1234      * invalid date 2009-02-29 (standard year). Instead of returning an invalid
1235      * result, the last valid day of the month, 2009-02-28, is selected instead.
1236      * !(p)
1237      * This instance is immutable and unaffected by this method call.
1238      *
1239      * @param years  the years to add, may be negative
1240      * @return an {@code OffsetDateTime} based on this date-time with the years added, not null
1241      * @throws DateTimeException if the result exceeds the supported date range
1242      */
1243     public OffsetDateTime plusYears(long years) {
1244         return _with(dateTime.plusYears(years), offset);
1245     }
1246 
1247     /**
1248      * Returns a copy of this {@code OffsetDateTime} with the specified number of months added.
1249      * !(p)
1250      * This method adds the specified amount to the months field _in three steps:
1251      * !(ol)
1252      * !(li)Add the input months to the month-of-year field</li>
1253      * !(li)Check if the resulting date would be invalid</li>
1254      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1255      * </ol>
1256      * !(p)
1257      * For example, 2007-03-31 plus one month would result _in the invalid date
1258      * 2007-04-31. Instead of returning an invalid result, the last valid day
1259      * of the month, 2007-04-30, is selected instead.
1260      * !(p)
1261      * This instance is immutable and unaffected by this method call.
1262      *
1263      * @param months  the months to add, may be negative
1264      * @return an {@code OffsetDateTime} based on this date-time with the months added, not null
1265      * @throws DateTimeException if the result exceeds the supported date range
1266      */
1267     public OffsetDateTime plusMonths(long months) {
1268         return _with(dateTime.plusMonths(months), offset);
1269     }
1270 
1271     /**
1272      * Returns a copy of this OffsetDateTime with the specified number of weeks added.
1273      * !(p)
1274      * This method adds the specified amount _in weeks to the days field incrementing
1275      * the month and year fields as necessary to ensure the result remains valid.
1276      * The result is only invalid if the maximum/minimum year is exceeded.
1277      * !(p)
1278      * For example, 2008-12-31 plus one week would result _in 2009-01-07.
1279      * !(p)
1280      * This instance is immutable and unaffected by this method call.
1281      *
1282      * @param weeks  the weeks to add, may be negative
1283      * @return an {@code OffsetDateTime} based on this date-time with the weeks added, not null
1284      * @throws DateTimeException if the result exceeds the supported date range
1285      */
1286     public OffsetDateTime plusWeeks(long weeks) {
1287         return _with(dateTime.plusWeeks(weeks), offset);
1288     }
1289 
1290     /**
1291      * Returns a copy of this OffsetDateTime with the specified number of days added.
1292      * !(p)
1293      * This method adds the specified amount to the days field incrementing the
1294      * month and year fields as necessary to ensure the result remains valid.
1295      * The result is only invalid if the maximum/minimum year is exceeded.
1296      * !(p)
1297      * For example, 2008-12-31 plus one day would result _in 2009-01-01.
1298      * !(p)
1299      * This instance is immutable and unaffected by this method call.
1300      *
1301      * @param days  the days to add, may be negative
1302      * @return an {@code OffsetDateTime} based on this date-time with the days added, not null
1303      * @throws DateTimeException if the result exceeds the supported date range
1304      */
1305     public OffsetDateTime plusDays(long days) {
1306         return _with(dateTime.plusDays(days), offset);
1307     }
1308 
1309     /**
1310      * Returns a copy of this {@code OffsetDateTime} with the specified number of hours added.
1311      * !(p)
1312      * This instance is immutable and unaffected by this method call.
1313      *
1314      * @param hours  the hours to add, may be negative
1315      * @return an {@code OffsetDateTime} based on this date-time with the hours added, not null
1316      * @throws DateTimeException if the result exceeds the supported date range
1317      */
1318     public OffsetDateTime plusHours(long hours) {
1319         return _with(dateTime.plusHours(hours), offset);
1320     }
1321 
1322     /**
1323      * Returns a copy of this {@code OffsetDateTime} with the specified number of minutes added.
1324      * !(p)
1325      * This instance is immutable and unaffected by this method call.
1326      *
1327      * @param minutes  the minutes to add, may be negative
1328      * @return an {@code OffsetDateTime} based on this date-time with the minutes added, not null
1329      * @throws DateTimeException if the result exceeds the supported date range
1330      */
1331     public OffsetDateTime plusMinutes(long minutes) {
1332         return _with(dateTime.plusMinutes(minutes), offset);
1333     }
1334 
1335     /**
1336      * Returns a copy of this {@code OffsetDateTime} with the specified number of seconds added.
1337      * !(p)
1338      * This instance is immutable and unaffected by this method call.
1339      *
1340      * @param seconds  the seconds to add, may be negative
1341      * @return an {@code OffsetDateTime} based on this date-time with the seconds added, not null
1342      * @throws DateTimeException if the result exceeds the supported date range
1343      */
1344     public OffsetDateTime plusSeconds(long seconds) {
1345         return _with(dateTime.plusSeconds(seconds), offset);
1346     }
1347 
1348     /**
1349      * Returns a copy of this {@code OffsetDateTime} with the specified number of nanoseconds added.
1350      * !(p)
1351      * This instance is immutable and unaffected by this method call.
1352      *
1353      * @param nanos  the nanos to add, may be negative
1354      * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds added, not null
1355      * @throws DateTimeException if the unit cannot be added to this type
1356      */
1357     public OffsetDateTime plusNanos(long nanos) {
1358         return _with(dateTime.plusNanos(nanos), offset);
1359     }
1360 
1361     //-----------------------------------------------------------------------
1362     /**
1363      * Returns a copy of this date-time with the specified amount subtracted.
1364      * !(p)
1365      * This returns an {@code OffsetDateTime}, based on this one, with the specified amount subtracted.
1366      * The amount is typically {@link Period} or {@link Duration} but may be
1367      * any other type implementing the {@link TemporalAmount} interface.
1368      * !(p)
1369      * The calculation is delegated to the amount object by calling
1370      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
1371      * to implement the subtraction _in any way it wishes, however it typically
1372      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
1373      * of the amount implementation to determine if it can be successfully subtracted.
1374      * !(p)
1375      * This instance is immutable and unaffected by this method call.
1376      *
1377      * @param amountToSubtract  the amount to subtract, not null
1378      * @return an {@code OffsetDateTime} based on this date-time with the subtraction made, not null
1379      * @throws DateTimeException if the subtraction cannot be made
1380      * @throws ArithmeticException if numeric overflow occurs
1381      */
1382     override
1383     public OffsetDateTime minus(TemporalAmount amountToSubtract) {
1384         return cast(OffsetDateTime) amountToSubtract.subtractFrom(this);
1385     }
1386 
1387     /**
1388      * Returns a copy of this date-time with the specified amount subtracted.
1389      * !(p)
1390      * This returns an {@code OffsetDateTime}, based on this one, with the amount
1391      * _in terms of the unit subtracted. If it is not possible to subtract the amount,
1392      * because the unit is not supported or for some other reason, an exception is thrown.
1393      * !(p)
1394      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
1395      * See that method for a full description of how addition, and thus subtraction, works.
1396      * !(p)
1397      * This instance is immutable and unaffected by this method call.
1398      *
1399      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
1400      * @param unit  the unit of the amount to subtract, not null
1401      * @return an {@code OffsetDateTime} based on this date-time with the specified amount subtracted, not null
1402      * @throws DateTimeException if the subtraction cannot be made
1403      * @throws UnsupportedTemporalTypeException if the unit is not supported
1404      * @throws ArithmeticException if numeric overflow occurs
1405      */
1406     override
1407     public OffsetDateTime minus(long amountToSubtract, TemporalUnit unit) {
1408         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
1409     }
1410 
1411     //-----------------------------------------------------------------------
1412     /**
1413      * Returns a copy of this {@code OffsetDateTime} with the specified number of years subtracted.
1414      * !(p)
1415      * This method subtracts the specified amount from the years field _in three steps:
1416      * !(ol)
1417      * !(li)Subtract the input years from the year field</li>
1418      * !(li)Check if the resulting date would be invalid</li>
1419      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1420      * </ol>
1421      * !(p)
1422      * For example, 2008-02-29 (leap year) minus one year would result _in the
1423      * invalid date 2007-02-29 (standard year). Instead of returning an invalid
1424      * result, the last valid day of the month, 2007-02-28, is selected instead.
1425      * !(p)
1426      * This instance is immutable and unaffected by this method call.
1427      *
1428      * @param years  the years to subtract, may be negative
1429      * @return an {@code OffsetDateTime} based on this date-time with the years subtracted, not null
1430      * @throws DateTimeException if the result exceeds the supported date range
1431      */
1432     public OffsetDateTime minusYears(long years) {
1433         return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
1434     }
1435 
1436     /**
1437      * Returns a copy of this {@code OffsetDateTime} with the specified number of months subtracted.
1438      * !(p)
1439      * This method subtracts the specified amount from the months field _in three steps:
1440      * !(ol)
1441      * !(li)Subtract the input months from the month-of-year field</li>
1442      * !(li)Check if the resulting date would be invalid</li>
1443      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1444      * </ol>
1445      * !(p)
1446      * For example, 2007-03-31 minus one month would result _in the invalid date
1447      * 2007-02-31. Instead of returning an invalid result, the last valid day
1448      * of the month, 2007-02-28, is selected instead.
1449      * !(p)
1450      * This instance is immutable and unaffected by this method call.
1451      *
1452      * @param months  the months to subtract, may be negative
1453      * @return an {@code OffsetDateTime} based on this date-time with the months subtracted, not null
1454      * @throws DateTimeException if the result exceeds the supported date range
1455      */
1456     public OffsetDateTime minusMonths(long months) {
1457         return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
1458     }
1459 
1460     /**
1461      * Returns a copy of this {@code OffsetDateTime} with the specified number of weeks subtracted.
1462      * !(p)
1463      * This method subtracts the specified amount _in weeks from the days field decrementing
1464      * the month and year fields as necessary to ensure the result remains valid.
1465      * The result is only invalid if the maximum/minimum year is exceeded.
1466      * !(p)
1467      * For example, 2009-01-07 minus one week would result _in 2008-12-31.
1468      * !(p)
1469      * This instance is immutable and unaffected by this method call.
1470      *
1471      * @param weeks  the weeks to subtract, may be negative
1472      * @return an {@code OffsetDateTime} based on this date-time with the weeks subtracted, not null
1473      * @throws DateTimeException if the result exceeds the supported date range
1474      */
1475     public OffsetDateTime minusWeeks(long weeks) {
1476         return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
1477     }
1478 
1479     /**
1480      * Returns a copy of this {@code OffsetDateTime} with the specified number of days subtracted.
1481      * !(p)
1482      * This method subtracts the specified amount from the days field decrementing the
1483      * month and year fields as necessary to ensure the result remains valid.
1484      * The result is only invalid if the maximum/minimum year is exceeded.
1485      * !(p)
1486      * For example, 2009-01-01 minus one day would result _in 2008-12-31.
1487      * !(p)
1488      * This instance is immutable and unaffected by this method call.
1489      *
1490      * @param days  the days to subtract, may be negative
1491      * @return an {@code OffsetDateTime} based on this date-time with the days subtracted, not null
1492      * @throws DateTimeException if the result exceeds the supported date range
1493      */
1494     public OffsetDateTime minusDays(long days) {
1495         return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
1496     }
1497 
1498     /**
1499      * Returns a copy of this {@code OffsetDateTime} with the specified number of hours subtracted.
1500      * !(p)
1501      * This instance is immutable and unaffected by this method call.
1502      *
1503      * @param hours  the hours to subtract, may be negative
1504      * @return an {@code OffsetDateTime} based on this date-time with the hours subtracted, not null
1505      * @throws DateTimeException if the result exceeds the supported date range
1506      */
1507     public OffsetDateTime minusHours(long hours) {
1508         return (hours == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hours));
1509     }
1510 
1511     /**
1512      * Returns a copy of this {@code OffsetDateTime} with the specified number of minutes subtracted.
1513      * !(p)
1514      * This instance is immutable and unaffected by this method call.
1515      *
1516      * @param minutes  the minutes to subtract, may be negative
1517      * @return an {@code OffsetDateTime} based on this date-time with the minutes subtracted, not null
1518      * @throws DateTimeException if the result exceeds the supported date range
1519      */
1520     public OffsetDateTime minusMinutes(long minutes) {
1521         return (minutes == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutes));
1522     }
1523 
1524     /**
1525      * Returns a copy of this {@code OffsetDateTime} with the specified number of seconds subtracted.
1526      * !(p)
1527      * This instance is immutable and unaffected by this method call.
1528      *
1529      * @param seconds  the seconds to subtract, may be negative
1530      * @return an {@code OffsetDateTime} based on this date-time with the seconds subtracted, not null
1531      * @throws DateTimeException if the result exceeds the supported date range
1532      */
1533     public OffsetDateTime minusSeconds(long seconds) {
1534         return (seconds == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-seconds));
1535     }
1536 
1537     /**
1538      * Returns a copy of this {@code OffsetDateTime} with the specified number of nanoseconds subtracted.
1539      * !(p)
1540      * This instance is immutable and unaffected by this method call.
1541      *
1542      * @param nanos  the nanos to subtract, may be negative
1543      * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds subtracted, not null
1544      * @throws DateTimeException if the result exceeds the supported date range
1545      */
1546     public OffsetDateTime minusNanos(long nanos) {
1547         return (nanos == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanos));
1548     }
1549 
1550     //-----------------------------------------------------------------------
1551     /**
1552      * Queries this date-time using the specified query.
1553      * !(p)
1554      * This queries this date-time using the specified query strategy object.
1555      * The {@code TemporalQuery} object defines the logic to be used to
1556      * obtain the result. Read the documentation of the query to understand
1557      * what the result of this method will be.
1558      * !(p)
1559      * The result of this method is obtained by invoking the
1560      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
1561      * specified query passing {@code this} as the argument.
1562      *
1563      * @param !(R) the type of the result
1564      * @param query  the query to invoke, not null
1565      * @return the query result, null may be returned (defined by the query)
1566      * @throws DateTimeException if unable to query (defined by the query)
1567      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
1568      */
1569     /*@SuppressWarnings("unchecked")*/
1570     // override
1571     public R query(R)(TemporalQuery!(R) query) {
1572         if (query == TemporalQueries.offset() || query == TemporalQueries.zone()) {
1573             return cast(R) getOffset();
1574         } else if (query == TemporalQueries.zoneId()) {
1575             return null;
1576         } else if (query == TemporalQueries.localDate()) {
1577             return cast(R) toLocalDate();
1578         } else if (query == TemporalQueries.localTime()) {
1579             return cast(R) toLocalTime();
1580         } else if (query == TemporalQueries.chronology()) {
1581             return cast(R) IsoChronology.INSTANCE;
1582         } else if (query == TemporalQueries.precision()) {
1583             return cast(R) (ChronoUnit.NANOS);
1584         }
1585         // inline TemporalAccessor.super.query(query) as an optimization
1586         // non-JDK classes are not permitted to make this optimization
1587         return query.queryFrom(this);
1588     }
1589 
1590     /**
1591      * Adjusts the specified temporal object to have the same offset, date
1592      * and time as this object.
1593      * !(p)
1594      * This returns a temporal object of the same observable type as the input
1595      * with the offset, date and time changed to be the same as this.
1596      * !(p)
1597      * The adjustment is equivalent to using {@link Temporal#_with(TemporalField, long)}
1598      * three times, passing {@link ChronoField#EPOCH_DAY},
1599      * {@link ChronoField#NANO_OF_DAY} and {@link ChronoField#OFFSET_SECONDS} as the fields.
1600      * !(p)
1601      * In most cases, it is clearer to reverse the calling pattern by using
1602      * {@link Temporal#_with(TemporalAdjuster)}:
1603      * !(pre)
1604      *   // these two lines are equivalent, but the second approach is recommended
1605      *   temporal = thisOffsetDateTime.adjustInto(temporal);
1606      *   temporal = temporal._with(thisOffsetDateTime);
1607      * </pre>
1608      * !(p)
1609      * This instance is immutable and unaffected by this method call.
1610      *
1611      * @param temporal  the target object to be adjusted, not null
1612      * @return the adjusted object, not null
1613      * @throws DateTimeException if unable to make the adjustment
1614      * @throws ArithmeticException if numeric overflow occurs
1615      */
1616     override
1617     public Temporal adjustInto(Temporal temporal) {
1618         // OffsetDateTime is treated as three separate fields, not an instant
1619         // this produces the most consistent set of results overall
1620         // the offset is set after the date and time, as it is typically a small
1621         // tweak to the result, with ZonedDateTime frequently ignoring the offset
1622         return temporal
1623                 ._with(ChronoField.EPOCH_DAY, toLocalDate().toEpochDay())
1624                 ._with(ChronoField.NANO_OF_DAY, toLocalTime().toNanoOfDay())
1625                 ._with(ChronoField.OFFSET_SECONDS, getOffset().getTotalSeconds());
1626     }
1627 
1628     /**
1629      * Calculates the amount of time until another date-time _in terms of the specified unit.
1630      * !(p)
1631      * This calculates the amount of time between two {@code OffsetDateTime}
1632      * objects _in terms of a single {@code TemporalUnit}.
1633      * The start and end points are {@code this} and the specified date-time.
1634      * The result will be negative if the end is before the start.
1635      * For example, the amount _in days between two date-times can be calculated
1636      * using {@code startDateTime.until(endDateTime, DAYS)}.
1637      * !(p)
1638      * The {@code Temporal} passed to this method is converted to a
1639      * {@code OffsetDateTime} using {@link #from(TemporalAccessor)}.
1640      * If the offset differs between the two date-times, the specified
1641      * end date-time is normalized to have the same offset as this date-time.
1642      * !(p)
1643      * The calculation returns a whole number, representing the number of
1644      * complete units between the two date-times.
1645      * For example, the amount _in months between 2012-06-15T00:00Z and 2012-08-14T23:59Z
1646      * will only be one month as it is one minute short of two months.
1647      * !(p)
1648      * There are two equivalent ways of using this method.
1649      * The first is to invoke this method.
1650      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1651      * !(pre)
1652      *   // these two lines are equivalent
1653      *   amount = start.until(end, MONTHS);
1654      *   amount = MONTHS.between(start, end);
1655      * </pre>
1656      * The choice should be made based on which makes the code more readable.
1657      * !(p)
1658      * The calculation is implemented _in this method for {@link ChronoUnit}.
1659      * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
1660      * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS},
1661      * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES},
1662      * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
1663      * Other {@code ChronoUnit} values will throw an exception.
1664      * !(p)
1665      * If the unit is not a {@code ChronoUnit}, then the result of this method
1666      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1667      * passing {@code this} as the first argument and the converted input temporal
1668      * as the second argument.
1669      * !(p)
1670      * This instance is immutable and unaffected by this method call.
1671      *
1672      * @param endExclusive  the end date, exclusive, which is converted to an {@code OffsetDateTime}, not null
1673      * @param unit  the unit to measure the amount _in, not null
1674      * @return the amount of time between this date-time and the end date-time
1675      * @throws DateTimeException if the amount cannot be calculated, or the end
1676      *  temporal cannot be converted to an {@code OffsetDateTime}
1677      * @throws UnsupportedTemporalTypeException if the unit is not supported
1678      * @throws ArithmeticException if numeric overflow occurs
1679      */
1680     override
1681     public long until(Temporal endExclusive, TemporalUnit unit) {
1682         OffsetDateTime end = OffsetDateTime.from(endExclusive);
1683         if (cast(ChronoUnit)(unit) !is null) {
1684             end = end.withOffsetSameInstant(offset);
1685             return dateTime.until(end.dateTime, unit);
1686         }
1687         return unit.between(this, end);
1688     }
1689 
1690     /**
1691      * Formats this date-time using the specified formatter.
1692      * !(p)
1693      * This date-time will be passed to the formatter to produce a string.
1694      *
1695      * @param formatter  the formatter to use, not null
1696      * @return the formatted date-time string, not null
1697      * @throws DateTimeException if an error occurs during printing
1698      */
1699     // public string format(DateTimeFormatter formatter) {
1700     //     assert(formatter, "formatter");
1701     //     return formatter.format(this);
1702     // }
1703 
1704     //-----------------------------------------------------------------------
1705     /**
1706      * Combines this date-time with a time-zone to create a {@code ZonedDateTime}
1707      * ensuring that the result has the same instant.
1708      * !(p)
1709      * This returns a {@code ZonedDateTime} formed from this date-time and the specified time-zone.
1710      * This conversion will ignore the visible local date-time and use the underlying instant instead.
1711      * This avoids any problems with local time-line gaps or overlaps.
1712      * The result might have different values for fields such as hour, minute an even day.
1713      * !(p)
1714      * To attempt to retain the values of the fields, use {@link #atZoneSimilarLocal(ZoneId)}.
1715      * To use the offset as the zone ID, use {@link #toZonedDateTime()}.
1716      *
1717      * @param zone  the time-zone to use, not null
1718      * @return the zoned date-time formed from this date-time, not null
1719      */
1720     public ZonedDateTime atZoneSameInstant(ZoneId zone) {
1721         return ZonedDateTime.ofInstant(dateTime, offset, zone);
1722     }
1723 
1724     /**
1725      * Combines this date-time with a time-zone to create a {@code ZonedDateTime}
1726      * trying to keep the same local date and time.
1727      * !(p)
1728      * This returns a {@code ZonedDateTime} formed from this date-time and the specified time-zone.
1729      * Where possible, the result will have the same local date-time as this object.
1730      * !(p)
1731      * Time-zone rules, such as daylight savings, mean that not every time on the
1732      * local time-line exists. If the local date-time is _in a gap or overlap according to
1733      * the rules then a resolver is used to determine the resultant local time and offset.
1734      * This method uses {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
1735      * to retain the offset from this instance if possible.
1736      * !(p)
1737      * Finer control over gaps and overlaps is available _in two ways.
1738      * If you simply want to use the later offset at overlaps then call
1739      * {@link ZonedDateTime#withLaterOffsetAtOverlap()} immediately after this method.
1740      * !(p)
1741      * To create a zoned date-time at the same instant irrespective of the local time-line,
1742      * use {@link #atZoneSameInstant(ZoneId)}.
1743      * To use the offset as the zone ID, use {@link #toZonedDateTime()}.
1744      *
1745      * @param zone  the time-zone to use, not null
1746      * @return the zoned date-time formed from this date and the earliest valid time for the zone, not null
1747      */
1748     public ZonedDateTime atZoneSimilarLocal(ZoneId zone) {
1749         return ZonedDateTime.ofLocal(dateTime, zone, offset);
1750     }
1751 
1752     //-----------------------------------------------------------------------
1753     /**
1754      * Converts this date-time to an {@code OffsetTime}.
1755      * !(p)
1756      * This returns an offset time with the same local time and offset.
1757      *
1758      * @return an OffsetTime representing the time and offset, not null
1759      */
1760     public OffsetTime toOffsetTime() {
1761         return OffsetTime.of(dateTime.toLocalTime(), offset);
1762     }
1763 
1764     /**
1765      * Converts this date-time to a {@code ZonedDateTime} using the offset as the zone ID.
1766      * !(p)
1767      * This creates the simplest possible {@code ZonedDateTime} using the offset
1768      * as the zone ID.
1769      * !(p)
1770      * To control the time-zone used, see {@link #atZoneSameInstant(ZoneId)} and
1771      * {@link #atZoneSimilarLocal(ZoneId)}.
1772      *
1773      * @return a zoned date-time representing the same local date-time and offset, not null
1774      */
1775     public ZonedDateTime toZonedDateTime() {
1776         return ZonedDateTime.of(dateTime, offset);
1777     }
1778 
1779     /**
1780      * Converts this date-time to an {@code Instant}.
1781      * !(p)
1782      * This returns an {@code Instant} representing the same point on the
1783      * time-line as this date-time.
1784      *
1785      * @return an {@code Instant} representing the same instant, not null
1786      */
1787     public Instant toInstant() {
1788         return dateTime.toInstant(offset);
1789     }
1790 
1791     /**
1792      * Converts this date-time to the number of seconds from the epoch of 1970-01-01T00:00:00Z.
1793      * !(p)
1794      * This allows this date-time to be converted to a value of the
1795      * {@link ChronoField#INSTANT_SECONDS epoch-seconds} field. This is primarily
1796      * intended for low-level conversions rather than general application usage.
1797      *
1798      * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
1799      */
1800     public long toEpochSecond() {
1801         return dateTime.toEpochSecond(offset);
1802     }
1803 
1804     //-----------------------------------------------------------------------
1805     /**
1806      * Compares this date-time to another date-time.
1807      * !(p)
1808      * The comparison is based on the instant then on the local date-time.
1809      * It is "consistent with equals", as defined by {@link Comparable}.
1810      * !(p)
1811      * For example, the following is the comparator order:
1812      * !(ol)
1813      * !(li){@code 2008-12-03T10:30+01:00}</li>
1814      * !(li){@code 2008-12-03T11:00+01:00}</li>
1815      * !(li){@code 2008-12-03T12:00+02:00}</li>
1816      * !(li){@code 2008-12-03T11:30+01:00}</li>
1817      * !(li){@code 2008-12-03T12:00+01:00}</li>
1818      * !(li){@code 2008-12-03T12:30+01:00}</li>
1819      * </ol>
1820      * Values #2 and #3 represent the same instant on the time-line.
1821      * When two values represent the same instant, the local date-time is compared
1822      * to distinguish them. This step is needed to make the ordering
1823      * consistent with {@code equals()}.
1824      *
1825      * @param other  the other date-time to compare to, not null
1826      * @return the comparator value, negative if less, positive if greater
1827      */
1828     // override
1829     public int compareTo(OffsetDateTime other) {
1830         int cmp = compareInstant(this, other);
1831         if (cmp == 0) {
1832             cmp = toLocalDateTime().compareTo(cast(ChronoLocalDateTime!(ChronoLocalDate))(other.toLocalDateTime()));
1833         }
1834         return cmp;
1835     }
1836     override
1837     public int opCmp(OffsetDateTime other) {
1838         return compareTo(other);
1839     }
1840     //-----------------------------------------------------------------------
1841     /**
1842      * Checks if the instant of this date-time is after that of the specified date-time.
1843      * !(p)
1844      * This method differs from the comparison _in {@link #compareTo} and {@link #equals} _in that it
1845      * only compares the instant of the date-time. This is equivalent to using
1846      * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.
1847      *
1848      * @param other  the other date-time to compare to, not null
1849      * @return true if this is after the instant of the specified date-time
1850      */
1851     public bool isAfter(OffsetDateTime other) {
1852         long thisEpochSec = toEpochSecond();
1853         long otherEpochSec = other.toEpochSecond();
1854         return thisEpochSec > otherEpochSec ||
1855             (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
1856     }
1857 
1858     /**
1859      * Checks if the instant of this date-time is before that of the specified date-time.
1860      * !(p)
1861      * This method differs from the comparison _in {@link #compareTo} _in that it
1862      * only compares the instant of the date-time. This is equivalent to using
1863      * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.
1864      *
1865      * @param other  the other date-time to compare to, not null
1866      * @return true if this is before the instant of the specified date-time
1867      */
1868     public bool isBefore(OffsetDateTime other) {
1869         long thisEpochSec = toEpochSecond();
1870         long otherEpochSec = other.toEpochSecond();
1871         return thisEpochSec < otherEpochSec ||
1872             (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
1873     }
1874 
1875     /**
1876      * Checks if the instant of this date-time is equal to that of the specified date-time.
1877      * !(p)
1878      * This method differs from the comparison _in {@link #compareTo} and {@link #equals}
1879      * _in that it only compares the instant of the date-time. This is equivalent to using
1880      * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.
1881      *
1882      * @param other  the other date-time to compare to, not null
1883      * @return true if the instant equals the instant of the specified date-time
1884      */
1885     public bool isEqual(OffsetDateTime other) {
1886         return toEpochSecond() == other.toEpochSecond() &&
1887                 toLocalTime().getNano() == other.toLocalTime().getNano();
1888     }
1889 
1890     //-----------------------------------------------------------------------
1891     /**
1892      * Checks if this date-time is equal to another date-time.
1893      * !(p)
1894      * The comparison is based on the local date-time and the offset.
1895      * To compare for the same instant on the time-line, use {@link #isEqual}.
1896      * Only objects of type {@code OffsetDateTime} are compared, other types return false.
1897      *
1898      * @param obj  the object to check, null returns false
1899      * @return true if this is equal to the other date-time
1900      */
1901     override
1902     public bool opEquals(Object obj) {
1903         if (this is obj) {
1904             return true;
1905         }
1906         if (cast(OffsetDateTime)(obj) !is null) {
1907             OffsetDateTime other = cast(OffsetDateTime) obj;
1908             return dateTime == (other.dateTime) && offset == (other.offset);
1909         }
1910         return false;
1911     }
1912 
1913     /**
1914      * A hash code for this date-time.
1915      *
1916      * @return a suitable hash code
1917      */
1918     override
1919     public size_t toHash() @trusted nothrow {
1920         return dateTime.toHash() ^ offset.toHash();
1921     }
1922 
1923     //-----------------------------------------------------------------------
1924     /**
1925      * Outputs this date-time as a {@code string}, such as {@code 2007-12-03T10:15:30+01:00}.
1926      * !(p)
1927      * The output will be one of the following ISO-8601 formats:
1928      * !(ul)
1929      * !(li){@code uuuu-MM-dd'T'HH:mmXXXXX}</li>
1930      * !(li){@code uuuu-MM-dd'T'HH:mm:ssXXXXX}</li>
1931      * !(li){@code uuuu-MM-dd'T'HH:mm:ss.SSSXXXXX}</li>
1932      * !(li){@code uuuu-MM-dd'T'HH:mm:ss.SSSSSSXXXXX}</li>
1933      * !(li){@code uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSXXXXX}</li>
1934      * </ul>
1935      * The format used will be the shortest that outputs the full value of
1936      * the time where the omitted parts are implied to be zero.
1937      *
1938      * @return a string representation of this date-time, not null
1939      */
1940     override
1941     public string toString() {
1942         return dateTime.toString() ~ offset.toString();
1943     }
1944 
1945     //-----------------------------------------------------------------------
1946     /**
1947      * Writes the object using a
1948      * <a href="{@docRoot}/serialized-form.html#hunt.time.Ser">dedicated serialized form</a>.
1949      * @serialData
1950      * !(pre)
1951      *  _out.writeByte(10);  // identifies an OffsetDateTime
1952      *  // the <a href="{@docRoot}/serialized-form.html#hunt.time.LocalDateTime">datetime</a> excluding the one byte header
1953      *  // the <a href="{@docRoot}/serialized-form.html#hunt.time.ZoneOffset">offset</a> excluding the one byte header
1954      * </pre>
1955      *
1956      * @return the instance of {@code Ser}, not null
1957      */
1958     private Object writeReplace() {
1959         return new Ser(Ser.OFFSET_DATE_TIME_TYPE, this);
1960     }
1961 
1962     /**
1963      * Defend against malicious streams.
1964      *
1965      * @param s the stream to read
1966      * @throws InvalidObjectException always
1967      */
1968      ///@gxc
1969     // private void readObject(ObjectInputStream s) /*throws InvalidObjectException*/ {
1970     //     throw new InvalidObjectException("Deserialization via serialization delegate");
1971     // }
1972 
1973     // void writeExternal(ObjectOutput _out) /*throws IOException*/ {
1974     //     dateTime.writeExternal(_out);
1975     //     offset.writeExternal(_out);
1976     // }
1977 
1978     // static OffsetDateTime readExternal(ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
1979     //     LocalDateTime dateTime = LocalDateTime.readExternal(_in);
1980     //     ZoneOffset offset = ZoneOffset.readExternal(_in);
1981     //     return OffsetDateTime.of(dateTime, offset);
1982     // }
1983 
1984 }