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