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.ZonedDateTime;
13 
14 import hunt.time.temporal.ChronoField;
15 
16 import hunt.stream.DataOutput;
17 import hunt.Exceptions;
18 import hunt.stream.ObjectInput;
19 
20 //import hunt.io.ObjectInputStream;
21 import hunt.stream.Common;
22 import hunt.time.chrono.ChronoZonedDateTime;
23 import hunt.time.chrono.ChronoLocalDate;
24 import hunt.time.chrono.ChronoLocalDateTime;
25 import hunt.time.chrono.Chronology;
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.ZoneOffsetTransition;
41 import hunt.time.zone.ZoneRules;
42 import hunt.collection.List;
43 import hunt.time.LocalDate;
44 import hunt.time.LocalDateTime;
45 import hunt.time.ZoneOffset;
46 import hunt.time.ZoneId;
47 import hunt.time.Clock;
48 import hunt.time.LocalTime;
49 import hunt.time.Instant;
50 import hunt.time.Month;
51 import hunt.time.DayOfWeek;
52 import hunt.time.OffsetDateTime;
53 import hunt.time.Exceptions;
54 import hunt.time.Period;
55 import hunt.Integer;
56 import hunt.Long;
57 import std.conv;
58 import hunt.time.Ser;
59 import hunt.util.Comparator;
60 
61 
62 import hunt.util.Common;
63 // import hunt.serialization.JsonSerializer;
64 
65 /**
66  * A date-time with a time-zone _in the ISO-8601 calendar system,
67  * such as {@code 2007-12-03T10:15:30+01:00 Europe/Paris}.
68  * !(p)
69  * {@code ZonedDateTime} is an immutable representation of a date-time with a time-zone.
70  * This class stores all date and time fields, to a precision of nanoseconds,
71  * and a time-zone, with a zone offset used to handle ambiguous local date-times.
72  * For example, the value
73  * "2nd October 2007 at 13:45.30.123456789 +02:00 _in the Europe/Paris time-zone"
74  * can be stored _in a {@code ZonedDateTime}.
75  * !(p)
76  * This class handles conversion from the local time-line of {@code LocalDateTime}
77  * to the instant time-line of {@code Instant}.
78  * The difference between the two time-lines is the offset from UTC/Greenwich,
79  * represented by a {@code ZoneOffset}.
80  * !(p)
81  * Converting between the two time-lines involves calculating the offset using the
82  * {@link ZoneRules rules} accessed from the {@code ZoneId}.
83  * Obtaining the offset for an instant is simple, as there is exactly one valid
84  * offset for each instant. By contrast, obtaining the offset for a local date-time
85  * is not straightforward. There are three cases:
86  * !(ul)
87  * !(li)Normal, with one valid offset. For the vast majority of the year, the normal
88  *  case applies, where there is a single valid offset for the local date-time.</li>
89  * !(li)Gap, with zero valid offsets. This is when clocks jump forward typically
90  *  due to the spring daylight savings change from "winter" to "summer".
91  *  In a gap there are local date-time values with no valid offset.</li>
92  * !(li)Overlap, with two valid offsets. This is when clocks are set back typically
93  *  due to the autumn daylight savings change from "summer" to "winter".
94  *  In an overlap there are local date-time values with two valid offsets.</li>
95  * </ul>
96  * !(p)
97  * Any method that converts directly or implicitly from a local date-time to an
98  * instant by obtaining the offset has the potential to be complicated.
99  * !(p)
100  * For Gaps, the general strategy is that if the local date-time falls _in the
101  * middle of a Gap, then the resulting zoned date-time will have a local date-time
102  * shifted forwards by the length of the Gap, resulting _in a date-time _in the later
103  * offset, typically "summer" time.
104  * !(p)
105  * For Overlaps, the general strategy is that if the local date-time falls _in the
106  * middle of an Overlap, then the previous offset will be retained. If there is no
107  * previous offset, or the previous offset is invalid, then the earlier offset is
108  * used, typically "summer" time.. Two additional methods,
109  * {@link #withEarlierOffsetAtOverlap()} and {@link #withLaterOffsetAtOverlap()},
110  * help manage the case of an overlap.
111  * !(p)
112  * In terms of design, this class should be viewed primarily as the combination
113  * of a {@code LocalDateTime} and a {@code ZoneId}. The {@code ZoneOffset} is
114  * a vital, but secondary, piece of information, used to ensure that the class
115  * represents an instant, especially during a daylight savings overlap.
116  *
117  * !(p)
118  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
119  * class; use of identity-sensitive operations (including reference equality
120  * ({@code ==}), identity hash code, or synchronization) on instances of
121  * {@code ZonedDateTime} may have unpredictable results and should be avoided.
122  * The {@code equals} method should be used for comparisons.
123  *
124  * @implSpec
125  * A {@code ZonedDateTime} holds state equivalent to three separate objects,
126  * a {@code LocalDateTime}, a {@code ZoneId} and the resolved {@code ZoneOffset}.
127  * The offset and local date-time are used to define an instant when necessary.
128  * The zone ID is used to obtain the rules for how and when the offset changes.
129  * The offset cannot be freely set, as the zone controls which offsets are valid.
130  * !(p)
131  * This class is immutable and thread-safe.
132  *
133  * @since 1.8
134  */
135 public  class ZonedDateTime
136         : Temporal, ChronoZonedDateTime!(LocalDate) { // , Serializable
137 
138     /**
139      * The local date-time.
140      */
141     private  LocalDateTime dateTime;
142     /**
143      * The offset from UTC/Greenwich.
144      */
145     private  ZoneOffset offset;
146     /**
147      * The time-zone.
148      */
149     private  ZoneId zone;
150 
151     //-----------------------------------------------------------------------
152     /**
153      * Obtains the current date-time from the system clock _in the default time-zone.
154      * !(p)
155      * This will query the {@link Clock#systemDefaultZone() system clock} _in the default
156      * time-zone to obtain the current date-time.
157      * The zone and offset will be set based on the time-zone _in the clock.
158      * !(p)
159      * Using this method will prevent the ability to use an alternate clock for testing
160      * because the clock is hard-coded.
161      *
162      * @return the current date-time using the system clock, not null
163      */
164     public static ZonedDateTime now() {
165         return now(Clock.systemDefaultZone());
166     }
167 
168     /**
169      * Obtains the current date-time from the system clock _in the specified time-zone.
170      * !(p)
171      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date-time.
172      * Specifying the time-zone avoids dependence on the default time-zone.
173      * The offset will be calculated from the specified time-zone.
174      * !(p)
175      * Using this method will prevent the ability to use an alternate clock for testing
176      * because the clock is hard-coded.
177      *
178      * @param zone  the zone ID to use, not null
179      * @return the current date-time using the system clock, not null
180      */
181     public static ZonedDateTime now(ZoneId zone) {
182         return now(Clock.system(zone));
183     }
184 
185     /**
186      * Obtains the current date-time from the specified clock.
187      * !(p)
188      * This will query the specified clock to obtain the current date-time.
189      * The zone and offset will be set based on the time-zone _in the clock.
190      * !(p)
191      * Using this method allows the use of an alternate clock for testing.
192      * The alternate clock may be introduced using {@link Clock dependency injection}.
193      *
194      * @param clock  the clock to use, not null
195      * @return the current date-time, not null
196      */
197     public static ZonedDateTime now(Clock clock) {
198         assert(clock, "clock");
199         Instant now = clock.instant();  // called once
200         return ofInstant(now, clock.getZone());
201     }
202 
203     //-----------------------------------------------------------------------
204     /**
205      * Obtains an instance of {@code ZonedDateTime} from a local date and time.
206      * !(p)
207      * This creates a zoned date-time matching the input local date and time as closely as possible.
208      * Time-zone rules, such as daylight savings, mean that not every local date-time
209      * is valid for the specified zone, thus the local date-time may be adjusted.
210      * !(p)
211      * The local date time and first combined to form a local date-time.
212      * The local date-time is then resolved to a single instant on the time-line.
213      * This is achieved by finding a valid offset from UTC/Greenwich for the local
214      * date-time as defined by the {@link ZoneRules rules} of the zone ID.
215      *!(p)
216      * In most cases, there is only one valid offset for a local date-time.
217      * In the case of an overlap, when clocks are set back, there are two valid offsets.
218      * This method uses the earlier offset typically corresponding to "summer".
219      * !(p)
220      * In the case of a gap, when clocks jump forward, there is no valid offset.
221      * Instead, the local date-time is adjusted to be later by the length of the gap.
222      * For a typical one hour daylight savings change, the local date-time will be
223      * moved one hour later into the offset typically corresponding to "summer".
224      *
225      * @param date  the local date, not null
226      * @param time  the local time, not null
227      * @param zone  the time-zone, not null
228      * @return the offset date-time, not null
229      */
230     public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone) {
231         return of(LocalDateTime.of(date, time), zone);
232     }
233 
234     /**
235      * Obtains an instance of {@code ZonedDateTime} from a local date-time.
236      * !(p)
237      * This creates a zoned date-time matching the input local date-time as closely as possible.
238      * Time-zone rules, such as daylight savings, mean that not every local date-time
239      * is valid for the specified zone, thus the local date-time may be adjusted.
240      * !(p)
241      * The local date-time is resolved to a single instant on the time-line.
242      * This is achieved by finding a valid offset from UTC/Greenwich for the local
243      * date-time as defined by the {@link ZoneRules rules} of the zone ID.
244      *!(p)
245      * In most cases, there is only one valid offset for a local date-time.
246      * In the case of an overlap, when clocks are set back, there are two valid offsets.
247      * This method uses the earlier offset typically corresponding to "summer".
248      * !(p)
249      * In the case of a gap, when clocks jump forward, there is no valid offset.
250      * Instead, the local date-time is adjusted to be later by the length of the gap.
251      * For a typical one hour daylight savings change, the local date-time will be
252      * moved one hour later into the offset typically corresponding to "summer".
253      *
254      * @param localDateTime  the local date-time, not null
255      * @param zone  the time-zone, not null
256      * @return the zoned date-time, not null
257      */
258     public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) {
259         return ofLocal(localDateTime, zone, null);
260     }
261 
262     /**
263      * Obtains an instance of {@code ZonedDateTime} from a year, month, day,
264      * hour, minute, second, nanosecond and time-zone.
265      * !(p)
266      * This creates a zoned date-time matching the local date-time of the seven
267      * specified fields as closely as possible.
268      * Time-zone rules, such as daylight savings, mean that not every local date-time
269      * is valid for the specified zone, thus the local date-time may be adjusted.
270      * !(p)
271      * The local date-time is resolved to a single instant on the time-line.
272      * This is achieved by finding a valid offset from UTC/Greenwich for the local
273      * date-time as defined by the {@link ZoneRules rules} of the zone ID.
274      *!(p)
275      * In most cases, there is only one valid offset for a local date-time.
276      * In the case of an overlap, when clocks are set back, there are two valid offsets.
277      * This method uses the earlier offset typically corresponding to "summer".
278      * !(p)
279      * In the case of a gap, when clocks jump forward, there is no valid offset.
280      * Instead, the local date-time is adjusted to be later by the length of the gap.
281      * For a typical one hour daylight savings change, the local date-time will be
282      * moved one hour later into the offset typically corresponding to "summer".
283      * !(p)
284      * This method exists primarily for writing test cases.
285      * Non test-code will typically use other methods to create an offset time.
286      * {@code LocalDateTime} has five additional convenience variants of the
287      * equivalent factory method taking fewer arguments.
288      * They are not provided here to reduce the footprint of the API.
289      *
290      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
291      * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
292      * @param dayOfMonth  the day-of-month to represent, from 1 to 31
293      * @param hour  the hour-of-day to represent, from 0 to 23
294      * @param minute  the minute-of-hour to represent, from 0 to 59
295      * @param second  the second-of-minute to represent, from 0 to 59
296      * @param nanoOfSecond  the nano-of-second to represent, from 0 to 999,999,999
297      * @param zone  the time-zone, not null
298      * @return the offset date-time, not null
299      * @throws DateTimeException if the value of any field is _out of range, or
300      *  if the day-of-month is invalid for the month-year
301      */
302     public static ZonedDateTime of(
303             int year, int month, int dayOfMonth,
304             int hour, int minute, int second, 
305             int nanoOfSecond, ZoneId zone) {
306         LocalDateTime dt = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
307         return ofLocal(dt, zone, null);
308     }
309 
310     public static ZonedDateTime of(
311             int year, Month month, int dayOfMonth,
312             int hour, int minute, int second, 
313             int nanoOfSecond, ZoneId zone) {
314         LocalDateTime dt = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
315         return ofLocal(dt, zone, null);
316     }
317 
318     /**
319      * Obtains an instance of {@code ZonedDateTime} from a local date-time
320      * using the preferred offset if possible.
321      * !(p)
322      * The local date-time is resolved to a single instant on the time-line.
323      * This is achieved by finding a valid offset from UTC/Greenwich for the local
324      * date-time as defined by the {@link ZoneRules rules} of the zone ID.
325      *!(p)
326      * In most cases, there is only one valid offset for a local date-time.
327      * In the case of an overlap, where clocks are set back, there are two valid offsets.
328      * If the preferred offset is one of the valid offsets then it is used.
329      * Otherwise the earlier valid offset is used, typically corresponding to "summer".
330      * !(p)
331      * In the case of a gap, where clocks jump forward, there is no valid offset.
332      * Instead, the local date-time is adjusted to be later by the length of the gap.
333      * For a typical one hour daylight savings change, the local date-time will be
334      * moved one hour later into the offset typically corresponding to "summer".
335      *
336      * @param localDateTime  the local date-time, not null
337      * @param zone  the time-zone, not null
338      * @param preferredOffset  the zone offset, null if no preference
339      * @return the zoned date-time, not null
340      */
341     public static ZonedDateTime ofLocal(LocalDateTime localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
342         assert(localDateTime, "localDateTime");
343         assert(zone, "zone");
344         if (cast(ZoneOffset)(zone) !is null) {
345             return new ZonedDateTime(localDateTime, cast(ZoneOffset) zone, zone);
346         }
347         ZoneRules rules = zone.getRules();
348         List!(ZoneOffset) validOffsets = rules.getValidOffsets(localDateTime);
349         ZoneOffset offset;
350         if (validOffsets.size() == 1) {
351             offset = validOffsets.get(0);
352         } else if (validOffsets.size() == 0) {
353             ZoneOffsetTransition trans = rules.getTransition(localDateTime);
354             localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds());
355             offset = trans.getOffsetAfter();
356         } else {
357             if (preferredOffset !is null && validOffsets.contains(preferredOffset)) {
358                 offset = preferredOffset;
359             } else {
360                 offset = validOffsets.get(0);  // protect against bad ZoneRules
361             }
362         }
363         return new ZonedDateTime(localDateTime, offset, zone);
364     }
365 
366     //-----------------------------------------------------------------------
367     /**
368      * Obtains an instance of {@code ZonedDateTime} from an {@code Instant}.
369      * !(p)
370      * This creates a zoned date-time with the same instant as that specified.
371      * Calling {@link #toInstant()} will return an instant equal to the one used here.
372      * !(p)
373      * Converting an instant to a zoned date-time is simple as there is only one valid
374      * offset for each instant.
375      *
376      * @param instant  the instant to create the date-time from, not null
377      * @param zone  the time-zone, not null
378      * @return the zoned date-time, not null
379      * @throws DateTimeException if the result exceeds the supported range
380      */
381     public static ZonedDateTime ofInstant(Instant instant, ZoneId zone) {
382         assert(instant, "instant");
383         assert(zone, "zone");
384         return create(instant.getEpochSecond(), instant.getNano(), zone);
385     }
386 
387     /**
388      * Obtains an instance of {@code ZonedDateTime} from the instant formed by combining
389      * the local date-time and offset.
390      * !(p)
391      * This creates a zoned date-time by {@link LocalDateTime#toInstant(ZoneOffset) combining}
392      * the {@code LocalDateTime} and {@code ZoneOffset}.
393      * This combination uniquely specifies an instant without ambiguity.
394      * !(p)
395      * Converting an instant to a zoned date-time is simple as there is only one valid
396      * offset for each instant. If the valid offset is different to the offset specified,
397      * then the date-time and offset of the zoned date-time will differ from those specified.
398      * !(p)
399      * If the {@code ZoneId} to be used is a {@code ZoneOffset}, this method is equivalent
400      * to {@link #of(LocalDateTime, ZoneId)}.
401      *
402      * @param localDateTime  the local date-time, not null
403      * @param offset  the zone offset, not null
404      * @param zone  the time-zone, not null
405      * @return the zoned date-time, not null
406      */
407     public static ZonedDateTime ofInstant(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
408         assert(localDateTime, "localDateTime");
409         assert(offset, "offset");
410         assert(zone, "zone");
411         if (zone.getRules().isValidOffset(localDateTime, offset)) {
412             return new ZonedDateTime(localDateTime, offset, zone);
413         }
414         return create(localDateTime.toEpochSecond(offset), localDateTime.getNano(), zone);
415     }
416 
417     /**
418      * Obtains an instance of {@code ZonedDateTime} using seconds from the
419      * epoch of 1970-01-01T00:00:00Z.
420      *
421      * @param epochSecond  the number of seconds from the epoch of 1970-01-01T00:00:00Z
422      * @param nanoOfSecond  the nanosecond within the second, from 0 to 999,999,999
423      * @param zone  the time-zone, not null
424      * @return the zoned date-time, not null
425      * @throws DateTimeException if the result exceeds the supported range
426      */
427     private static ZonedDateTime create(long epochSecond, int nanoOfSecond, ZoneId zone) {
428         ZoneRules rules = zone.getRules();
429         Instant instant = Instant.ofEpochSecond(epochSecond, nanoOfSecond);  // TODO: rules should be queryable by epochSeconds
430         ZoneOffset offset = rules.getOffset(instant);
431         LocalDateTime ldt = LocalDateTime.ofEpochSecond(epochSecond, nanoOfSecond, offset);
432         return new ZonedDateTime(ldt, offset, zone);
433     }
434 
435     //-----------------------------------------------------------------------
436     /**
437      * Obtains an instance of {@code ZonedDateTime} strictly validating the
438      * combination of local date-time, offset and zone ID.
439      * !(p)
440      * This creates a zoned date-time ensuring that the offset is valid for the
441      * local date-time according to the rules of the specified zone.
442      * If the offset is invalid, an exception is thrown.
443      *
444      * @param localDateTime  the local date-time, not null
445      * @param offset  the zone offset, not null
446      * @param zone  the time-zone, not null
447      * @return the zoned date-time, not null
448      * @throws DateTimeException if the combination of arguments is invalid
449      */
450     public static ZonedDateTime ofStrict(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
451         assert(localDateTime, "localDateTime");
452         assert(offset, "offset");
453         assert(zone, "zone");
454         ZoneRules rules = zone.getRules();
455         if (rules.isValidOffset(localDateTime, offset) == false) {
456             ZoneOffsetTransition trans = rules.getTransition(localDateTime);
457             if (trans !is null && trans.isGap()) {
458                 // error message says daylight savings for simplicity
459                 // even though there are other kinds of gaps
460                 throw new DateTimeException("LocalDateTime '" ~ localDateTime.toString ~
461                         "' does not exist _in zone '" ~ zone.toString ~
462                         "' due to a gap _in the local time-line, typically caused by daylight savings");
463             }
464             throw new DateTimeException("ZoneOffset '" ~ offset.toString  ~ "' is not valid for LocalDateTime '" ~
465                     localDateTime.toString  ~ "' _in zone '" ~ zone.toString  ~ "'");
466         }
467         return new ZonedDateTime(localDateTime, offset, zone);
468     }
469 
470     /**
471      * Obtains an instance of {@code ZonedDateTime} leniently, for advanced use cases,
472      * allowing any combination of local date-time, offset and zone ID.
473      * !(p)
474      * This creates a zoned date-time with no checks other than no nulls.
475      * This means that the resulting zoned date-time may have an offset that is _in conflict
476      * with the zone ID.
477      * !(p)
478      * This method is intended for advanced use cases.
479      * For example, consider the case where a zoned date-time with valid fields is created
480      * and then stored _in a database or serialization-based store. At some later point,
481      * the object is then re-loaded. However, between those points _in time, the government
482      * that defined the time-zone has changed the rules, such that the originally stored
483      * local date-time now does not occur. This method can be used to create the object
484      * _in an "invalid" state, despite the change _in rules.
485      *
486      * @param localDateTime  the local date-time, not null
487      * @param offset  the zone offset, not null
488      * @param zone  the time-zone, not null
489      * @return the zoned date-time, not null
490      */
491     private static ZonedDateTime ofLenient(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
492         assert(localDateTime, "localDateTime");
493         assert(offset, "offset");
494         assert(zone, "zone");
495         if (cast(ZoneOffset)(zone) !is null && (offset == zone) == false) {
496             throw new IllegalArgumentException("ZoneId must match ZoneOffset");
497         }
498         return new ZonedDateTime(localDateTime, offset, zone);
499     }
500 
501     //-----------------------------------------------------------------------
502     /**
503      * Obtains an instance of {@code ZonedDateTime} from a temporal object.
504      * !(p)
505      * This obtains a zoned date-time based on the specified temporal.
506      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
507      * which this factory converts to an instance of {@code ZonedDateTime}.
508      * !(p)
509      * The conversion will first obtain a {@code ZoneId} from the temporal object,
510      * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
511      * an {@code Instant}, falling back to a {@code LocalDateTime} if necessary.
512      * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
513      * with {@code Instant} or {@code LocalDateTime}.
514      * Implementations are permitted to perform optimizations such as accessing
515      * those fields that are equivalent to the relevant objects.
516      * !(p)
517      * This method matches the signature of the functional interface {@link TemporalQuery}
518      * allowing it to be used as a query via method reference, {@code ZonedDateTime::from}.
519      *
520      * @param temporal  the temporal object to convert, not null
521      * @return the zoned date-time, not null
522      * @throws DateTimeException if unable to convert to an {@code ZonedDateTime}
523      */
524     public static ZonedDateTime from(TemporalAccessor temporal) {
525         if (cast(ZonedDateTime)(temporal) !is null) {
526             return cast(ZonedDateTime) temporal;
527         }
528         try {
529             ZoneId zone = ZoneId.from(temporal);
530             if (temporal.isSupported(ChronoField.INSTANT_SECONDS)) {
531                 long epochSecond = temporal.getLong(ChronoField.INSTANT_SECONDS);
532                 int nanoOfSecond = temporal.get(ChronoField.NANO_OF_SECOND);
533                 return create(epochSecond, nanoOfSecond, zone);
534             } else {
535                 LocalDate date = LocalDate.from(temporal);
536                 LocalTime time = LocalTime.from(temporal);
537                 return of(date, time, zone);
538             }
539         } catch (DateTimeException ex) {
540             throw new DateTimeException("Unable to obtain ZonedDateTime from TemporalAccessor: " ~
541                     typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
542         }
543     }
544 
545     //-----------------------------------------------------------------------
546     /**
547      * Obtains an instance of {@code ZonedDateTime} from a text string such as
548      * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}.
549      * !(p)
550      * The string must represent a valid date-time and is parsed using
551      * {@link hunt.time.format.DateTimeFormatter#ISO_ZONED_DATE_TIME}.
552      *
553      * @param text  the text to parse such as "2007-12-03T10:15:30+01:00[Europe/Paris]", not null
554      * @return the parsed zoned date-time, not null
555      * @throws DateTimeParseException if the text cannot be parsed
556      */
557     // public static ZonedDateTime parse(string text) {
558     //     return parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME);
559     // }
560 
561     /**
562      * Obtains an instance of {@code ZonedDateTime} from a text string using a specific formatter.
563      * !(p)
564      * The text is parsed using the formatter, returning a date-time.
565      *
566      * @param text  the text to parse, not null
567      * @param formatter  the formatter to use, not null
568      * @return the parsed zoned date-time, not null
569      * @throws DateTimeParseException if the text cannot be parsed
570      */
571     // public static ZonedDateTime parse(string text, DateTimeFormatter formatter) {
572     //     assert(formatter, "formatter");
573     //     return formatter.parse(text, new class TemporalQuery!ZonedDateTime{
574     //         ZonedDateTime queryFrom(TemporalAccessor temporal)
575     //         {
576     //             if (cast(ZonedDateTime)(temporal) !is null) {
577     //                 return cast(ZonedDateTime) temporal;
578     //             }
579     //             try {
580     //                 ZoneId zone = ZoneId.from(temporal);
581     //                 if (temporal.isSupported(ChronoField.INSTANT_SECONDS)) {
582     //                     long epochSecond = temporal.getLong(ChronoField.INSTANT_SECONDS);
583     //                     int nanoOfSecond = temporal.get(ChronoField.NANO_OF_SECOND);
584     //                     return create(epochSecond, nanoOfSecond, zone);
585     //                 } else {
586     //                     LocalDate date = LocalDate.from(temporal);
587     //                     LocalTime time = LocalTime.from(temporal);
588     //                     return of(date, time, zone);
589     //                 }
590     //             } catch (DateTimeException ex) {
591     //                 throw new DateTimeException("Unable to obtain ZonedDateTime from TemporalAccessor: " ~
592     //                         typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
593     //             }
594     //         }
595     //     });
596     // }
597 
598     //-----------------------------------------------------------------------
599     /**
600      * Constructor.
601      *
602      * @param dateTime  the date-time, validated as not null
603      * @param offset  the zone offset, validated as not null
604      * @param zone  the time-zone, validated as not null
605      */
606     private this(LocalDateTime dateTime, ZoneOffset offset, ZoneId zone) {
607         this.dateTime = dateTime;
608         this.offset = offset;
609         this.zone = zone;
610     }
611 
612     /**
613      * Resolves the new local date-time using this zone ID, retaining the offset if possible.
614      *
615      * @param newDateTime  the new local date-time, not null
616      * @return the zoned date-time, not null
617      */
618     private ZonedDateTime resolveLocal(LocalDateTime newDateTime) {
619         return ofLocal(newDateTime, zone, offset);
620     }
621 
622     /**
623      * Resolves the new local date-time using the offset to identify the instant.
624      *
625      * @param newDateTime  the new local date-time, not null
626      * @return the zoned date-time, not null
627      */
628     private ZonedDateTime resolveInstant(LocalDateTime newDateTime) {
629         return ofInstant(newDateTime, offset, zone);
630     }
631 
632     /**
633      * Resolves the offset into this zoned date-time for the with methods.
634      * !(p)
635      * This typically ignores the offset, unless it can be used to switch offset _in a DST overlap.
636      *
637      * @param offset  the offset, not null
638      * @return the zoned date-time, not null
639      */
640     private ZonedDateTime resolveOffset(ZoneOffset offset) {
641         if ((offset == this.offset) == false && zone.getRules().isValidOffset(dateTime, offset)) {
642             return new ZonedDateTime(dateTime, offset, zone);
643         }
644         return this;
645     }
646 
647     //-----------------------------------------------------------------------
648     /**
649      * Checks if the specified field is supported.
650      * !(p)
651      * This checks if this date-time can be queried for the specified field.
652      * If false, then calling the {@link #range(TemporalField) range},
653      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
654      * methods will throw an exception.
655      * !(p)
656      * If the field is a {@link ChronoField} then the query is implemented here.
657      * The supported fields are:
658      * !(ul)
659      * !(li){@code NANO_OF_SECOND}
660      * !(li){@code NANO_OF_DAY}
661      * !(li){@code MICRO_OF_SECOND}
662      * !(li){@code MICRO_OF_DAY}
663      * !(li){@code MILLI_OF_SECOND}
664      * !(li){@code MILLI_OF_DAY}
665      * !(li){@code SECOND_OF_MINUTE}
666      * !(li){@code SECOND_OF_DAY}
667      * !(li){@code MINUTE_OF_HOUR}
668      * !(li){@code MINUTE_OF_DAY}
669      * !(li){@code HOUR_OF_AMPM}
670      * !(li){@code CLOCK_HOUR_OF_AMPM}
671      * !(li){@code HOUR_OF_DAY}
672      * !(li){@code CLOCK_HOUR_OF_DAY}
673      * !(li){@code AMPM_OF_DAY}
674      * !(li){@code DAY_OF_WEEK}
675      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
676      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
677      * !(li){@code DAY_OF_MONTH}
678      * !(li){@code DAY_OF_YEAR}
679      * !(li){@code EPOCH_DAY}
680      * !(li){@code ALIGNED_WEEK_OF_MONTH}
681      * !(li){@code ALIGNED_WEEK_OF_YEAR}
682      * !(li){@code MONTH_OF_YEAR}
683      * !(li){@code PROLEPTIC_MONTH}
684      * !(li){@code YEAR_OF_ERA}
685      * !(li){@code YEAR}
686      * !(li){@code ERA}
687      * !(li){@code INSTANT_SECONDS}
688      * !(li){@code OFFSET_SECONDS}
689      * </ul>
690      * All other {@code ChronoField} instances will return false.
691      * !(p)
692      * If the field is not a {@code ChronoField}, then the result of this method
693      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
694      * passing {@code this} as the argument.
695      * Whether the field is supported is determined by the field.
696      *
697      * @param field  the field to check, null returns false
698      * @return true if the field is supported on this date-time, false if not
699      */
700     override
701     public bool isSupported(TemporalField field) {
702         return cast(ChronoField)(field) !is null || (field !is null && field.isSupportedBy(this));
703     }
704 
705     /**
706      * Checks if the specified unit is supported.
707      * !(p)
708      * This checks if the specified unit can be added to, or subtracted from, this date-time.
709      * If false, then calling the {@link #plus(long, TemporalUnit)} and
710      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
711      * !(p)
712      * If the unit is a {@link ChronoUnit} then the query is implemented here.
713      * The supported units are:
714      * !(ul)
715      * !(li){@code NANOS}
716      * !(li){@code MICROS}
717      * !(li){@code MILLIS}
718      * !(li){@code SECONDS}
719      * !(li){@code MINUTES}
720      * !(li){@code HOURS}
721      * !(li){@code HALF_DAYS}
722      * !(li){@code DAYS}
723      * !(li){@code WEEKS}
724      * !(li){@code MONTHS}
725      * !(li){@code YEARS}
726      * !(li){@code DECADES}
727      * !(li){@code CENTURIES}
728      * !(li){@code MILLENNIA}
729      * !(li){@code ERAS}
730      * </ul>
731      * All other {@code ChronoUnit} instances will return false.
732      * !(p)
733      * If the unit is not a {@code ChronoUnit}, then the result of this method
734      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
735      * passing {@code this} as the argument.
736      * Whether the unit is supported is determined by the unit.
737      *
738      * @param unit  the unit to check, null returns false
739      * @return true if the unit can be added/subtracted, false if not
740      */
741     override  // override for Javadoc
742     public bool isSupported(TemporalUnit unit) {
743         return /* ChronoZonedDateTime. super.*/super_isSupported(unit);
744     }
745     bool super_isSupported(TemporalUnit unit) {
746         if (cast(ChronoUnit)(unit) !is null) {
747             return unit != ChronoUnit.FOREVER;
748         }
749         return unit !is null && unit.isSupportedBy(this);
750     }
751     //-----------------------------------------------------------------------
752     /**
753      * Gets the range of valid values for the specified field.
754      * !(p)
755      * The range object expresses the minimum and maximum valid values for a field.
756      * This date-time is used to enhance the accuracy of the returned range.
757      * If it is not possible to return the range, because the field is not supported
758      * or for some other reason, an exception is thrown.
759      * !(p)
760      * If the field is a {@link ChronoField} then the query is implemented here.
761      * The {@link #isSupported(TemporalField) supported fields} will return
762      * appropriate range instances.
763      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
764      * !(p)
765      * If the field is not a {@code ChronoField}, then the result of this method
766      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
767      * passing {@code this} as the argument.
768      * Whether the range can be obtained is determined by the field.
769      *
770      * @param field  the field to query the range for, not null
771      * @return the range of valid values for the field, not null
772      * @throws DateTimeException if the range for the field cannot be obtained
773      * @throws UnsupportedTemporalTypeException if the field is not supported
774      */
775     override
776     public ValueRange range(TemporalField field) {
777         if (cast(ChronoField)(field) !is null) {
778             if (field == ChronoField.INSTANT_SECONDS || field == ChronoField.OFFSET_SECONDS) {
779                 return field.range();
780             }
781             return dateTime.range(field);
782         }
783         return field.rangeRefinedBy(this);
784     }
785 
786     /**
787      * Gets the value of the specified field from this date-time as an {@code int}.
788      * !(p)
789      * This queries this date-time for the value of the specified field.
790      * The returned value will always be within the valid range of values for the field.
791      * If it is not possible to return the value, because the field is not supported
792      * or for some other reason, an exception is thrown.
793      * !(p)
794      * If the field is a {@link ChronoField} then the query is implemented here.
795      * The {@link #isSupported(TemporalField) supported fields} will return valid
796      * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
797      * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
798      * large to fit _in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
799      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
800      * !(p)
801      * If the field is not a {@code ChronoField}, then the result of this method
802      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
803      * passing {@code this} as the argument. Whether the value can be obtained,
804      * and what the value represents, is determined by the field.
805      *
806      * @param field  the field to get, not null
807      * @return the value for the field
808      * @throws DateTimeException if a value for the field cannot be obtained or
809      *         the value is outside the range of valid values for the field
810      * @throws UnsupportedTemporalTypeException if the field is not supported or
811      *         the range of values exceeds an {@code int}
812      * @throws ArithmeticException if numeric overflow occurs
813      */
814     override  // override for Javadoc and performance
815     public int get(TemporalField field) {
816         if (cast(ChronoField)(field) !is null) {
817             auto f = cast(ChronoField) field;
818             {
819                 if( f == ChronoField.INSTANT_SECONDS)
820                     throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
821                 if( f == ChronoField.OFFSET_SECONDS)
822                     return getOffset().getTotalSeconds();
823             }
824             return dateTime.get(field);
825         }
826         return /* ChronoZonedDateTime. super.*/super_get(field);
827     }
828      int super_get(TemporalField field) {
829         if (cast(ChronoField)(field) !is null) {
830             auto f = cast(ChronoField) field;
831             {
832                 if( f == ChronoField.INSTANT_SECONDS)
833                     throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
834                 if( f == ChronoField.OFFSET_SECONDS)
835                     return getOffset().getTotalSeconds();
836             }
837             return toLocalDateTime().get(field);
838         }
839 
840         ValueRange range = range(field);
841         if (range.isIntValue() == false) {
842             throw new UnsupportedTemporalTypeException("Invalid field " ~ typeid(field).name ~ " for get() method, use getLong() instead");
843         }
844         long value = getLong(field);
845         if (range.isValidValue(value) == false) {
846             throw new DateTimeException("Invalid value for " ~ typeid(field).name ~ " (valid values " ~ range.toString ~ "): " ~ value.to!string);
847         }
848         return cast(int) value;
849     }
850 
851     /**
852      * Gets the value of the specified field from this date-time as a {@code long}.
853      * !(p)
854      * This queries this date-time for the value of the specified field.
855      * If it is not possible to return the value, because the field is not supported
856      * or for some other reason, an exception is thrown.
857      * !(p)
858      * If the field is a {@link ChronoField} then the query is implemented here.
859      * The {@link #isSupported(TemporalField) supported fields} will return valid
860      * values based on this date-time.
861      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
862      * !(p)
863      * If the field is not a {@code ChronoField}, then the result of this method
864      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
865      * passing {@code this} as the argument. Whether the value can be obtained,
866      * and what the value represents, is determined by the field.
867      *
868      * @param field  the field to get, not null
869      * @return the value for the field
870      * @throws DateTimeException if a value for the field cannot be obtained
871      * @throws UnsupportedTemporalTypeException if the field is not supported
872      * @throws ArithmeticException if numeric overflow occurs
873      */
874     override
875     public long getLong(TemporalField field) {
876         if (cast(ChronoField)(field) !is null) {
877             auto f = cast(ChronoField) field;
878             {
879                 if( f == ChronoField.INSTANT_SECONDS) return toEpochSecond();
880                 if( f == ChronoField.OFFSET_SECONDS) return getOffset().getTotalSeconds();
881             }
882             return dateTime.getLong(field);
883         }
884         return field.getFrom(this);
885     }
886 
887     //-----------------------------------------------------------------------
888     /**
889      * Gets the zone offset, such as '+01:00'.
890      * !(p)
891      * This is the offset of the local date-time from UTC/Greenwich.
892      *
893      * @return the zone offset, not null
894      */
895     // override
896     public ZoneOffset getOffset() {
897         return offset;
898     }
899 
900     /**
901      * Returns a copy of this date-time changing the zone offset to the
902      * earlier of the two valid offsets at a local time-line overlap.
903      * !(p)
904      * This method only has any effect when the local time-line overlaps, such as
905      * at an autumn daylight savings cutover. In this scenario, there are two
906      * valid offsets for the local date-time. Calling this method will return
907      * a zoned date-time with the earlier of the two selected.
908      * !(p)
909      * If this method is called when it is not an overlap, {@code this}
910      * is returned.
911      * !(p)
912      * This instance is immutable and unaffected by this method call.
913      *
914      * @return a {@code ZonedDateTime} based on this date-time with the earlier offset, not null
915      */
916     // override
917     public ZonedDateTime withEarlierOffsetAtOverlap() {
918         ZoneOffsetTransition trans = getZone().getRules().getTransition(dateTime);
919         if (trans !is null && trans.isOverlap()) {
920             ZoneOffset earlierOffset = trans.getOffsetBefore();
921             if ((earlierOffset == offset) == false) {
922                 return new ZonedDateTime(dateTime, earlierOffset, zone);
923             }
924         }
925         return this;
926     }
927 
928     /**
929      * Returns a copy of this date-time changing the zone offset to the
930      * later of the two valid offsets at a local time-line overlap.
931      * !(p)
932      * This method only has any effect when the local time-line overlaps, such as
933      * at an autumn daylight savings cutover. In this scenario, there are two
934      * valid offsets for the local date-time. Calling this method will return
935      * a zoned date-time with the later of the two selected.
936      * !(p)
937      * If this method is called when it is not an overlap, {@code this}
938      * is returned.
939      * !(p)
940      * This instance is immutable and unaffected by this method call.
941      *
942      * @return a {@code ZonedDateTime} based on this date-time with the later offset, not null
943      */
944     // override
945     public ZonedDateTime withLaterOffsetAtOverlap() {
946         ZoneOffsetTransition trans = getZone().getRules().getTransition(toLocalDateTime());
947         if (trans !is null) {
948             ZoneOffset laterOffset = trans.getOffsetAfter();
949             if ((laterOffset == offset) == false) {
950                 return new ZonedDateTime(dateTime, laterOffset, zone);
951             }
952         }
953         return this;
954     }
955 
956     //-----------------------------------------------------------------------
957     /**
958      * Gets the time-zone, such as 'Europe/Paris'.
959      * !(p)
960      * This returns the zone ID. This identifies the time-zone {@link ZoneRules rules}
961      * that determine when and how the offset from UTC/Greenwich changes.
962      * !(p)
963      * The zone ID may be same as the {@linkplain #getOffset() offset}.
964      * If this is true, then any future calculations, such as addition or subtraction,
965      * have no complex edge cases due to time-zone rules.
966      * See also {@link #withFixedOffsetZone()}.
967      *
968      * @return the time-zone, not null
969      */
970     // override
971     public ZoneId getZone() {
972         return zone;
973     }
974 
975     /**
976      * Returns a copy of this date-time with a different time-zone,
977      * retaining the local date-time if possible.
978      * !(p)
979      * This method changes the time-zone and retains the local date-time.
980      * The local date-time is only changed if it is invalid for the new zone,
981      * determined using the same approach as
982      * {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}.
983      * !(p)
984      * To change the zone and adjust the local date-time,
985      * use {@link #withZoneSameInstant(ZoneId)}.
986      * !(p)
987      * This instance is immutable and unaffected by this method call.
988      *
989      * @param zone  the time-zone to change to, not null
990      * @return a {@code ZonedDateTime} based on this date-time with the requested zone, not null
991      */
992     // override
993     public ZonedDateTime withZoneSameLocal(ZoneId zone) {
994         assert(zone, "zone");
995         return this.zone == (zone) ? this : ofLocal(dateTime, zone, offset);
996     }
997 
998     /**
999      * Returns a copy of this date-time with a different time-zone,
1000      * retaining the instant.
1001      * !(p)
1002      * This method changes the time-zone and retains the instant.
1003      * This normally results _in a change to the local date-time.
1004      * !(p)
1005      * This method is based on retaining the same instant, thus gaps and overlaps
1006      * _in the local time-line have no effect on the result.
1007      * !(p)
1008      * To change the offset while keeping the local time,
1009      * use {@link #withZoneSameLocal(ZoneId)}.
1010      *
1011      * @param zone  the time-zone to change to, not null
1012      * @return a {@code ZonedDateTime} based on this date-time with the requested zone, not null
1013      * @throws DateTimeException if the result exceeds the supported date range
1014      */
1015     // override
1016     public ZonedDateTime withZoneSameInstant(ZoneId zone) {
1017         assert(zone, "zone");
1018         return this.zone == (zone) ? this :
1019             create(dateTime.toEpochSecond(offset), dateTime.getNano(), zone);
1020     }
1021 
1022     /**
1023      * Returns a copy of this date-time with the zone ID set to the offset.
1024      * !(p)
1025      * This returns a zoned date-time where the zone ID is the same as {@link #getOffset()}.
1026      * The local date-time, offset and instant of the result will be the same as _in this date-time.
1027      * !(p)
1028      * Setting the date-time to a fixed single offset means that any future
1029      * calculations, such as addition or subtraction, have no complex edge cases
1030      * due to time-zone rules.
1031      * This might also be useful when sending a zoned date-time across a network,
1032      * as most protocols, such as ISO-8601, only handle offsets,
1033      * and not region-based zone IDs.
1034      * !(p)
1035      * This is equivalent to {@code ZonedDateTime.of(zdt.toLocalDateTime(), zdt.getOffset())}.
1036      *
1037      * @return a {@code ZonedDateTime} with the zone ID set to the offset, not null
1038      */
1039     public ZonedDateTime withFixedOffsetZone() {
1040         return this.zone == (offset) ? this : new ZonedDateTime(dateTime, offset, offset);
1041     }
1042 
1043     //-----------------------------------------------------------------------
1044     /**
1045      * Gets the {@code LocalDateTime} part of this date-time.
1046      * !(p)
1047      * This returns a {@code LocalDateTime} with the same year, month, day and time
1048      * as this date-time.
1049      *
1050      * @return the local date-time part of this date-time, not null
1051      */
1052     // override  // override for return type
1053     public LocalDateTime toLocalDateTime() {
1054         return dateTime;
1055     }
1056 
1057     //-----------------------------------------------------------------------
1058     /**
1059      * Gets the {@code LocalDate} part of this date-time.
1060      * !(p)
1061      * This returns a {@code LocalDate} with the same year, month and day
1062      * as this date-time.
1063      *
1064      * @return the date part of this date-time, not null
1065      */
1066     // override  // override for return type
1067     public LocalDate toLocalDate() {
1068         return dateTime.toLocalDate();
1069     }
1070 
1071     /**
1072      * Gets the year field.
1073      * !(p)
1074      * This method returns the primitive {@code int} value for the year.
1075      * !(p)
1076      * The year returned by this method is proleptic as per {@code get(YEAR)}.
1077      * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
1078      *
1079      * @return the year, from MIN_YEAR to MAX_YEAR
1080      */
1081     public int getYear() {
1082         return dateTime.getYear();
1083     }
1084 
1085     /**
1086      * Gets the month-of-year field from 1 to 12.
1087      * !(p)
1088      * This method returns the month as an {@code int} from 1 to 12.
1089      * Application code is frequently clearer if the enum {@link Month}
1090      * is used by calling {@link #getMonth()}.
1091      *
1092      * @return the month-of-year, from 1 to 12
1093      * @see #getMonth()
1094      */
1095     public int getMonthValue() {
1096         return dateTime.getMonthValue();
1097     }
1098 
1099     /**
1100      * Gets the month-of-year field using the {@code Month} enum.
1101      * !(p)
1102      * This method returns the enum {@link Month} for the month.
1103      * This avoids confusion as to what {@code int} values mean.
1104      * If you need access to the primitive {@code int} value then the enum
1105      * provides the {@link Month#getValue() int value}.
1106      *
1107      * @return the month-of-year, not null
1108      * @see #getMonthValue()
1109      */
1110     public Month getMonth() {
1111         return dateTime.getMonth();
1112     }
1113 
1114     /**
1115      * Gets the day-of-month field.
1116      * !(p)
1117      * This method returns the primitive {@code int} value for the day-of-month.
1118      *
1119      * @return the day-of-month, from 1 to 31
1120      */
1121     public int getDayOfMonth() {
1122         return dateTime.getDayOfMonth();
1123     }
1124 
1125     /**
1126      * Gets the day-of-year field.
1127      * !(p)
1128      * This method returns the primitive {@code int} value for the day-of-year.
1129      *
1130      * @return the day-of-year, from 1 to 365, or 366 _in a leap year
1131      */
1132     public int getDayOfYear() {
1133         return dateTime.getDayOfYear();
1134     }
1135 
1136     /**
1137      * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
1138      * !(p)
1139      * This method returns the enum {@link DayOfWeek} for the day-of-week.
1140      * This avoids confusion as to what {@code int} values mean.
1141      * If you need access to the primitive {@code int} value then the enum
1142      * provides the {@link DayOfWeek#getValue() int value}.
1143      * !(p)
1144      * Additional information can be obtained from the {@code DayOfWeek}.
1145      * This includes textual names of the values.
1146      *
1147      * @return the day-of-week, not null
1148      */
1149     public DayOfWeek getDayOfWeek() {
1150         return dateTime.getDayOfWeek();
1151     }
1152 
1153     //-----------------------------------------------------------------------
1154     /**
1155      * Gets the {@code LocalTime} part of this date-time.
1156      * !(p)
1157      * This returns a {@code LocalTime} with the same hour, minute, second and
1158      * nanosecond as this date-time.
1159      *
1160      * @return the time part of this date-time, not null
1161      */
1162     // override  // override for Javadoc and performance
1163     public LocalTime toLocalTime() {
1164         return dateTime.toLocalTime();
1165     }
1166 
1167     /**
1168      * Gets the hour-of-day field.
1169      *
1170      * @return the hour-of-day, from 0 to 23
1171      */
1172     public int getHour() {
1173         return dateTime.getHour();
1174     }
1175 
1176     /**
1177      * Gets the minute-of-hour field.
1178      *
1179      * @return the minute-of-hour, from 0 to 59
1180      */
1181     public int getMinute() {
1182         return dateTime.getMinute();
1183     }
1184 
1185     /**
1186      * Gets the second-of-minute field.
1187      *
1188      * @return the second-of-minute, from 0 to 59
1189      */
1190     public int getSecond() {
1191         return dateTime.getSecond();
1192     }
1193 
1194     /**
1195      * Gets the nano-of-second field.
1196      *
1197      * @return the nano-of-second, from 0 to 999,999,999
1198      */
1199     public int getNano() {
1200         return dateTime.getNano();
1201     }
1202 
1203     //-----------------------------------------------------------------------
1204     /**
1205      * Returns an adjusted copy of this date-time.
1206      * !(p)
1207      * This returns a {@code ZonedDateTime}, based on this one, with the date-time adjusted.
1208      * The adjustment takes place using the specified adjuster strategy object.
1209      * Read the documentation of the adjuster to understand what adjustment will be made.
1210      * !(p)
1211      * A simple adjuster might simply set the one of the fields, such as the year field.
1212      * A more complex adjuster might set the date to the last day of the month.
1213      * A selection of common adjustments is provided _in
1214      * {@link hunt.time.temporal.TemporalAdjusters TemporalAdjusters}.
1215      * These include finding the "last day of the month" and "next Wednesday".
1216      * Key date-time classes also implement the {@code TemporalAdjuster} interface,
1217      * such as {@link Month} and {@link hunt.time.MonthDay MonthDay}.
1218      * The adjuster is responsible for handling special cases, such as the varying
1219      * lengths of month and leap years.
1220      * !(p)
1221      * For example this code returns a date on the last day of July:
1222      * !(pre)
1223      *  import hunt.time.Month.*;
1224      *  import hunt.time.temporal.TemporalAdjusters.*;
1225      *
1226      *  result = zonedDateTime._with(JULY)._with(lastDayOfMonth());
1227      * </pre>
1228      * !(p)
1229      * The classes {@link LocalDate} and {@link LocalTime} implement {@code TemporalAdjuster},
1230      * thus this method can be used to change the date, time or offset:
1231      * !(pre)
1232      *  result = zonedDateTime._with(date);
1233      *  result = zonedDateTime._with(time);
1234      * </pre>
1235      * !(p)
1236      * {@link ZoneOffset} also implements {@code TemporalAdjuster} however using it
1237      * as an argument typically has no effect. The offset of a {@code ZonedDateTime} is
1238      * controlled primarily by the time-zone. As such, changing the offset does not generally
1239      * make sense, because there is only one valid offset for the local date-time and zone.
1240      * If the zoned date-time is _in a daylight savings overlap, then the offset is used
1241      * to switch between the two valid offsets. In all other cases, the offset is ignored.
1242      * !(p)
1243      * The result of this method is obtained by invoking the
1244      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
1245      * specified adjuster passing {@code this} as the argument.
1246      * !(p)
1247      * This instance is immutable and unaffected by this method call.
1248      *
1249      * @param adjuster the adjuster to use, not null
1250      * @return a {@code ZonedDateTime} based on {@code this} with the adjustment made, not null
1251      * @throws DateTimeException if the adjustment cannot be made
1252      * @throws ArithmeticException if numeric overflow occurs
1253      */
1254     override
1255     public ZonedDateTime _with(TemporalAdjuster adjuster) {
1256         // optimizations
1257         if (cast(LocalDate)(adjuster) !is null) {
1258             return resolveLocal(LocalDateTime.of(cast(LocalDate) adjuster, dateTime.toLocalTime()));
1259         } else if (cast(LocalTime)(adjuster) !is null) {
1260             return resolveLocal(LocalDateTime.of(dateTime.toLocalDate(), cast(LocalTime) adjuster));
1261         } else if (cast(LocalDateTime)(adjuster) !is null) {
1262             return resolveLocal(cast(LocalDateTime) adjuster);
1263         } else if (cast(OffsetDateTime)(adjuster) !is null) {
1264             OffsetDateTime odt = cast(OffsetDateTime) adjuster;
1265             return ofLocal(odt.toLocalDateTime(), zone, odt.getOffset());
1266         } else if (cast(Instant)(adjuster) !is null) {
1267             Instant instant = cast(Instant) adjuster;
1268             return create(instant.getEpochSecond(), instant.getNano(), zone);
1269         } else if (cast(ZoneOffset)(adjuster) !is null) {
1270             return resolveOffset(cast(ZoneOffset) adjuster);
1271         }
1272         return cast(ZonedDateTime) adjuster.adjustInto(this);
1273     }
1274 
1275     /**
1276      * Returns a copy of this date-time with the specified field set to a new value.
1277      * !(p)
1278      * This returns a {@code ZonedDateTime}, based on this one, with the value
1279      * for the specified field changed.
1280      * This can be used to change any supported field, such as the year, month or day-of-month.
1281      * If it is not possible to set the value, because the field is not supported or for
1282      * some other reason, an exception is thrown.
1283      * !(p)
1284      * In some cases, changing the specified field can cause the resulting date-time to become invalid,
1285      * such as changing the month from 31st January to February would make the day-of-month invalid.
1286      * In cases like this, the field is responsible for resolving the date. Typically it will choose
1287      * the previous valid date, which would be the last valid day of February _in this example.
1288      * !(p)
1289      * If the field is a {@link ChronoField} then the adjustment is implemented here.
1290      * !(p)
1291      * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
1292      * The zone and nano-of-second are unchanged.
1293      * The result will have an offset derived from the new instant and original zone.
1294      * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
1295      * !(p)
1296      * The {@code OFFSET_SECONDS} field will typically be ignored.
1297      * The offset of a {@code ZonedDateTime} is controlled primarily by the time-zone.
1298      * As such, changing the offset does not generally make sense, because there is only
1299      * one valid offset for the local date-time and zone.
1300      * If the zoned date-time is _in a daylight savings overlap, then the offset is used
1301      * to switch between the two valid offsets. In all other cases, the offset is ignored.
1302      * If the new offset value is outside the valid range then a {@code DateTimeException} will be thrown.
1303      * !(p)
1304      * The other {@link #isSupported(TemporalField) supported fields} will behave as per
1305      * the matching method on {@link LocalDateTime#_with(TemporalField, long) LocalDateTime}.
1306      * The zone is not part of the calculation and will be unchanged.
1307      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1308      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1309      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1310      * !(p)
1311      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
1312      * !(p)
1313      * If the field is not a {@code ChronoField}, then the result of this method
1314      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
1315      * passing {@code this} as the argument. In this case, the field determines
1316      * whether and how to adjust the instant.
1317      * !(p)
1318      * This instance is immutable and unaffected by this method call.
1319      *
1320      * @param field  the field to set _in the result, not null
1321      * @param newValue  the new value of the field _in the result
1322      * @return a {@code ZonedDateTime} based on {@code this} with the specified field set, not null
1323      * @throws DateTimeException if the field cannot be set
1324      * @throws UnsupportedTemporalTypeException if the field is not supported
1325      * @throws ArithmeticException if numeric overflow occurs
1326      */
1327     override
1328     public ZonedDateTime _with(TemporalField field, long newValue) {
1329         if (cast(ChronoField)(field) !is null) {
1330             ChronoField f = cast(ChronoField) field;
1331             {
1332                 if( f == ChronoField.INSTANT_SECONDS)
1333                     return create(newValue, getNano(), zone);
1334                 if( f == ChronoField.OFFSET_SECONDS){
1335                     ZoneOffset offset = ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue));
1336                     return resolveOffset(offset);
1337                 }
1338             }
1339             return resolveLocal(dateTime._with(field, newValue));
1340         }
1341         return cast(ZonedDateTime)(field.adjustInto(this, newValue));
1342     }
1343 
1344     //-----------------------------------------------------------------------
1345     /**
1346      * Returns a copy of this {@code ZonedDateTime} with the year altered.
1347      * !(p)
1348      * This operates on the local time-line,
1349      * {@link LocalDateTime#withYear(int) changing the year} of the local date-time.
1350      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1351      * to obtain the offset.
1352      * !(p)
1353      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1354      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1355      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1356      * !(p)
1357      * This instance is immutable and unaffected by this method call.
1358      *
1359      * @param year  the year to set _in the result, from MIN_YEAR to MAX_YEAR
1360      * @return a {@code ZonedDateTime} based on this date-time with the requested year, not null
1361      * @throws DateTimeException if the year value is invalid
1362      */
1363     public ZonedDateTime withYear(int year) {
1364         return resolveLocal(dateTime.withYear(year));
1365     }
1366 
1367     /**
1368      * Returns a copy of this {@code ZonedDateTime} with the month-of-year altered.
1369      * !(p)
1370      * This operates on the local time-line,
1371      * {@link LocalDateTime#withMonth(int) changing the month} of the local date-time.
1372      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1373      * to obtain the offset.
1374      * !(p)
1375      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1376      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1377      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1378      * !(p)
1379      * This instance is immutable and unaffected by this method call.
1380      *
1381      * @param month  the month-of-year to set _in the result, from 1 (January) to 12 (December)
1382      * @return a {@code ZonedDateTime} based on this date-time with the requested month, not null
1383      * @throws DateTimeException if the month-of-year value is invalid
1384      */
1385     public ZonedDateTime withMonth(int month) {
1386         return resolveLocal(dateTime.withMonth(month));
1387     }
1388 
1389     /**
1390      * Returns a copy of this {@code ZonedDateTime} with the day-of-month altered.
1391      * !(p)
1392      * This operates on the local time-line,
1393      * {@link LocalDateTime#withDayOfMonth(int) changing the day-of-month} of the local date-time.
1394      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1395      * to obtain the offset.
1396      * !(p)
1397      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1398      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1399      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1400      * !(p)
1401      * This instance is immutable and unaffected by this method call.
1402      *
1403      * @param dayOfMonth  the day-of-month to set _in the result, from 1 to 28-31
1404      * @return a {@code ZonedDateTime} based on this date-time with the requested day, not null
1405      * @throws DateTimeException if the day-of-month value is invalid,
1406      *  or if the day-of-month is invalid for the month-year
1407      */
1408     public ZonedDateTime withDayOfMonth(int dayOfMonth) {
1409         return resolveLocal(dateTime.withDayOfMonth(dayOfMonth));
1410     }
1411 
1412     /**
1413      * Returns a copy of this {@code ZonedDateTime} with the day-of-year altered.
1414      * !(p)
1415      * This operates on the local time-line,
1416      * {@link LocalDateTime#withDayOfYear(int) changing the day-of-year} of the local date-time.
1417      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1418      * to obtain the offset.
1419      * !(p)
1420      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1421      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1422      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1423      * !(p)
1424      * This instance is immutable and unaffected by this method call.
1425      *
1426      * @param dayOfYear  the day-of-year to set _in the result, from 1 to 365-366
1427      * @return a {@code ZonedDateTime} based on this date with the requested day, not null
1428      * @throws DateTimeException if the day-of-year value is invalid,
1429      *  or if the day-of-year is invalid for the year
1430      */
1431     public ZonedDateTime withDayOfYear(int dayOfYear) {
1432         return resolveLocal(dateTime.withDayOfYear(dayOfYear));
1433     }
1434 
1435     //-----------------------------------------------------------------------
1436     /**
1437      * Returns a copy of this {@code ZonedDateTime} with the hour-of-day altered.
1438      * !(p)
1439      * This operates on the local time-line,
1440      * {@linkplain LocalDateTime#withHour(int) changing the time} of the local date-time.
1441      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1442      * to obtain the offset.
1443      * !(p)
1444      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1445      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1446      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1447      * !(p)
1448      * This instance is immutable and unaffected by this method call.
1449      *
1450      * @param hour  the hour-of-day to set _in the result, from 0 to 23
1451      * @return a {@code ZonedDateTime} based on this date-time with the requested hour, not null
1452      * @throws DateTimeException if the hour value is invalid
1453      */
1454     public ZonedDateTime withHour(int hour) {
1455         return resolveLocal(dateTime.withHour(hour));
1456     }
1457 
1458     /**
1459      * Returns a copy of this {@code ZonedDateTime} with the minute-of-hour altered.
1460      * !(p)
1461      * This operates on the local time-line,
1462      * {@linkplain LocalDateTime#withMinute(int) changing the time} of the local date-time.
1463      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1464      * to obtain the offset.
1465      * !(p)
1466      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1467      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1468      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1469      * !(p)
1470      * This instance is immutable and unaffected by this method call.
1471      *
1472      * @param minute  the minute-of-hour to set _in the result, from 0 to 59
1473      * @return a {@code ZonedDateTime} based on this date-time with the requested minute, not null
1474      * @throws DateTimeException if the minute value is invalid
1475      */
1476     public ZonedDateTime withMinute(int minute) {
1477         return resolveLocal(dateTime.withMinute(minute));
1478     }
1479 
1480     /**
1481      * Returns a copy of this {@code ZonedDateTime} with the second-of-minute altered.
1482      * !(p)
1483      * This operates on the local time-line,
1484      * {@linkplain LocalDateTime#withSecond(int) changing the time} of the local date-time.
1485      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1486      * to obtain the offset.
1487      * !(p)
1488      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1489      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1490      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1491      * !(p)
1492      * This instance is immutable and unaffected by this method call.
1493      *
1494      * @param second  the second-of-minute to set _in the result, from 0 to 59
1495      * @return a {@code ZonedDateTime} based on this date-time with the requested second, not null
1496      * @throws DateTimeException if the second value is invalid
1497      */
1498     public ZonedDateTime withSecond(int second) {
1499         return resolveLocal(dateTime.withSecond(second));
1500     }
1501 
1502     /**
1503      * Returns a copy of this {@code ZonedDateTime} with the nano-of-second altered.
1504      * !(p)
1505      * This operates on the local time-line,
1506      * {@linkplain LocalDateTime#withNano(int) changing the time} of the local date-time.
1507      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1508      * to obtain the offset.
1509      * !(p)
1510      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1511      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1512      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1513      * !(p)
1514      * This instance is immutable and unaffected by this method call.
1515      *
1516      * @param nanoOfSecond  the nano-of-second to set _in the result, from 0 to 999,999,999
1517      * @return a {@code ZonedDateTime} based on this date-time with the requested nanosecond, not null
1518      * @throws DateTimeException if the nano value is invalid
1519      */
1520     public ZonedDateTime withNano(int nanoOfSecond) {
1521         return resolveLocal(dateTime.withNano(nanoOfSecond));
1522     }
1523 
1524     //-----------------------------------------------------------------------
1525     /**
1526      * Returns a copy of this {@code ZonedDateTime} with the time truncated.
1527      * !(p)
1528      * Truncation returns a copy of the original date-time with fields
1529      * smaller than the specified unit set to zero.
1530      * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
1531      * will set the second-of-minute and nano-of-second field to zero.
1532      * !(p)
1533      * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
1534      * that divides into the length of a standard day without remainder.
1535      * This includes all supplied time units on {@link ChronoUnit} and
1536      * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
1537      * !(p)
1538      * This operates on the local time-line,
1539      * {@link LocalDateTime#truncatedTo(TemporalUnit) truncating}
1540      * the underlying local date-time. This is then converted back to a
1541      * {@code ZonedDateTime}, using the zone ID to obtain the offset.
1542      * !(p)
1543      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1544      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1545      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1546      * !(p)
1547      * This instance is immutable and unaffected by this method call.
1548      *
1549      * @param unit  the unit to truncate to, not null
1550      * @return a {@code ZonedDateTime} based on this date-time with the time truncated, not null
1551      * @throws DateTimeException if unable to truncate
1552      * @throws UnsupportedTemporalTypeException if the unit is not supported
1553      */
1554     public ZonedDateTime truncatedTo(TemporalUnit unit) {
1555         return resolveLocal(dateTime.truncatedTo(unit));
1556     }
1557 
1558     //-----------------------------------------------------------------------
1559     /**
1560      * Returns a copy of this date-time with the specified amount added.
1561      * !(p)
1562      * This returns a {@code ZonedDateTime}, based on this one, with the specified amount added.
1563      * The amount is typically {@link Period} or {@link Duration} but may be
1564      * any other type implementing the {@link TemporalAmount} interface.
1565      * !(p)
1566      * The calculation is delegated to the amount object by calling
1567      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
1568      * to implement the addition _in any way it wishes, however it typically
1569      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
1570      * of the amount implementation to determine if it can be successfully added.
1571      * !(p)
1572      * This instance is immutable and unaffected by this method call.
1573      *
1574      * @param amountToAdd  the amount to add, not null
1575      * @return a {@code ZonedDateTime} based on this date-time with the addition made, not null
1576      * @throws DateTimeException if the addition cannot be made
1577      * @throws ArithmeticException if numeric overflow occurs
1578      */
1579     override
1580     public ZonedDateTime plus(TemporalAmount amountToAdd) {
1581         if (cast(Period)(amountToAdd) !is null) {
1582             Period periodToAdd = cast(Period) amountToAdd;
1583             return resolveLocal(dateTime.plus(periodToAdd));
1584         }
1585         assert(amountToAdd, "amountToAdd");
1586         return cast(ZonedDateTime) amountToAdd.addTo(this);
1587     }
1588 
1589     /**
1590      * Returns a copy of this date-time with the specified amount added.
1591      * !(p)
1592      * This returns a {@code ZonedDateTime}, based on this one, with the amount
1593      * _in terms of the unit added. If it is not possible to add the amount, because the
1594      * unit is not supported or for some other reason, an exception is thrown.
1595      * !(p)
1596      * If the field is a {@link ChronoUnit} then the addition is implemented here.
1597      * The zone is not part of the calculation and will be unchanged _in the result.
1598      * The calculation for date and time units differ.
1599      * !(p)
1600      * Date units operate on the local time-line.
1601      * The period is first added to the local date-time, then converted back
1602      * to a zoned date-time using the zone ID.
1603      * The conversion uses {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
1604      * with the offset before the addition.
1605      * !(p)
1606      * Time units operate on the instant time-line.
1607      * The period is first added to the local date-time, then converted back to
1608      * a zoned date-time using the zone ID.
1609      * The conversion uses {@link #ofInstant(LocalDateTime, ZoneOffset, ZoneId)}
1610      * with the offset before the addition.
1611      * !(p)
1612      * If the field is not a {@code ChronoUnit}, then the result of this method
1613      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
1614      * passing {@code this} as the argument. In this case, the unit determines
1615      * whether and how to perform the addition.
1616      * !(p)
1617      * This instance is immutable and unaffected by this method call.
1618      *
1619      * @param amountToAdd  the amount of the unit to add to the result, may be negative
1620      * @param unit  the unit of the amount to add, not null
1621      * @return a {@code ZonedDateTime} based on this date-time with the specified amount added, not null
1622      * @throws DateTimeException if the addition cannot be made
1623      * @throws UnsupportedTemporalTypeException if the unit is not supported
1624      * @throws ArithmeticException if numeric overflow occurs
1625      */
1626     override
1627     public ZonedDateTime plus(long amountToAdd, TemporalUnit unit) {
1628         if (cast(ChronoUnit)(unit) !is null) {
1629             if (unit.isDateBased()) {
1630                 return resolveLocal(dateTime.plus(amountToAdd, unit));
1631             } else {
1632                 return resolveInstant(dateTime.plus(amountToAdd, unit));
1633             }
1634         }
1635         return cast(ZonedDateTime)(unit.addTo(this, amountToAdd));
1636     }
1637 
1638     //-----------------------------------------------------------------------
1639     /**
1640      * Returns a copy of this {@code ZonedDateTime} with the specified number of years added.
1641      * !(p)
1642      * This operates on the local time-line,
1643      * {@link LocalDateTime#plusYears(long) adding years} to the local date-time.
1644      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1645      * to obtain the offset.
1646      * !(p)
1647      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1648      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1649      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1650      * !(p)
1651      * This instance is immutable and unaffected by this method call.
1652      *
1653      * @param years  the years to add, may be negative
1654      * @return a {@code ZonedDateTime} based on this date-time with the years added, not null
1655      * @throws DateTimeException if the result exceeds the supported date range
1656      */
1657     public ZonedDateTime plusYears(long years) {
1658         return resolveLocal(dateTime.plusYears(years));
1659     }
1660 
1661     /**
1662      * Returns a copy of this {@code ZonedDateTime} with the specified number of months added.
1663      * !(p)
1664      * This operates on the local time-line,
1665      * {@link LocalDateTime#plusMonths(long) adding months} to the local date-time.
1666      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1667      * to obtain the offset.
1668      * !(p)
1669      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1670      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1671      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1672      * !(p)
1673      * This instance is immutable and unaffected by this method call.
1674      *
1675      * @param months  the months to add, may be negative
1676      * @return a {@code ZonedDateTime} based on this date-time with the months added, not null
1677      * @throws DateTimeException if the result exceeds the supported date range
1678      */
1679     public ZonedDateTime plusMonths(long months) {
1680         return resolveLocal(dateTime.plusMonths(months));
1681     }
1682 
1683     /**
1684      * Returns a copy of this {@code ZonedDateTime} with the specified number of weeks added.
1685      * !(p)
1686      * This operates on the local time-line,
1687      * {@link LocalDateTime#plusWeeks(long) adding weeks} to the local date-time.
1688      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1689      * to obtain the offset.
1690      * !(p)
1691      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1692      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1693      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1694      * !(p)
1695      * This instance is immutable and unaffected by this method call.
1696      *
1697      * @param weeks  the weeks to add, may be negative
1698      * @return a {@code ZonedDateTime} based on this date-time with the weeks added, not null
1699      * @throws DateTimeException if the result exceeds the supported date range
1700      */
1701     public ZonedDateTime plusWeeks(long weeks) {
1702         return resolveLocal(dateTime.plusWeeks(weeks));
1703     }
1704 
1705     /**
1706      * Returns a copy of this {@code ZonedDateTime} with the specified number of days added.
1707      * !(p)
1708      * This operates on the local time-line,
1709      * {@link LocalDateTime#plusDays(long) adding days} to the local date-time.
1710      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1711      * to obtain the offset.
1712      * !(p)
1713      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1714      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1715      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1716      * !(p)
1717      * This instance is immutable and unaffected by this method call.
1718      *
1719      * @param days  the days to add, may be negative
1720      * @return a {@code ZonedDateTime} based on this date-time with the days added, not null
1721      * @throws DateTimeException if the result exceeds the supported date range
1722      */
1723     public ZonedDateTime plusDays(long days) {
1724         return resolveLocal(dateTime.plusDays(days));
1725     }
1726 
1727     //-----------------------------------------------------------------------
1728     /**
1729      * Returns a copy of this {@code ZonedDateTime} with the specified number of hours added.
1730      * !(p)
1731      * This operates on the instant time-line, such that adding one hour will
1732      * always be a duration of one hour later.
1733      * This may cause the local date-time to change by an amount other than one hour.
1734      * Note that this is a different approach to that used by days, months and years,
1735      * thus adding one day is not the same as adding 24 hours.
1736      * !(p)
1737      * For example, consider a time-zone, such as 'Europe/Paris', where the
1738      * Autumn DST cutover means that the local times 02:00 to 02:59 occur twice
1739      * changing from offset +02:00 _in summer to +01:00 _in winter.
1740      * !(ul)
1741      * !(li)Adding one hour to 01:30+02:00 will result _in 02:30+02:00
1742      *     (both _in summer time)
1743      * !(li)Adding one hour to 02:30+02:00 will result _in 02:30+01:00
1744      *     (moving from summer to winter time)
1745      * !(li)Adding one hour to 02:30+01:00 will result _in 03:30+01:00
1746      *     (both _in winter time)
1747      * !(li)Adding three hours to 01:30+02:00 will result _in 03:30+01:00
1748      *     (moving from summer to winter time)
1749      * </ul>
1750      * !(p)
1751      * This instance is immutable and unaffected by this method call.
1752      *
1753      * @param hours  the hours to add, may be negative
1754      * @return a {@code ZonedDateTime} based on this date-time with the hours added, not null
1755      * @throws DateTimeException if the result exceeds the supported date range
1756      */
1757     public ZonedDateTime plusHours(long hours) {
1758         return resolveInstant(dateTime.plusHours(hours));
1759     }
1760 
1761     /**
1762      * Returns a copy of this {@code ZonedDateTime} with the specified number of minutes added.
1763      * !(p)
1764      * This operates on the instant time-line, such that adding one minute will
1765      * always be a duration of one minute later.
1766      * This may cause the local date-time to change by an amount other than one minute.
1767      * Note that this is a different approach to that used by days, months and years.
1768      * !(p)
1769      * This instance is immutable and unaffected by this method call.
1770      *
1771      * @param minutes  the minutes to add, may be negative
1772      * @return a {@code ZonedDateTime} based on this date-time with the minutes added, not null
1773      * @throws DateTimeException if the result exceeds the supported date range
1774      */
1775     public ZonedDateTime plusMinutes(long minutes) {
1776         return resolveInstant(dateTime.plusMinutes(minutes));
1777     }
1778 
1779     /**
1780      * Returns a copy of this {@code ZonedDateTime} with the specified number of seconds added.
1781      * !(p)
1782      * This operates on the instant time-line, such that adding one second will
1783      * always be a duration of one second later.
1784      * This may cause the local date-time to change by an amount other than one second.
1785      * Note that this is a different approach to that used by days, months and years.
1786      * !(p)
1787      * This instance is immutable and unaffected by this method call.
1788      *
1789      * @param seconds  the seconds to add, may be negative
1790      * @return a {@code ZonedDateTime} based on this date-time with the seconds added, not null
1791      * @throws DateTimeException if the result exceeds the supported date range
1792      */
1793     public ZonedDateTime plusSeconds(long seconds) {
1794         return resolveInstant(dateTime.plusSeconds(seconds));
1795     }
1796 
1797     /**
1798      * Returns a copy of this {@code ZonedDateTime} with the specified number of nanoseconds added.
1799      * !(p)
1800      * This operates on the instant time-line, such that adding one nano will
1801      * always be a duration of one nano later.
1802      * This may cause the local date-time to change by an amount other than one nano.
1803      * Note that this is a different approach to that used by days, months and years.
1804      * !(p)
1805      * This instance is immutable and unaffected by this method call.
1806      *
1807      * @param nanos  the nanos to add, may be negative
1808      * @return a {@code ZonedDateTime} based on this date-time with the nanoseconds added, not null
1809      * @throws DateTimeException if the result exceeds the supported date range
1810      */
1811     public ZonedDateTime plusNanos(long nanos) {
1812         return resolveInstant(dateTime.plusNanos(nanos));
1813     }
1814 
1815     //-----------------------------------------------------------------------
1816     /**
1817      * Returns a copy of this date-time with the specified amount subtracted.
1818      * !(p)
1819      * This returns a {@code ZonedDateTime}, based on this one, with the specified amount subtracted.
1820      * The amount is typically {@link Period} or {@link Duration} but may be
1821      * any other type implementing the {@link TemporalAmount} interface.
1822      * !(p)
1823      * The calculation is delegated to the amount object by calling
1824      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
1825      * to implement the subtraction _in any way it wishes, however it typically
1826      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
1827      * of the amount implementation to determine if it can be successfully subtracted.
1828      * !(p)
1829      * This instance is immutable and unaffected by this method call.
1830      *
1831      * @param amountToSubtract  the amount to subtract, not null
1832      * @return a {@code ZonedDateTime} based on this date-time with the subtraction made, not null
1833      * @throws DateTimeException if the subtraction cannot be made
1834      * @throws ArithmeticException if numeric overflow occurs
1835      */
1836     override
1837     public ZonedDateTime minus(TemporalAmount amountToSubtract) {
1838         if (cast(Period)(amountToSubtract) !is null) {
1839             Period periodToSubtract = cast(Period) amountToSubtract;
1840             return resolveLocal(dateTime.minus(periodToSubtract));
1841         }
1842         assert(amountToSubtract, "amountToSubtract");
1843         return cast(ZonedDateTime) amountToSubtract.subtractFrom(this);
1844     }
1845 
1846     /**
1847      * Returns a copy of this date-time with the specified amount subtracted.
1848      * !(p)
1849      * This returns a {@code ZonedDateTime}, based on this one, with the amount
1850      * _in terms of the unit subtracted. If it is not possible to subtract the amount,
1851      * because the unit is not supported or for some other reason, an exception is thrown.
1852      * !(p)
1853      * The calculation for date and time units differ.
1854      * !(p)
1855      * Date units operate on the local time-line.
1856      * The period is first subtracted from the local date-time, then converted back
1857      * to a zoned date-time using the zone ID.
1858      * The conversion uses {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
1859      * with the offset before the subtraction.
1860      * !(p)
1861      * Time units operate on the instant time-line.
1862      * The period is first subtracted from the local date-time, then converted back to
1863      * a zoned date-time using the zone ID.
1864      * The conversion uses {@link #ofInstant(LocalDateTime, ZoneOffset, ZoneId)}
1865      * with the offset before the subtraction.
1866      * !(p)
1867      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
1868      * See that method for a full description of how addition, and thus subtraction, works.
1869      * !(p)
1870      * This instance is immutable and unaffected by this method call.
1871      *
1872      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
1873      * @param unit  the unit of the amount to subtract, not null
1874      * @return a {@code ZonedDateTime} based on this date-time with the specified amount subtracted, not null
1875      * @throws DateTimeException if the subtraction cannot be made
1876      * @throws UnsupportedTemporalTypeException if the unit is not supported
1877      * @throws ArithmeticException if numeric overflow occurs
1878      */
1879     override
1880     public ZonedDateTime minus(long amountToSubtract, TemporalUnit unit) {
1881         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
1882     }
1883 
1884     //-----------------------------------------------------------------------
1885     /**
1886      * Returns a copy of this {@code ZonedDateTime} with the specified number of years subtracted.
1887      * !(p)
1888      * This operates on the local time-line,
1889      * {@link LocalDateTime#minusYears(long) subtracting years} to the local date-time.
1890      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1891      * to obtain the offset.
1892      * !(p)
1893      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1894      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1895      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1896      * !(p)
1897      * This instance is immutable and unaffected by this method call.
1898      *
1899      * @param years  the years to subtract, may be negative
1900      * @return a {@code ZonedDateTime} based on this date-time with the years subtracted, not null
1901      * @throws DateTimeException if the result exceeds the supported date range
1902      */
1903     public ZonedDateTime minusYears(long years) {
1904         return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
1905     }
1906 
1907     /**
1908      * Returns a copy of this {@code ZonedDateTime} with the specified number of months subtracted.
1909      * !(p)
1910      * This operates on the local time-line,
1911      * {@link LocalDateTime#minusMonths(long) subtracting months} to the local date-time.
1912      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1913      * to obtain the offset.
1914      * !(p)
1915      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1916      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1917      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1918      * !(p)
1919      * This instance is immutable and unaffected by this method call.
1920      *
1921      * @param months  the months to subtract, may be negative
1922      * @return a {@code ZonedDateTime} based on this date-time with the months subtracted, not null
1923      * @throws DateTimeException if the result exceeds the supported date range
1924      */
1925     public ZonedDateTime minusMonths(long months) {
1926         return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
1927     }
1928 
1929     /**
1930      * Returns a copy of this {@code ZonedDateTime} with the specified number of weeks subtracted.
1931      * !(p)
1932      * This operates on the local time-line,
1933      * {@link LocalDateTime#minusWeeks(long) subtracting weeks} to the local date-time.
1934      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1935      * to obtain the offset.
1936      * !(p)
1937      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1938      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1939      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1940      * !(p)
1941      * This instance is immutable and unaffected by this method call.
1942      *
1943      * @param weeks  the weeks to subtract, may be negative
1944      * @return a {@code ZonedDateTime} based on this date-time with the weeks subtracted, not null
1945      * @throws DateTimeException if the result exceeds the supported date range
1946      */
1947     public ZonedDateTime minusWeeks(long weeks) {
1948         return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
1949     }
1950 
1951     /**
1952      * Returns a copy of this {@code ZonedDateTime} with the specified number of days subtracted.
1953      * !(p)
1954      * This operates on the local time-line,
1955      * {@link LocalDateTime#minusDays(long) subtracting days} to the local date-time.
1956      * This is then converted back to a {@code ZonedDateTime}, using the zone ID
1957      * to obtain the offset.
1958      * !(p)
1959      * When converting back to {@code ZonedDateTime}, if the local date-time is _in an overlap,
1960      * then the offset will be retained if possible, otherwise the earlier offset will be used.
1961      * If _in a gap, the local date-time will be adjusted forward by the length of the gap.
1962      * !(p)
1963      * This instance is immutable and unaffected by this method call.
1964      *
1965      * @param days  the days to subtract, may be negative
1966      * @return a {@code ZonedDateTime} based on this date-time with the days subtracted, not null
1967      * @throws DateTimeException if the result exceeds the supported date range
1968      */
1969     public ZonedDateTime minusDays(long days) {
1970         return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
1971     }
1972 
1973     //-----------------------------------------------------------------------
1974     /**
1975      * Returns a copy of this {@code ZonedDateTime} with the specified number of hours subtracted.
1976      * !(p)
1977      * This operates on the instant time-line, such that subtracting one hour will
1978      * always be a duration of one hour earlier.
1979      * This may cause the local date-time to change by an amount other than one hour.
1980      * Note that this is a different approach to that used by days, months and years,
1981      * thus subtracting one day is not the same as adding 24 hours.
1982      * !(p)
1983      * For example, consider a time-zone, such as 'Europe/Paris', where the
1984      * Autumn DST cutover means that the local times 02:00 to 02:59 occur twice
1985      * changing from offset +02:00 _in summer to +01:00 _in winter.
1986      * !(ul)
1987      * !(li)Subtracting one hour from 03:30+01:00 will result _in 02:30+01:00
1988      *     (both _in winter time)
1989      * !(li)Subtracting one hour from 02:30+01:00 will result _in 02:30+02:00
1990      *     (moving from winter to summer time)
1991      * !(li)Subtracting one hour from 02:30+02:00 will result _in 01:30+02:00
1992      *     (both _in summer time)
1993      * !(li)Subtracting three hours from 03:30+01:00 will result _in 01:30+02:00
1994      *     (moving from winter to summer time)
1995      * </ul>
1996      * !(p)
1997      * This instance is immutable and unaffected by this method call.
1998      *
1999      * @param hours  the hours to subtract, may be negative
2000      * @return a {@code ZonedDateTime} based on this date-time with the hours subtracted, not null
2001      * @throws DateTimeException if the result exceeds the supported date range
2002      */
2003     public ZonedDateTime minusHours(long hours) {
2004         return (hours == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hours));
2005     }
2006 
2007     /**
2008      * Returns a copy of this {@code ZonedDateTime} with the specified number of minutes subtracted.
2009      * !(p)
2010      * This operates on the instant time-line, such that subtracting one minute will
2011      * always be a duration of one minute earlier.
2012      * This may cause the local date-time to change by an amount other than one minute.
2013      * Note that this is a different approach to that used by days, months and years.
2014      * !(p)
2015      * This instance is immutable and unaffected by this method call.
2016      *
2017      * @param minutes  the minutes to subtract, may be negative
2018      * @return a {@code ZonedDateTime} based on this date-time with the minutes subtracted, not null
2019      * @throws DateTimeException if the result exceeds the supported date range
2020      */
2021     public ZonedDateTime minusMinutes(long minutes) {
2022         return (minutes == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutes));
2023     }
2024 
2025     /**
2026      * Returns a copy of this {@code ZonedDateTime} with the specified number of seconds subtracted.
2027      * !(p)
2028      * This operates on the instant time-line, such that subtracting one second will
2029      * always be a duration of one second earlier.
2030      * This may cause the local date-time to change by an amount other than one second.
2031      * Note that this is a different approach to that used by days, months and years.
2032      * !(p)
2033      * This instance is immutable and unaffected by this method call.
2034      *
2035      * @param seconds  the seconds to subtract, may be negative
2036      * @return a {@code ZonedDateTime} based on this date-time with the seconds subtracted, not null
2037      * @throws DateTimeException if the result exceeds the supported date range
2038      */
2039     public ZonedDateTime minusSeconds(long seconds) {
2040         return (seconds == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-seconds));
2041     }
2042 
2043     /**
2044      * Returns a copy of this {@code ZonedDateTime} with the specified number of nanoseconds subtracted.
2045      * !(p)
2046      * This operates on the instant time-line, such that subtracting one nano will
2047      * always be a duration of one nano earlier.
2048      * This may cause the local date-time to change by an amount other than one nano.
2049      * Note that this is a different approach to that used by days, months and years.
2050      * !(p)
2051      * This instance is immutable and unaffected by this method call.
2052      *
2053      * @param nanos  the nanos to subtract, may be negative
2054      * @return a {@code ZonedDateTime} based on this date-time with the nanoseconds subtracted, not null
2055      * @throws DateTimeException if the result exceeds the supported date range
2056      */
2057     public ZonedDateTime minusNanos(long nanos) {
2058         return (nanos == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanos));
2059     }
2060 
2061     //-----------------------------------------------------------------------
2062     /**
2063      * Queries this date-time using the specified query.
2064      * !(p)
2065      * This queries this date-time using the specified query strategy object.
2066      * The {@code TemporalQuery} object defines the logic to be used to
2067      * obtain the result. Read the documentation of the query to understand
2068      * what the result of this method will be.
2069      * !(p)
2070      * The result of this method is obtained by invoking the
2071      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
2072      * specified query passing {@code this} as the argument.
2073      *
2074      * @param !(R) the type of the result
2075      * @param query  the query to invoke, not null
2076      * @return the query result, null may be returned (defined by the query)
2077      * @throws DateTimeException if unable to query (defined by the query)
2078      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
2079      */
2080     /*@SuppressWarnings("unchecked")*/
2081     // override  // override for Javadoc
2082     public R query(R)(TemporalQuery!(R) query) {
2083         if (query == TemporalQueries.localDate()) {
2084             return cast(R) toLocalDate();
2085         }
2086         return /* ChronoZonedDateTime. */super_query(query);
2087     }
2088     R super_query(R)(TemporalQuery!(R) query) {
2089          if (query == TemporalQueries.zoneId()
2090                  || query == TemporalQueries.chronology()
2091                  || query == TemporalQueries.precision()) {
2092              return null;
2093          }
2094          return query.queryFrom(this);
2095      }
2096 
2097     /**
2098      * Calculates the amount of time until another date-time _in terms of the specified unit.
2099      * !(p)
2100      * This calculates the amount of time between two {@code ZonedDateTime}
2101      * objects _in terms of a single {@code TemporalUnit}.
2102      * The start and end points are {@code this} and the specified date-time.
2103      * The result will be negative if the end is before the start.
2104      * For example, the amount _in days between two date-times can be calculated
2105      * using {@code startDateTime.until(endDateTime, DAYS)}.
2106      * !(p)
2107      * The {@code Temporal} passed to this method is converted to a
2108      * {@code ZonedDateTime} using {@link #from(TemporalAccessor)}.
2109      * If the time-zone differs between the two zoned date-times, the specified
2110      * end date-time is normalized to have the same zone as this date-time.
2111      * !(p)
2112      * The calculation returns a whole number, representing the number of
2113      * complete units between the two date-times.
2114      * For example, the amount _in months between 2012-06-15T00:00Z and 2012-08-14T23:59Z
2115      * will only be one month as it is one minute short of two months.
2116      * !(p)
2117      * There are two equivalent ways of using this method.
2118      * The first is to invoke this method.
2119      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
2120      * !(pre)
2121      *   // these two lines are equivalent
2122      *   amount = start.until(end, MONTHS);
2123      *   amount = MONTHS.between(start, end);
2124      * </pre>
2125      * The choice should be made based on which makes the code more readable.
2126      * !(p)
2127      * The calculation is implemented _in this method for {@link ChronoUnit}.
2128      * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
2129      * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS},
2130      * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES},
2131      * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
2132      * Other {@code ChronoUnit} values will throw an exception.
2133      * !(p)
2134      * The calculation for date and time units differ.
2135      * !(p)
2136      * Date units operate on the local time-line, using the local date-time.
2137      * For example, the period from noon on day 1 to noon the following day
2138      * _in days will always be counted as exactly one day, irrespective of whether
2139      * there was a daylight savings change or not.
2140      * !(p)
2141      * Time units operate on the instant time-line.
2142      * The calculation effectively converts both zoned date-times to instants
2143      * and then calculates the period between the instants.
2144      * For example, the period from noon on day 1 to noon the following day
2145      * _in hours may be 23, 24 or 25 hours (or some other amount) depending on
2146      * whether there was a daylight savings change or not.
2147      * !(p)
2148      * If the unit is not a {@code ChronoUnit}, then the result of this method
2149      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
2150      * passing {@code this} as the first argument and the converted input temporal
2151      * as the second argument.
2152      * !(p)
2153      * This instance is immutable and unaffected by this method call.
2154      *
2155      * @param endExclusive  the end date, exclusive, which is converted to a {@code ZonedDateTime}, not null
2156      * @param unit  the unit to measure the amount _in, not null
2157      * @return the amount of time between this date-time and the end date-time
2158      * @throws DateTimeException if the amount cannot be calculated, or the end
2159      *  temporal cannot be converted to a {@code ZonedDateTime}
2160      * @throws UnsupportedTemporalTypeException if the unit is not supported
2161      * @throws ArithmeticException if numeric overflow occurs
2162      */
2163     override
2164     public long until(Temporal endExclusive, TemporalUnit unit) {
2165         ZonedDateTime end = ZonedDateTime.from(endExclusive);
2166         if (cast(ChronoUnit)(unit) !is null) {
2167             end = end.withZoneSameInstant(zone);
2168             if (unit.isDateBased()) {
2169                 return dateTime.until(end.dateTime, unit);
2170             } else {
2171                 return toOffsetDateTime().until(end.toOffsetDateTime(), unit);
2172             }
2173         }
2174         return unit.between(this, end);
2175     }
2176 
2177     /**
2178      * Formats this date-time using the specified formatter.
2179      * !(p)
2180      * This date-time will be passed to the formatter to produce a string.
2181      *
2182      * @param formatter  the formatter to use, not null
2183      * @return the formatted date-time string, not null
2184      * @throws DateTimeException if an error occurs during printing
2185      */
2186     // override  // override for Javadoc and performance
2187     // public string format(DateTimeFormatter formatter) {
2188     //     assert(formatter, "formatter");
2189     //     return formatter.format(this);
2190     // }
2191 
2192     //-----------------------------------------------------------------------
2193     /**
2194      * Converts this date-time to an {@code OffsetDateTime}.
2195      * !(p)
2196      * This creates an offset date-time using the local date-time and offset.
2197      * The zone ID is ignored.
2198      *
2199      * @return an offset date-time representing the same local date-time and offset, not null
2200      */
2201     public OffsetDateTime toOffsetDateTime() {
2202         return OffsetDateTime.of(dateTime, offset);
2203     }
2204 
2205     //-----------------------------------------------------------------------
2206     /**
2207      * Checks if this date-time is equal to another date-time.
2208      * !(p)
2209      * The comparison is based on the offset date-time and the zone.
2210      * Only objects of type {@code ZonedDateTime} are compared, other types return false.
2211      *
2212      * @param obj  the object to check, null returns false
2213      * @return true if this is equal to the other date-time
2214      */
2215     override
2216     public bool opEquals(Object obj) {
2217         if (this is obj) {
2218             return true;
2219         }
2220         if (cast(ZonedDateTime)(obj) !is null) {
2221             ZonedDateTime other = cast(ZonedDateTime) obj;
2222             return dateTime == (other.dateTime) &&
2223                 offset == (other.offset) &&
2224                 zone == (other.zone);
2225         }
2226         return false;
2227     }
2228 
2229     /**
2230      * A hash code for this date-time.
2231      *
2232      * @return a suitable hash code
2233      */
2234     override
2235     public size_t toHash() @trusted nothrow {
2236         try{
2237             return dateTime.toHash() ^ offset.toHash() ^ Integer.rotateLeft(cast(int)(zone.toHash()), 3);
2238         }
2239         catch(Exception e){
2240         return int.init;
2241         }
2242     }
2243 
2244     //-----------------------------------------------------------------------
2245     /**
2246      * Outputs this date-time as a {@code string}, such as
2247      * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}.
2248      * !(p)
2249      * The format consists of the {@code LocalDateTime} followed by the {@code ZoneOffset}.
2250      * If the {@code ZoneId} is not the same as the offset, then the ID is output.
2251      * The output is compatible with ISO-8601 if the offset and ID are the same.
2252      *
2253      * @return a string representation of this date-time, not null
2254      */
2255     override  // override for Javadoc
2256     public string toString() {
2257         string str = dateTime.toString() ~ offset.toString();
2258         if (offset != zone) {
2259             str ~= '[' ~ zone.toString() ~ ']';
2260         }
2261         return str;
2262     }
2263 
2264     //-----------------------------------------------------------------------
2265     /**
2266      * Writes the object using a
2267      * <a href="{@docRoot}/serialized-form.html#hunt.time.Ser">dedicated serialized form</a>.
2268      * @serialData
2269      * !(pre)
2270      *  _out.writeByte(6);  // identifies a ZonedDateTime
2271      *  // the <a href="{@docRoot}/serialized-form.html#hunt.time.LocalDateTime">dateTime</a> excluding the one byte header
2272      *  // the <a href="{@docRoot}/serialized-form.html#hunt.time.ZoneOffset">offset</a> excluding the one byte header
2273      *  // the <a href="{@docRoot}/serialized-form.html#hunt.time.ZoneId">zone ID</a> excluding the one byte header
2274      * </pre>
2275      *
2276      * @return the instance of {@code Ser}, not null
2277      */
2278     // private Object writeReplace() {
2279     //     return new Ser(Ser.ZONE_DATE_TIME_TYPE, this);
2280     // }
2281 
2282     /**
2283      * Defend against malicious streams.
2284      *
2285      * @param s the stream to read
2286      * @throws InvalidObjectException always
2287      */
2288      ///@gxc
2289     // private void readObject(ObjectInputStream s) /*throws InvalidObjectException*/ {
2290     //     throw new InvalidObjectException("Deserialization via serialization delegate");
2291     // }
2292 
2293     // void writeExternal(DataOutput _out) /*throws IOException*/ {
2294     //     dateTime.writeExternal(_out);
2295     //     offset.writeExternal(_out);
2296     //     zone.write(_out);
2297     // }
2298 
2299     // static ZonedDateTime readExternal(ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
2300     //     LocalDateTime dateTime = LocalDateTime.readExternal(_in);
2301     //     ZoneOffset offset = ZoneOffset.readExternal(_in);
2302     //     ZoneId zone = cast(ZoneId) Ser.read(_in);
2303     //     return ZonedDateTime.ofLenient(dateTime, offset, zone);
2304     // }
2305 
2306     override
2307      int compareTo(ChronoZonedDateTime!(ChronoLocalDate) other) {
2308         int cmp = compare(toEpochSecond(), other.toEpochSecond());
2309         if (cmp == 0) {
2310             cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
2311             if (cmp == 0) {
2312                 cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
2313                 if (cmp == 0) {
2314                     cmp = getZone().getId().compare(other.getZone().getId());
2315                     if (cmp == 0) {
2316                         cmp = getChronology().compareTo(other.getChronology());
2317                     }
2318                 }
2319             }
2320         }
2321         return cmp;
2322     }
2323 
2324     override
2325      int opCmp(ChronoZonedDateTime!(LocalDate) other) {
2326         int cmp = compare(toEpochSecond(), other.toEpochSecond());
2327         if (cmp == 0) {
2328             cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
2329             if (cmp == 0) {
2330                 cmp = toLocalDateTime().compareTo(cast(ChronoLocalDateTime!(ChronoLocalDate))(other.toLocalDateTime()));
2331                 if (cmp == 0) {
2332                     cmp = getZone().getId().compare(other.getZone().getId());
2333                     if (cmp == 0) {
2334                         cmp = getChronology().compareTo(other.getChronology());
2335                     }
2336                 }
2337             }
2338         }
2339         return cmp;
2340     }
2341 
2342         override
2343      Chronology getChronology() {
2344         return toLocalDate().getChronology();
2345     }
2346     
2347     override
2348      Instant toInstant() {
2349         return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano());
2350     }
2351     override
2352      long toEpochSecond() {
2353         long epochDay = toLocalDate().toEpochDay();
2354         long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
2355         secs -= getOffset().getTotalSeconds();
2356         return secs;
2357     }
2358 
2359     override
2360      bool isBefore(ChronoZonedDateTime!(ChronoLocalDate) other) {
2361         long thisEpochSec = toEpochSecond();
2362         long otherEpochSec = other.toEpochSecond();
2363         return thisEpochSec < otherEpochSec ||
2364             (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
2365     }
2366     override
2367      bool isAfter(ChronoZonedDateTime!(ChronoLocalDate) other) {
2368         long thisEpochSec = toEpochSecond();
2369         long otherEpochSec = other.toEpochSecond();
2370         return thisEpochSec > otherEpochSec ||
2371             (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
2372     }
2373     override
2374      bool isEqual(ChronoZonedDateTime!(ChronoLocalDate) other) {
2375         return toEpochSecond() == other.toEpochSecond() &&
2376                 toLocalTime().getNano() == other.toLocalTime().getNano();
2377     }
2378 }