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.chrono.ChronoZonedDateTime;
13 
14 import hunt.time.temporal.ChronoField;
15 import hunt.time.temporal.ChronoUnit;
16 
17 import hunt.stream.Common;
18 import hunt.time.Exceptions;
19 import hunt.time.Instant;
20 import hunt.time.LocalTime;
21 import hunt.time.ZoneId;
22 import hunt.time.ZoneOffset;
23 import hunt.time.ZonedDateTime;
24 // import hunt.time.format.DateTimeFormatter;
25 import hunt.time.temporal.ChronoField;
26 import hunt.time.temporal.ChronoUnit;
27 import hunt.time.temporal.Temporal;
28 import hunt.time.temporal.TemporalAccessor;
29 import hunt.time.temporal.TemporalAdjuster;
30 import hunt.time.temporal.TemporalAmount;
31 import hunt.time.temporal.TemporalField;
32 import hunt.time.temporal.TemporalQueries;
33 import hunt.time.temporal.TemporalQuery;
34 import hunt.time.temporal.TemporalUnit;
35 import hunt.time.Exceptions;
36 import hunt.time.temporal.ValueRange;
37 import hunt.util.Common;
38 import hunt.util.Comparator;
39 import hunt.time.chrono.ChronoLocalDate;
40 import hunt.Functions;
41 import hunt.time.chrono.ChronoLocalDateTime;
42 import hunt.time.chrono.Chronology;
43 import hunt.time.util.QueryHelper;
44 
45 /**
46  * A date-time with a time-zone _in an arbitrary chronology,
47  * intended for advanced globalization use cases.
48  * !(p)
49  * !(b)Most applications should declare method signatures, fields and variables
50  * as {@link ZonedDateTime}, not this interface.</b>
51  * !(p)
52  * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time
53  * where the {@code Chronology chronology}, or calendar system, is pluggable.
54  * The date-time is defined _in terms of fields expressed by {@link TemporalField},
55  * where most common implementations are defined _in {@link ChronoField}.
56  * The chronology defines how the calendar system operates and the meaning of
57  * the standard fields.
58  *
59  * !(h3)When to use this interface</h3>
60  * The design of the API encourages the use of {@code ZonedDateTime} rather than this
61  * interface, even _in the case where the application needs to deal with multiple
62  * calendar systems. The rationale for this is explored _in detail _in {@link ChronoLocalDate}.
63  * !(p)
64  * Ensure that the discussion _in {@code ChronoLocalDate} has been read and understood
65  * before using this interface.
66  *
67  * @implSpec
68  * This interface must be implemented with care to ensure other classes operate correctly.
69  * All implementations that can be instantiated must be final, immutable and thread-safe.
70  * Subclasses should be Serializable wherever possible.
71  *
72  * @param !(D) the concrete type for the date of this date-time
73  * @since 1.8
74  */
75 public interface ChronoZonedDateTime(D = ChronoLocalDate)  if(is(D : ChronoLocalDate))
76         : Temporal, Comparable!(ChronoZonedDateTime!(D)) { 
77 
78     /**
79      * Gets a comparator that compares {@code ChronoZonedDateTime} _in
80      * time-line order ignoring the chronology.
81      * !(p)
82      * This comparator differs from the comparison _in {@link #compareTo} _in that it
83      * only compares the underlying instant and not the chronology.
84      * This allows dates _in different calendar systems to be compared based
85      * on the position of the date-time on the instant time-line.
86      * The underlying comparison is equivalent to comparing the epoch-second and nano-of-second.
87      *
88      * @return a comparator that compares _in time-line order ignoring the chronology
89      * @see #isAfter
90      * @see #isBefore
91      * @see #isEqual
92      */
93     static Comparator!(ChronoZonedDateTime!(ChronoLocalDate)) timeLineOrder() {
94         return new class Comparator!(ChronoZonedDateTime!(ChronoLocalDate)) {
95             int compare(ChronoZonedDateTime!(ChronoLocalDate) dateTime1, 
96                 ChronoZonedDateTime!(ChronoLocalDate) dateTime2) nothrow {
97                 try {
98                     int cmp = hunt.util.Comparator.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
99                     if (cmp == 0) {
100                         cmp = hunt.util.Comparator.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());
101                     }
102                     return cmp;
103                 } catch(Exception) {
104                     // FIXME: Needing refactor or cleanup -@zxp at 12/29/2018, 11:30:49 PM
105                     // 
106                     return 0;
107                 }
108             };
109         };
110     }
111 
112     //-----------------------------------------------------------------------
113     /**
114      * Obtains an instance of {@code ChronoZonedDateTime} from a temporal object.
115      * !(p)
116      * This creates a zoned date-time based on the specified temporal.
117      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
118      * which this factory converts to an instance of {@code ChronoZonedDateTime}.
119      * !(p)
120      * The conversion extracts and combines the chronology, date, time and zone
121      * from the temporal object. The behavior is equivalent to using
122      * {@link Chronology#zonedDateTime(TemporalAccessor)} with the extracted chronology.
123      * Implementations are permitted to perform optimizations such as accessing
124      * those fields that are equivalent to the relevant objects.
125      * !(p)
126      * This method matches the signature of the functional interface {@link TemporalQuery}
127      * allowing it to be used as a query via method reference, {@code ChronoZonedDateTime::from}.
128      *
129      * @param temporal  the temporal object to convert, not null
130      * @return the date-time, not null
131      * @throws DateTimeException if unable to convert to a {@code ChronoZonedDateTime}
132      * @see Chronology#zonedDateTime(TemporalAccessor)
133      */
134     static ChronoZonedDateTime!(ChronoLocalDate) from(TemporalAccessor temporal) {
135         if (cast(ChronoZonedDateTime)(temporal) !is null) {
136             return cast(ChronoZonedDateTime!(ChronoLocalDate)) temporal;
137         }
138         assert(temporal, "temporal");
139         Chronology chrono = QueryHelper.query!Chronology(temporal,TemporalQueries.chronology());
140         if (chrono is null) {
141             throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " ~ typeid(temporal).stringof);
142         }
143         return chrono.zonedDateTime(temporal);
144     }
145 
146     //-----------------------------------------------------------------------
147     ValueRange range(TemporalField field);
148     // override
149     //  ValueRange range(TemporalField field) {
150     //     if (cast(ChronoField)(field) !is null) {
151     //         if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
152     //             return field.range();
153     //         }
154     //         return toLocalDateTime().range(field);
155     //     }
156     //     return field.rangeRefinedBy(this);
157     // }
158     int get(TemporalField field);
159     // override
160     //  int get(TemporalField field) {
161     //     if (cast(ChronoField)(field) !is null) {
162     //         switch (cast(ChronoField) field) {
163     //             case INSTANT_SECONDS:
164     //                 throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
165     //             case OFFSET_SECONDS:
166     //                 return getOffset().getTotalSeconds();
167     //         }
168     //         return toLocalDateTime().get(field);
169     //     }
170     //     return /* Temporal. */super.get(field);
171     // }
172     long getLong(TemporalField field);
173     // override
174     //  long getLong(TemporalField field) {
175     //     if (cast(ChronoField)(field) !is null) {
176     //         switch (cast(ChronoField) field) {
177     //             case INSTANT_SECONDS: return toEpochSecond();
178     //             case OFFSET_SECONDS: return getOffset().getTotalSeconds();
179     //         }
180     //         return toLocalDateTime().getLong(field);
181     //     }
182     //     return field.getFrom(this);
183     // }
184 
185     /**
186      * Gets the local date part of this date-time.
187      * !(p)
188      * This returns a local date with the same year, month and day
189      * as this date-time.
190      *
191      * @return the date part of this date-time, not null
192      */
193      D toLocalDate();
194     //  D toLocalDate() {
195     //     return toLocalDateTime().toLocalDate();
196     // }
197 
198     /**
199      * Gets the local time part of this date-time.
200      * !(p)
201      * This returns a local time with the same hour, minute, second and
202      * nanosecond as this date-time.
203      *
204      * @return the time part of this date-time, not null
205      */
206      LocalTime toLocalTime();
207     //  LocalTime toLocalTime() {
208     //     return toLocalDateTime().toLocalTime();
209     // }
210 
211     /**
212      * Gets the local date-time part of this date-time.
213      * !(p)
214      * This returns a local date with the same year, month and day
215      * as this date-time.
216      *
217      * @return the local date-time part of this date-time, not null
218      */
219     ChronoLocalDateTime!(D) toLocalDateTime();
220 
221     /**
222      * Gets the chronology of this date-time.
223      * !(p)
224      * The {@code Chronology} represents the calendar system _in use.
225      * The era and other fields _in {@link ChronoField} are defined by the chronology.
226      *
227      * @return the chronology, not null
228      */
229      Chronology getChronology();
230     //  Chronology getChronology() {
231     //     return toLocalDate().getChronology();
232     // }
233 
234     /**
235      * Gets the zone offset, such as '+01:00'.
236      * !(p)
237      * This is the offset of the local date-time from UTC/Greenwich.
238      *
239      * @return the zone offset, not null
240      */
241     ZoneOffset getOffset();
242 
243     /**
244      * Gets the zone ID, such as 'Europe/Paris'.
245      * !(p)
246      * This returns the stored time-zone id used to determine the time-zone rules.
247      *
248      * @return the zone ID, not null
249      */
250     ZoneId getZone();
251 
252     //-----------------------------------------------------------------------
253     /**
254      * Returns a copy of this date-time changing the zone offset to the
255      * earlier of the two valid offsets at a local time-line overlap.
256      * !(p)
257      * This method only has any effect when the local time-line overlaps, such as
258      * at an autumn daylight savings cutover. In this scenario, there are two
259      * valid offsets for the local date-time. Calling this method will return
260      * a zoned date-time with the earlier of the two selected.
261      * !(p)
262      * If this method is called when it is not an overlap, {@code this}
263      * is returned.
264      * !(p)
265      * This instance is immutable and unaffected by this method call.
266      *
267      * @return a {@code ChronoZonedDateTime} based on this date-time with the earlier offset, not null
268      * @throws DateTimeException if no rules can be found for the zone
269      * @throws DateTimeException if no rules are valid for this date-time
270      */
271     ChronoZonedDateTime!(D) withEarlierOffsetAtOverlap();
272 
273     /**
274      * Returns a copy of this date-time changing the zone offset to the
275      * later of the two valid offsets at a local time-line overlap.
276      * !(p)
277      * This method only has any effect when the local time-line overlaps, such as
278      * at an autumn daylight savings cutover. In this scenario, there are two
279      * valid offsets for the local date-time. Calling this method will return
280      * a zoned date-time with the later of the two selected.
281      * !(p)
282      * If this method is called when it is not an overlap, {@code this}
283      * is returned.
284      * !(p)
285      * This instance is immutable and unaffected by this method call.
286      *
287      * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null
288      * @throws DateTimeException if no rules can be found for the zone
289      * @throws DateTimeException if no rules are valid for this date-time
290      */
291     ChronoZonedDateTime!(D) withLaterOffsetAtOverlap();
292 
293     /**
294      * Returns a copy of this date-time with a different time-zone,
295      * retaining the local date-time if possible.
296      * !(p)
297      * This method changes the time-zone and retains the local date-time.
298      * The local date-time is only changed if it is invalid for the new zone.
299      * !(p)
300      * To change the zone and adjust the local date-time,
301      * use {@link #withZoneSameInstant(ZoneId)}.
302      * !(p)
303      * This instance is immutable and unaffected by this method call.
304      *
305      * @param zone  the time-zone to change to, not null
306      * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
307      */
308     ChronoZonedDateTime!(D) withZoneSameLocal(ZoneId zone);
309 
310     /**
311      * Returns a copy of this date-time with a different time-zone,
312      * retaining the instant.
313      * !(p)
314      * This method changes the time-zone and retains the instant.
315      * This normally results _in a change to the local date-time.
316      * !(p)
317      * This method is based on retaining the same instant, thus gaps and overlaps
318      * _in the local time-line have no effect on the result.
319      * !(p)
320      * To change the offset while keeping the local time,
321      * use {@link #withZoneSameLocal(ZoneId)}.
322      *
323      * @param zone  the time-zone to change to, not null
324      * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
325      * @throws DateTimeException if the result exceeds the supported date range
326      */
327     ChronoZonedDateTime!(D) withZoneSameInstant(ZoneId zone);
328 
329     /**
330      * Checks if the specified field is supported.
331      * !(p)
332      * This checks if the specified field can be queried on this date-time.
333      * If false, then calling the {@link #range(TemporalField) range},
334      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
335      * methods will throw an exception.
336      * !(p)
337      * The set of supported fields is defined by the chronology and normally includes
338      * all {@code ChronoField} fields.
339      * !(p)
340      * If the field is not a {@code ChronoField}, then the result of this method
341      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
342      * passing {@code this} as the argument.
343      * Whether the field is supported is determined by the field.
344      *
345      * @param field  the field to check, null returns false
346      * @return true if the field can be queried, false if not
347      */
348     override
349     bool isSupported(TemporalField field);
350 
351     /**
352      * Checks if the specified unit is supported.
353      * !(p)
354      * This checks if the specified unit can be added to or subtracted from this date-time.
355      * If false, then calling the {@link #plus(long, TemporalUnit)} and
356      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
357      * !(p)
358      * The set of supported units is defined by the chronology and normally includes
359      * all {@code ChronoUnit} units except {@code FOREVER}.
360      * !(p)
361      * If the unit is not a {@code ChronoUnit}, then the result of this method
362      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
363      * passing {@code this} as the argument.
364      * Whether the unit is supported is determined by the unit.
365      *
366      * @param unit  the unit to check, null returns false
367      * @return true if the unit can be added/subtracted, false if not
368      */
369      bool isSupported(TemporalUnit unit);
370     // override
371     //  bool isSupported(TemporalUnit unit) {
372     //     if (cast(ChronoUnit)(unit) !is null) {
373     //         return unit != FOREVER;
374     //     }
375     //     return unit !is null && unit.isSupportedBy(this);
376     // }
377 
378     //-----------------------------------------------------------------------
379     // override for covariant return type
380     /**
381      * {@inheritDoc}
382      * @throws DateTimeException {@inheritDoc}
383      * @throws ArithmeticException {@inheritDoc}
384      */
385      ChronoZonedDateTime!(D) _with(TemporalAdjuster adjuster);
386     // override
387     //  ChronoZonedDateTime!(D) _with(TemporalAdjuster adjuster) {
388     //     return ChronoZonedDateTimeImpl.ensureValid(getChronology(), /* Temporal. */super._with(adjuster));
389     // }
390 
391     /**
392      * {@inheritDoc}
393      * @throws DateTimeException {@inheritDoc}
394      * @throws ArithmeticException {@inheritDoc}
395      */
396     override
397     ChronoZonedDateTime!(D) _with(TemporalField field, long newValue);
398 
399     /**
400      * {@inheritDoc}
401      * @throws DateTimeException {@inheritDoc}
402      * @throws ArithmeticException {@inheritDoc}
403      */
404       ChronoZonedDateTime!(D) plus(TemporalAmount amount);
405     // override
406     //  ChronoZonedDateTime!(D) plus(TemporalAmount amount) {
407     //     return ChronoZonedDateTimeImpl.ensureValid(getChronology(), /* Temporal. */super.plus(amount));
408     // }
409 
410     /**
411      * {@inheritDoc}
412      * @throws DateTimeException {@inheritDoc}
413      * @throws ArithmeticException {@inheritDoc}
414      */
415     override
416     ChronoZonedDateTime!(D) plus(long amountToAdd, TemporalUnit unit);
417 
418     /**
419      * {@inheritDoc}
420      * @throws DateTimeException {@inheritDoc}
421      * @throws ArithmeticException {@inheritDoc}
422      */
423      ChronoZonedDateTime!(D) minus(TemporalAmount amount);
424     // override
425     //  ChronoZonedDateTime!(D) minus(TemporalAmount amount) {
426     //     return ChronoZonedDateTimeImpl.ensureValid(getChronology(), /* Temporal. */super.minus(amount));
427     // }
428 
429     /**
430      * {@inheritDoc}
431      * @throws DateTimeException {@inheritDoc}
432      * @throws ArithmeticException {@inheritDoc}
433      */
434      override
435      ChronoZonedDateTime!(D) minus(long amountToSubtract, TemporalUnit unit);
436     // override
437     //  ChronoZonedDateTime!(D) minus(long amountToSubtract, TemporalUnit unit) {
438     //     return ChronoZonedDateTimeImpl.ensureValid(getChronology(), /* Temporal. */super.minus(amountToSubtract, unit));
439     // }
440 
441     //-----------------------------------------------------------------------
442     /**
443      * Queries this date-time using the specified query.
444      * !(p)
445      * This queries this date-time using the specified query strategy object.
446      * The {@code TemporalQuery} object defines the logic to be used to
447      * obtain the result. Read the documentation of the query to understand
448      * what the result of this method will be.
449      * !(p)
450      * The result of this method is obtained by invoking the
451      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
452      * specified query passing {@code this} as the argument.
453      *
454      * @param !(R) the type of the result
455      * @param query  the query to invoke, not null
456      * @return the query result, null may be returned (defined by the query)
457      * @throws DateTimeException if unable to query (defined by the query)
458      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
459      */
460     /*@SuppressWarnings("unchecked")*/
461     override
462      R query(R)(TemporalQuery!(R) query);
463     // override
464     //  R query(R)(TemporalQuery!(R) query) {
465     //     if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {
466     //         return cast(R) getZone();
467     //     } else if (query == TemporalQueries.offset()) {
468     //         return cast(R) getOffset();
469     //     } else if (query == TemporalQueries.localTime()) {
470     //         return cast(R) toLocalTime();
471     //     } else if (query == TemporalQueries.chronology()) {
472     //         return cast(R) getChronology();
473     //     } else if (query == TemporalQueries.precision()) {
474     //         return cast(R) NANOS;
475     //     }
476     //     // inline TemporalAccessor.super.query(query) as an optimization
477     //     // non-JDK classes are not permitted to make this optimization
478     //     return query.queryFrom(this);
479     // }
480 
481     /**
482      * Formats this date-time using the specified formatter.
483      * !(p)
484      * This date-time will be passed to the formatter to produce a string.
485      * !(p)
486      * The  implementation must behave as follows:
487      * !(pre)
488      *  return formatter.format(this);
489      * </pre>
490      *
491      * @param formatter  the formatter to use, not null
492      * @return the formatted date-time string, not null
493      * @throws DateTimeException if an error occurs during printing
494      */
495     //  string format(DateTimeFormatter formatter);
496     //  string format(DateTimeFormatter formatter) {
497     //     assert(formatter, "formatter");
498     //     return formatter.format(this);
499     // }
500 
501     //-----------------------------------------------------------------------
502     /**
503      * Converts this date-time to an {@code Instant}.
504      * !(p)
505      * This returns an {@code Instant} representing the same point on the
506      * time-line as this date-time. The calculation combines the
507      * {@linkplain #toLocalDateTime() local date-time} and
508      * {@linkplain #getOffset() offset}.
509      *
510      * @return an {@code Instant} representing the same instant, not null
511      */
512      Instant toInstant();
513     //  Instant toInstant() {
514     //     return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano());
515     // }
516 
517     /**
518      * Converts this date-time to the number of seconds from the epoch
519      * of 1970-01-01T00:00:00Z.
520      * !(p)
521      * This uses the {@linkplain #toLocalDateTime() local date-time} and
522      * {@linkplain #getOffset() offset} to calculate the epoch-second value,
523      * which is the number of elapsed seconds from 1970-01-01T00:00:00Z.
524      * Instants on the time-line after the epoch are positive, earlier are negative.
525      *
526      * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
527      */
528      long toEpochSecond();
529     //  long toEpochSecond() {
530     //     long epochDay = toLocalDate().toEpochDay();
531     //     long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
532     //     secs -= getOffset().getTotalSeconds();
533     //     return secs;
534     // }
535 
536     //-----------------------------------------------------------------------
537     /**
538      * Compares this date-time to another date-time, including the chronology.
539      * !(p)
540      * The comparison is based first on the instant, then on the local date-time,
541      * then on the zone ID, then on the chronology.
542      * It is "consistent with equals", as defined by {@link Comparable}.
543      * !(p)
544      * If all the date-time objects being compared are _in the same chronology, then the
545      * additional chronology stage is not required.
546      * !(p)
547      * This  implementation performs the comparison defined above.
548      *
549      * @param other  the other date-time to compare to, not null
550      * @return the comparator value, negative if less, positive if greater
551      */
552     //  override
553      int compareTo(ChronoZonedDateTime!(ChronoLocalDate) other);
554     // override
555     //  int compareTo(ChronoZonedDateTime!(ChronoLocalDate) other) {
556     //     int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
557     //     if (cmp == 0) {
558     //         cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
559     //         if (cmp == 0) {
560     //             cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
561     //             if (cmp == 0) {
562     //                 cmp = getZone().getId().compareTo(other.getZone().getId());
563     //                 if (cmp == 0) {
564     //                     cmp = getChronology().compareTo(other.getChronology());
565     //                 }
566     //             }
567     //         }
568     //     }
569     //     return cmp;
570     // }
571 
572     /**
573      * Checks if the instant of this date-time is before that of the specified date-time.
574      * !(p)
575      * This method differs from the comparison _in {@link #compareTo} _in that it
576      * only compares the instant of the date-time. This is equivalent to using
577      * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.
578      * !(p)
579      * This  implementation performs the comparison based on the epoch-second
580      * and nano-of-second.
581      *
582      * @param other  the other date-time to compare to, not null
583      * @return true if this point is before the specified date-time
584      */
585      bool isBefore(ChronoZonedDateTime!(ChronoLocalDate) other);
586     //  bool isBefore(ChronoZonedDateTime!(Object) other) {
587     //     long thisEpochSec = toEpochSecond();
588     //     long otherEpochSec = other.toEpochSecond();
589     //     return thisEpochSec < otherEpochSec ||
590     //         (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
591     // }
592 
593     /**
594      * Checks if the instant of this date-time is after that of the specified date-time.
595      * !(p)
596      * This method differs from the comparison _in {@link #compareTo} _in that it
597      * only compares the instant of the date-time. This is equivalent to using
598      * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.
599      * !(p)
600      * This  implementation performs the comparison based on the epoch-second
601      * and nano-of-second.
602      *
603      * @param other  the other date-time to compare to, not null
604      * @return true if this is after the specified date-time
605      */
606      bool isAfter(ChronoZonedDateTime!(ChronoLocalDate) other);
607     //  bool isAfter(ChronoZonedDateTime!(Object) other) {
608     //     long thisEpochSec = toEpochSecond();
609     //     long otherEpochSec = other.toEpochSecond();
610     //     return thisEpochSec > otherEpochSec ||
611     //         (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
612     // }
613 
614     /**
615      * Checks if the instant of this date-time is equal to that of the specified date-time.
616      * !(p)
617      * This method differs from the comparison _in {@link #compareTo} and {@link #equals}
618      * _in that it only compares the instant of the date-time. This is equivalent to using
619      * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.
620      * !(p)
621      * This  implementation performs the comparison based on the epoch-second
622      * and nano-of-second.
623      *
624      * @param other  the other date-time to compare to, not null
625      * @return true if the instant equals the instant of the specified date-time
626      */
627      bool isEqual(ChronoZonedDateTime!(ChronoLocalDate) other);
628     //  bool isEqual(ChronoZonedDateTime!(Object) other) {
629     //     return toEpochSecond() == other.toEpochSecond() &&
630     //             toLocalTime().getNano() == other.toLocalTime().getNano();
631     // }
632 
633     //-----------------------------------------------------------------------
634     /**
635      * Checks if this date-time is equal to another date-time.
636      * !(p)
637      * The comparison is based on the offset date-time and the zone.
638      * To compare for the same instant on the time-line, use {@link #compareTo}.
639      * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false.
640      *
641      * @param obj  the object to check, null returns false
642      * @return true if this is equal to the other date-time
643      */
644     // override
645     // bool opEquals(Object obj);
646 
647     /**
648      * A hash code for this date-time.
649      *
650      * @return a suitable hash code
651      */
652     // override
653     // size_t toHash() @trusted nothrow;
654 
655     //-----------------------------------------------------------------------
656     /**
657      * Outputs this date-time as a {@code string}.
658      * !(p)
659      * The output will include the full zoned date-time.
660      *
661      * @return a string representation of this date-time, not null
662      */
663     // override
664     // string toString();
665 
666 }