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