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.temporal; 63 64 import static java.time.temporal.ChronoField.INSTANT_SECONDS; 65 import static java.time.temporal.ChronoField.OFFSET_SECONDS; 66 import static java.time.temporal.ChronoUnit.NANOS; 67 68 import java.time.DateTimeException; 69 import java.time.Instant; 70 import java.time.LocalTime; 71 import java.time.ZoneId; 72 import java.time.ZoneOffset; 73 import java.time.ZonedDateTime; 74 import java.time.format.DateTimeFormatter; 75 import java.util.Comparator; 76 import java.util.Objects; 77 78 /** 79 * A date-time with a time-zone in an arbitrary chronology, 80 * intended for advanced globalization use cases. 81 * <p> 82 * <b>Most applications should declare method signatures, fields and variables 83 * as {@link ZonedDateTime}, not this interface.</b> 84 * <p> 85 * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time 86 * where the {@code Chrono chronology}, or calendar system, is pluggable. 87 * The date-time is defined in terms of fields expressed by {@link TemporalField}, 88 * where most common implementations are defined in {@link ChronoField}. 89 * The chronology defines how the calendar system operates and the meaning of 90 * the standard fields. 91 * 92 * <h3>When to use this interface</h3> 93 * The design of the API encourages the use of {@code ZonedDateTime} rather than this 94 * interface, even in the case where the application needs to deal with multiple 95 * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. 96 * <p> 97 * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood 98 * before using this interface. 99 * 100 * <h3>Specification for implementors</h3> 101 * This interface must be implemented with care to ensure other classes operate correctly. 102 * All implementations that can be instantiated must be final, immutable and thread-safe. 103 * Subclasses should be Serializable wherever possible. 104 * 105 * @param <C> the chronology of this date-time 106 * @since 1.8 107 */ 108 public interface ChronoZonedDateTime<C extends Chrono<C>> 109 extends Temporal, Comparable<ChronoZonedDateTime<?>> { 110 111 /** 112 * Comparator for two {@code ChronoZonedDateTime} instances ignoring the chronology. 113 * <p> 114 * This method differs from the comparison in {@link #compareTo} in that it 115 * only compares the underlying date and not the chronology. 116 * This allows dates in different calendar systems to be compared based 117 * on the time-line position. 118 * 119 * @see #isAfter 120 * @see #isBefore 121 * @see #isEqual 122 */ 123 Comparator<ChronoZonedDateTime<?>> INSTANT_COMPARATOR = new Comparator<ChronoZonedDateTime<?>>() { 124 @Override 125 public int compare(ChronoZonedDateTime<?> datetime1, ChronoZonedDateTime<?> datetime2) { 126 int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond()); 127 if (cmp == 0) { 128 cmp = Long.compare(datetime1.getTime().toNanoOfDay(), datetime2.getTime().toNanoOfDay()); 129 } 130 return cmp; 131 } 132 }; 133 134 @Override 135 public default ValueRange range(TemporalField field) { 136 if (field instanceof ChronoField) { 137 if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) { 138 return field.range(); 139 } 140 return getDateTime().range(field); 141 } 142 return field.doRange(this); 143 } 144 145 @Override 146 public default int get(TemporalField field) { 147 if (field instanceof ChronoField) { 148 switch ((ChronoField) field) { 149 case INSTANT_SECONDS: throw new DateTimeException("Field too large for an int: " + field); 150 case OFFSET_SECONDS: return getOffset().getTotalSeconds(); 151 } 152 return getDateTime().get(field); 153 } 154 return Temporal.super.get(field); 155 } 156 157 @Override 158 public default long getLong(TemporalField field) { 159 if (field instanceof ChronoField) { 160 switch ((ChronoField) field) { 161 case INSTANT_SECONDS: return toEpochSecond(); 162 case OFFSET_SECONDS: return getOffset().getTotalSeconds(); 163 } 164 return getDateTime().getLong(field); 165 } 166 return field.doGet(this); 167 } 168 169 /** 170 * Gets the local date part of this date-time. 171 * <p> 172 * This returns a local date with the same year, month and day 173 * as this date-time. 174 * 175 * @return the date part of this date-time, not null 176 */ 177 public default ChronoLocalDate<C> getDate() { 178 return getDateTime().getDate(); 179 } 180 181 /** 182 * Gets the local time part of this date-time. 183 * <p> 184 * This returns a local time with the same hour, minute, second and 185 * nanosecond as this date-time. 186 * 187 * @return the time part of this date-time, not null 188 */ 189 public default LocalTime getTime() { 190 return getDateTime().getTime(); 191 } 192 193 /** 194 * Gets the local date-time part of this date-time. 195 * <p> 196 * This returns a local date with the same year, month and day 197 * as this date-time. 198 * 199 * @return the local date-time part of this date-time, not null 200 */ 201 ChronoLocalDateTime<C> getDateTime(); 202 203 /** 204 * Gets the zone offset, such as '+01:00'. 205 * <p> 206 * This is the offset of the local date-time from UTC/Greenwich. 207 * 208 * @return the zone offset, not null 209 */ 210 ZoneOffset getOffset(); 211 212 /** 213 * Gets the zone ID, such as 'Europe/Paris'. 214 * <p> 215 * This returns the stored time-zone id used to determine the time-zone rules. 216 * 217 * @return the zone ID, not null 218 */ 219 ZoneId getZone(); 220 221 //----------------------------------------------------------------------- 222 /** 223 * Returns a copy of this date-time changing the zone offset to the 224 * earlier of the two valid offsets at a local time-line overlap. 225 * <p> 226 * This method only has any effect when the local time-line overlaps, such as 227 * at an autumn daylight savings cutover. In this scenario, there are two 228 * valid offsets for the local date-time. Calling this method will return 229 * a zoned date-time with the earlier of the two selected. 230 * <p> 231 * If this method is called when it is not an overlap, {@code this} 232 * is returned. 233 * <p> 234 * This instance is immutable and unaffected by this method call. 235 * 236 * @return a {@code ZoneChronoDateTime} based on this date-time with the earlier offset, not null 237 * @throws DateTimeException if no rules can be found for the zone 238 * @throws DateTimeException if no rules are valid for this date-time 239 */ 240 ChronoZonedDateTime<C> withEarlierOffsetAtOverlap(); 241 242 /** 243 * Returns a copy of this date-time changing the zone offset to the 244 * later of the two valid offsets at a local time-line overlap. 245 * <p> 246 * This method only has any effect when the local time-line overlaps, such as 247 * at an autumn daylight savings cutover. In this scenario, there are two 248 * valid offsets for the local date-time. Calling this method will return 249 * a zoned date-time with the later of the two selected. 250 * <p> 251 * If this method is called when it is not an overlap, {@code this} 252 * is returned. 253 * <p> 254 * This instance is immutable and unaffected by this method call. 255 * 256 * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null 257 * @throws DateTimeException if no rules can be found for the zone 258 * @throws DateTimeException if no rules are valid for this date-time 259 */ 260 ChronoZonedDateTime<C> withLaterOffsetAtOverlap(); 261 262 /** 263 * Returns a copy of this ZonedDateTime with a different time-zone, 264 * retaining the local date-time if possible. 265 * <p> 266 * This method changes the time-zone and retains the local date-time. 267 * The local date-time is only changed if it is invalid for the new zone. 268 * <p> 269 * To change the zone and adjust the local date-time, 270 * use {@link #withZoneSameInstant(ZoneId)}. 271 * <p> 272 * This instance is immutable and unaffected by this method call. 273 * 274 * @param zone the time-zone to change to, not null 275 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 276 */ 277 ChronoZonedDateTime<C> withZoneSameLocal(ZoneId zone); 278 279 /** 280 * Returns a copy of this date-time with a different time-zone, 281 * retaining the instant. 282 * <p> 283 * This method changes the time-zone and retains the instant. 284 * This normally results in a change to the local date-time. 285 * <p> 286 * This method is based on retaining the same instant, thus gaps and overlaps 287 * in the local time-line have no effect on the result. 288 * <p> 289 * To change the offset while keeping the local time, 290 * use {@link #withZoneSameLocal(ZoneId)}. 291 * 292 * @param zone the time-zone to change to, not null 293 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 294 * @throws DateTimeException if the result exceeds the supported date range 295 */ 296 ChronoZonedDateTime<C> withZoneSameInstant(ZoneId zone); 297 298 //----------------------------------------------------------------------- 299 // override for covariant return type 300 /** 301 * {@inheritDoc} 302 * @throws DateTimeException {@inheritDoc} 303 * @throws ArithmeticException {@inheritDoc} 304 */ 305 @Override 306 public default ChronoZonedDateTime<C> with(TemporalAdjuster adjuster) { 307 return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.with(adjuster)); 308 } 309 310 /** 311 * {@inheritDoc} 312 * @throws DateTimeException {@inheritDoc} 313 * @throws ArithmeticException {@inheritDoc} 314 */ 315 @Override 316 ChronoZonedDateTime<C> with(TemporalField field, long newValue); 317 318 /** 319 * {@inheritDoc} 320 * @throws DateTimeException {@inheritDoc} 321 * @throws ArithmeticException {@inheritDoc} 322 */ 323 @Override 324 public default ChronoZonedDateTime<C> plus(TemporalAdder adder) { 325 return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.plus(adder)); 326 } 327 328 /** 329 * {@inheritDoc} 330 * @throws DateTimeException {@inheritDoc} 331 * @throws ArithmeticException {@inheritDoc} 332 */ 333 @Override 334 ChronoZonedDateTime<C> plus(long amountToAdd, TemporalUnit unit); 335 336 /** 337 * {@inheritDoc} 338 * @throws DateTimeException {@inheritDoc} 339 * @throws ArithmeticException {@inheritDoc} 340 */ 341 @Override 342 public default ChronoZonedDateTime<C> minus(TemporalSubtractor subtractor) { 343 return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.minus(subtractor)); 344 } 345 346 /** 347 * {@inheritDoc} 348 * @throws DateTimeException {@inheritDoc} 349 * @throws ArithmeticException {@inheritDoc} 350 */ 351 @Override 352 public default ChronoZonedDateTime<C> minus(long amountToSubtract, TemporalUnit unit) { 353 return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit)); 354 } 355 356 //----------------------------------------------------------------------- 357 /** 358 * Queries this date-time using the specified query. 359 * <p> 360 * This queries this date-time using the specified query strategy object. 361 * The {@code TemporalQuery} object defines the logic to be used to 362 * obtain the result. Read the documentation of the query to understand 363 * what the result of this method will be. 364 * <p> 365 * The result of this method is obtained by invoking the 366 * {@link java.time.temporal.TemporalQuery#queryFrom(TemporalAccessor)} method on the 367 * specified query passing {@code this} as the argument. 368 * 369 * @param <R> the type of the result 370 * @param query the query to invoke, not null 371 * @return the query result, null may be returned (defined by the query) 372 * @throws DateTimeException if unable to query (defined by the query) 373 * @throws ArithmeticException if numeric overflow occurs (defined by the query) 374 */ 375 @SuppressWarnings("unchecked") 376 @Override 377 public default <R> R query(TemporalQuery<R> query) { 378 if (query == Queries.zone() || query == Queries.zoneId()) { 379 return (R) getZone(); 380 } else if (query == Queries.chrono()) { 381 return (R) getDate().getChrono(); 382 } else if (query == Queries.precision()) { 383 return (R) NANOS; 384 } else if (query == Queries.offset()) { 385 return (R) getOffset(); 386 } 387 // inline TemporalAccessor.super.query(query) as an optimization 388 return query.queryFrom(this); 389 } 390 391 //----------------------------------------------------------------------- 392 /** 393 * Converts this date-time to an {@code Instant}. 394 * <p> 395 * This combines the {@linkplain #getDateTime() local date-time} and 396 * {@linkplain #getOffset() offset} to form an {@code Instant}. 397 * 398 * @return an {@code Instant} representing the same instant, not null 399 */ 400 public default Instant toInstant() { 401 return Instant.ofEpochSecond(toEpochSecond(), getTime().getNano()); 402 } 403 404 /** 405 * Converts this date-time to the number of seconds from the epoch 406 * of 1970-01-01T00:00:00Z. 407 * <p> 408 * This uses the {@linkplain #getDateTime() local date-time} and 409 * {@linkplain #getOffset() offset} to calculate the epoch-second value, 410 * which is the number of elapsed seconds from 1970-01-01T00:00:00Z. 411 * Instants on the time-line after the epoch are positive, earlier are negative. 412 * 413 * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z 414 */ 415 public default long toEpochSecond() { 416 long epochDay = getDate().toEpochDay(); 417 long secs = epochDay * 86400 + getTime().toSecondOfDay(); 418 secs -= getOffset().getTotalSeconds(); 419 return secs; 420 } 421 422 //----------------------------------------------------------------------- 423 /** 424 * Compares this date-time to another date-time, including the chronology. 425 * <p> 426 * The comparison is based first on the instant, then on the local date-time, 427 * then on the zone ID, then on the chronology. 428 * It is "consistent with equals", as defined by {@link Comparable}. 429 * <p> 430 * If all the date-time objects being compared are in the same chronology, then the 431 * additional chronology stage is not required. 432 * <p> 433 * This default implementation performs the comparison defined above. 434 * 435 * @param other the other date-time to compare to, not null 436 * @return the comparator value, negative if less, positive if greater 437 */ 438 @Override 439 public default int compareTo(ChronoZonedDateTime<?> other) { 440 int cmp = Long.compare(toEpochSecond(), other.toEpochSecond()); 441 if (cmp == 0) { 442 cmp = getTime().getNano() - other.getTime().getNano(); 443 if (cmp == 0) { 444 cmp = getDateTime().compareTo(other.getDateTime()); 445 if (cmp == 0) { 446 cmp = getZone().getId().compareTo(other.getZone().getId()); 447 if (cmp == 0) { 448 cmp = getDate().getChrono().compareTo(other.getDate().getChrono()); 449 } 450 } 451 } 452 } 453 return cmp; 454 } 455 456 /** 457 * Checks if the instant of this date-time is before that of the specified date-time. 458 * <p> 459 * This method differs from the comparison in {@link #compareTo} in that it 460 * only compares the instant of the date-time. This is equivalent to using 461 * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}. 462 * <p> 463 * This default implementation performs the comparison based on the epoch-second 464 * and nano-of-second. 465 * 466 * @param other the other date-time to compare to, not null 467 * @return true if this point is before the specified date-time 468 */ 469 public default boolean isBefore(ChronoZonedDateTime<?> other) { 470 long thisEpochSec = toEpochSecond(); 471 long otherEpochSec = other.toEpochSecond(); 472 return thisEpochSec < otherEpochSec || 473 (thisEpochSec == otherEpochSec && getTime().getNano() < other.getTime().getNano()); 474 } 475 476 /** 477 * Checks if the instant of this date-time is after that of the specified date-time. 478 * <p> 479 * This method differs from the comparison in {@link #compareTo} in that it 480 * only compares the instant of the date-time. This is equivalent to using 481 * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}. 482 * <p> 483 * This default implementation performs the comparison based on the epoch-second 484 * and nano-of-second. 485 * 486 * @param other the other date-time to compare to, not null 487 * @return true if this is after the specified date-time 488 */ 489 public default boolean isAfter(ChronoZonedDateTime<?> other) { 490 long thisEpochSec = toEpochSecond(); 491 long otherEpochSec = other.toEpochSecond(); 492 return thisEpochSec > otherEpochSec || 493 (thisEpochSec == otherEpochSec && getTime().getNano() > other.getTime().getNano()); 494 } 495 496 /** 497 * Checks if the instant of this date-time is equal to that of the specified date-time. 498 * <p> 499 * This method differs from the comparison in {@link #compareTo} and {@link #equals} 500 * in that it only compares the instant of the date-time. This is equivalent to using 501 * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}. 502 * <p> 503 * This default implementation performs the comparison based on the epoch-second 504 * and nano-of-second. 505 * 506 * @param other the other date-time to compare to, not null 507 * @return true if the instant equals the instant of the specified date-time 508 */ 509 public default boolean isEqual(ChronoZonedDateTime<?> other) { 510 return toEpochSecond() == other.toEpochSecond() && 511 getTime().getNano() == other.getTime().getNano(); 512 } 513 514 //----------------------------------------------------------------------- 515 /** 516 * Checks if this date-time is equal to another date-time. 517 * <p> 518 * The comparison is based on the offset date-time and the zone. 519 * To compare for the same instant on the time-line, use {@link #compareTo}. 520 * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false. 521 * 522 * @param obj the object to check, null returns false 523 * @return true if this is equal to the other date-time 524 */ 525 @Override 526 boolean equals(Object obj); 527 528 /** 529 * A hash code for this date-time. 530 * 531 * @return a suitable hash code 532 */ 533 @Override 534 int hashCode(); 535 536 //----------------------------------------------------------------------- 537 /** 538 * Outputs this date-time as a {@code String}. 539 * <p> 540 * The output will include the full zoned date-time and the chronology ID. 541 * 542 * @return a string representation of this date-time, not null 543 */ 544 @Override 545 String toString(); 546 547 /** 548 * Outputs this date-time as a {@code String} using the formatter. 549 * <p> 550 * The default implementation must behave as follows: 551 * <pre> 552 * return formatter.print(this); 553 * </pre> 554 * 555 * @param formatter the formatter to use, not null 556 * @return the formatted date-time string, not null 557 * @throws DateTimeException if an error occurs during printing 558 */ 559 public default String toString(DateTimeFormatter formatter) { 560 Objects.requireNonNull(formatter, "formatter"); 561 return formatter.print(this); 562 } 563 564 } | 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.chrono; 63 64 import static java.time.temporal.ChronoField.INSTANT_SECONDS; 65 import static java.time.temporal.ChronoField.OFFSET_SECONDS; 66 import static java.time.temporal.ChronoUnit.NANOS; 67 68 import java.time.DateTimeException; 69 import java.time.Instant; 70 import java.time.LocalTime; 71 import java.time.ZoneId; 72 import java.time.ZoneOffset; 73 import java.time.ZonedDateTime; 74 import java.time.format.DateTimeFormatter; 75 import java.time.temporal.ChronoField; 76 import java.time.temporal.Queries; 77 import java.time.temporal.Temporal; 78 import java.time.temporal.TemporalAccessor; 79 import java.time.temporal.TemporalAdjuster; 80 import java.time.temporal.TemporalAmount; 81 import java.time.temporal.TemporalField; 82 import java.time.temporal.TemporalQuery; 83 import java.time.temporal.TemporalUnit; 84 import java.time.temporal.ValueRange; 85 import java.util.Comparator; 86 import java.util.Objects; 87 88 /** 89 * A date-time with a time-zone in an arbitrary chronology, 90 * intended for advanced globalization use cases. 91 * <p> 92 * <b>Most applications should declare method signatures, fields and variables 93 * as {@link ZonedDateTime}, not this interface.</b> 94 * <p> 95 * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time 96 * where the {@code Chronology chronology}, or calendar system, is pluggable. 97 * The date-time is defined in terms of fields expressed by {@link TemporalField}, 98 * where most common implementations are defined in {@link ChronoField}. 99 * The chronology defines how the calendar system operates and the meaning of 100 * the standard fields. 101 * 102 * <h3>When to use this interface</h3> 103 * The design of the API encourages the use of {@code ZonedDateTime} rather than this 104 * interface, even in the case where the application needs to deal with multiple 105 * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. 106 * <p> 107 * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood 108 * before using this interface. 109 * 110 * <h3>Specification for implementors</h3> 111 * This interface must be implemented with care to ensure other classes operate correctly. 112 * All implementations that can be instantiated must be final, immutable and thread-safe. 113 * Subclasses should be Serializable wherever possible. 114 * 115 * @param <D> the concrete type for the date of this date-time 116 * @since 1.8 117 */ 118 public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> 119 extends Temporal, Comparable<ChronoZonedDateTime<?>> { 120 121 /** 122 * Comparator for two {@code ChronoZonedDateTime} instances ignoring the chronology. 123 * <p> 124 * This method differs from the comparison in {@link #compareTo} in that it 125 * only compares the underlying date and not the chronology. 126 * This allows dates in different calendar systems to be compared based 127 * on the time-line position. 128 * 129 * @see #isAfter 130 * @see #isBefore 131 * @see #isEqual 132 */ 133 Comparator<ChronoZonedDateTime<?>> INSTANT_COMPARATOR = new Comparator<ChronoZonedDateTime<?>>() { 134 @Override 135 public int compare(ChronoZonedDateTime<?> datetime1, ChronoZonedDateTime<?> datetime2) { 136 int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond()); 137 if (cmp == 0) { 138 cmp = Long.compare(datetime1.toLocalTime().toNanoOfDay(), datetime2.toLocalTime().toNanoOfDay()); 139 } 140 return cmp; 141 } 142 }; 143 144 @Override 145 public default ValueRange range(TemporalField field) { 146 if (field instanceof ChronoField) { 147 if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) { 148 return field.range(); 149 } 150 return toLocalDateTime().range(field); 151 } 152 return field.rangeRefinedBy(this); 153 } 154 155 @Override 156 public default int get(TemporalField field) { 157 if (field instanceof ChronoField) { 158 switch ((ChronoField) field) { 159 case INSTANT_SECONDS: throw new DateTimeException("Field too large for an int: " + field); 160 case OFFSET_SECONDS: return getOffset().getTotalSeconds(); 161 } 162 return toLocalDateTime().get(field); 163 } 164 return Temporal.super.get(field); 165 } 166 167 @Override 168 public default long getLong(TemporalField field) { 169 if (field instanceof ChronoField) { 170 switch ((ChronoField) field) { 171 case INSTANT_SECONDS: return toEpochSecond(); 172 case OFFSET_SECONDS: return getOffset().getTotalSeconds(); 173 } 174 return toLocalDateTime().getLong(field); 175 } 176 return field.getFrom(this); 177 } 178 179 /** 180 * Gets the local date part of this date-time. 181 * <p> 182 * This returns a local date with the same year, month and day 183 * as this date-time. 184 * 185 * @return the date part of this date-time, not null 186 */ 187 public default D toLocalDate() { 188 return toLocalDateTime().toLocalDate(); 189 } 190 191 /** 192 * Gets the local time part of this date-time. 193 * <p> 194 * This returns a local time with the same hour, minute, second and 195 * nanosecond as this date-time. 196 * 197 * @return the time part of this date-time, not null 198 */ 199 public default LocalTime toLocalTime() { 200 return toLocalDateTime().toLocalTime(); 201 } 202 203 /** 204 * Gets the local date-time part of this date-time. 205 * <p> 206 * This returns a local date with the same year, month and day 207 * as this date-time. 208 * 209 * @return the local date-time part of this date-time, not null 210 */ 211 ChronoLocalDateTime<D> toLocalDateTime(); 212 213 /** 214 * Gets the zone offset, such as '+01:00'. 215 * <p> 216 * This is the offset of the local date-time from UTC/Greenwich. 217 * 218 * @return the zone offset, not null 219 */ 220 ZoneOffset getOffset(); 221 222 /** 223 * Gets the zone ID, such as 'Europe/Paris'. 224 * <p> 225 * This returns the stored time-zone id used to determine the time-zone rules. 226 * 227 * @return the zone ID, not null 228 */ 229 ZoneId getZone(); 230 231 //----------------------------------------------------------------------- 232 /** 233 * Returns a copy of this date-time changing the zone offset to the 234 * earlier of the two valid offsets at a local time-line overlap. 235 * <p> 236 * This method only has any effect when the local time-line overlaps, such as 237 * at an autumn daylight savings cutover. In this scenario, there are two 238 * valid offsets for the local date-time. Calling this method will return 239 * a zoned date-time with the earlier of the two selected. 240 * <p> 241 * If this method is called when it is not an overlap, {@code this} 242 * is returned. 243 * <p> 244 * This instance is immutable and unaffected by this method call. 245 * 246 * @return a {@code ZoneChronoDateTime} based on this date-time with the earlier offset, not null 247 * @throws DateTimeException if no rules can be found for the zone 248 * @throws DateTimeException if no rules are valid for this date-time 249 */ 250 ChronoZonedDateTime<D> withEarlierOffsetAtOverlap(); 251 252 /** 253 * Returns a copy of this date-time changing the zone offset to the 254 * later of the two valid offsets at a local time-line overlap. 255 * <p> 256 * This method only has any effect when the local time-line overlaps, such as 257 * at an autumn daylight savings cutover. In this scenario, there are two 258 * valid offsets for the local date-time. Calling this method will return 259 * a zoned date-time with the later of the two selected. 260 * <p> 261 * If this method is called when it is not an overlap, {@code this} 262 * is returned. 263 * <p> 264 * This instance is immutable and unaffected by this method call. 265 * 266 * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null 267 * @throws DateTimeException if no rules can be found for the zone 268 * @throws DateTimeException if no rules are valid for this date-time 269 */ 270 ChronoZonedDateTime<D> withLaterOffsetAtOverlap(); 271 272 /** 273 * Returns a copy of this ZonedDateTime with a different time-zone, 274 * retaining the local date-time if possible. 275 * <p> 276 * This method changes the time-zone and retains the local date-time. 277 * The local date-time is only changed if it is invalid for the new zone. 278 * <p> 279 * To change the zone and adjust the local date-time, 280 * use {@link #withZoneSameInstant(ZoneId)}. 281 * <p> 282 * This instance is immutable and unaffected by this method call. 283 * 284 * @param zone the time-zone to change to, not null 285 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 286 */ 287 ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone); 288 289 /** 290 * Returns a copy of this date-time with a different time-zone, 291 * retaining the instant. 292 * <p> 293 * This method changes the time-zone and retains the instant. 294 * This normally results in a change to the local date-time. 295 * <p> 296 * This method is based on retaining the same instant, thus gaps and overlaps 297 * in the local time-line have no effect on the result. 298 * <p> 299 * To change the offset while keeping the local time, 300 * use {@link #withZoneSameLocal(ZoneId)}. 301 * 302 * @param zone the time-zone to change to, not null 303 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 304 * @throws DateTimeException if the result exceeds the supported date range 305 */ 306 ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone); 307 308 @Override // Override to provide javadoc 309 public boolean isSupported(TemporalField field); 310 311 //----------------------------------------------------------------------- 312 // override for covariant return type 313 /** 314 * {@inheritDoc} 315 * @throws DateTimeException {@inheritDoc} 316 * @throws ArithmeticException {@inheritDoc} 317 */ 318 @Override 319 public default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) { 320 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.with(adjuster))); 321 } 322 323 /** 324 * {@inheritDoc} 325 * @throws DateTimeException {@inheritDoc} 326 * @throws ArithmeticException {@inheritDoc} 327 */ 328 @Override 329 ChronoZonedDateTime<D> with(TemporalField field, long newValue); 330 331 /** 332 * {@inheritDoc} 333 * @throws DateTimeException {@inheritDoc} 334 * @throws ArithmeticException {@inheritDoc} 335 */ 336 @Override 337 public default ChronoZonedDateTime<D> plus(TemporalAmount amount) { 338 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.plus(amount))); 339 } 340 341 /** 342 * {@inheritDoc} 343 * @throws DateTimeException {@inheritDoc} 344 * @throws ArithmeticException {@inheritDoc} 345 */ 346 @Override 347 ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit); 348 349 /** 350 * {@inheritDoc} 351 * @throws DateTimeException {@inheritDoc} 352 * @throws ArithmeticException {@inheritDoc} 353 */ 354 @Override 355 public default ChronoZonedDateTime<D> minus(TemporalAmount amount) { 356 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amount))); 357 } 358 359 /** 360 * {@inheritDoc} 361 * @throws DateTimeException {@inheritDoc} 362 * @throws ArithmeticException {@inheritDoc} 363 */ 364 @Override 365 public default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { 366 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit))); 367 } 368 369 //----------------------------------------------------------------------- 370 /** 371 * Queries this date-time using the specified query. 372 * <p> 373 * This queries this date-time using the specified query strategy object. 374 * The {@code TemporalQuery} object defines the logic to be used to 375 * obtain the result. Read the documentation of the query to understand 376 * what the result of this method will be. 377 * <p> 378 * The result of this method is obtained by invoking the 379 * {@link java.time.temporal.TemporalQuery#queryFrom(TemporalAccessor)} method on the 380 * specified query passing {@code this} as the argument. 381 * 382 * @param <R> the type of the result 383 * @param query the query to invoke, not null 384 * @return the query result, null may be returned (defined by the query) 385 * @throws DateTimeException if unable to query (defined by the query) 386 * @throws ArithmeticException if numeric overflow occurs (defined by the query) 387 */ 388 @SuppressWarnings("unchecked") 389 @Override 390 public default <R> R query(TemporalQuery<R> query) { 391 if (query == Queries.zone() || query == Queries.zoneId()) { 392 return (R) getZone(); 393 } else if (query == Queries.offset()) { 394 return (R) getOffset(); 395 } else if (query == Queries.localTime()) { 396 return (R) toLocalTime(); 397 } else if (query == Queries.chronology()) { 398 return (R) toLocalDate().getChronology(); 399 } else if (query == Queries.precision()) { 400 return (R) NANOS; 401 } 402 // inline TemporalAccessor.super.query(query) as an optimization 403 // non-JDK classes are not permitted to make this optimization 404 return query.queryFrom(this); 405 } 406 407 //----------------------------------------------------------------------- 408 /** 409 * Converts this date-time to an {@code Instant}. 410 * <p> 411 * This returns an {@code Instant} representing the same point on the 412 * time-line as this date-time. The calculation combines the 413 * {@linkplain #toLocalDateTime() local date-time} and 414 * {@linkplain #getOffset() offset}. 415 * 416 * @return an {@code Instant} representing the same instant, not null 417 */ 418 public default Instant toInstant() { 419 return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano()); 420 } 421 422 /** 423 * Converts this date-time to the number of seconds from the epoch 424 * of 1970-01-01T00:00:00Z. 425 * <p> 426 * This uses the {@linkplain #toLocalDateTime() local date-time} and 427 * {@linkplain #getOffset() offset} to calculate the epoch-second value, 428 * which is the number of elapsed seconds from 1970-01-01T00:00:00Z. 429 * Instants on the time-line after the epoch are positive, earlier are negative. 430 * 431 * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z 432 */ 433 public default long toEpochSecond() { 434 long epochDay = toLocalDate().toEpochDay(); 435 long secs = epochDay * 86400 + toLocalTime().toSecondOfDay(); 436 secs -= getOffset().getTotalSeconds(); 437 return secs; 438 } 439 440 //----------------------------------------------------------------------- 441 /** 442 * Compares this date-time to another date-time, including the chronology. 443 * <p> 444 * The comparison is based first on the instant, then on the local date-time, 445 * then on the zone ID, then on the chronology. 446 * It is "consistent with equals", as defined by {@link Comparable}. 447 * <p> 448 * If all the date-time objects being compared are in the same chronology, then the 449 * additional chronology stage is not required. 450 * <p> 451 * This default implementation performs the comparison defined above. 452 * 453 * @param other the other date-time to compare to, not null 454 * @return the comparator value, negative if less, positive if greater 455 */ 456 @Override 457 public default int compareTo(ChronoZonedDateTime<?> other) { 458 int cmp = Long.compare(toEpochSecond(), other.toEpochSecond()); 459 if (cmp == 0) { 460 cmp = toLocalTime().getNano() - other.toLocalTime().getNano(); 461 if (cmp == 0) { 462 cmp = toLocalDateTime().compareTo(other.toLocalDateTime()); 463 if (cmp == 0) { 464 cmp = getZone().getId().compareTo(other.getZone().getId()); 465 if (cmp == 0) { 466 cmp = toLocalDate().getChronology().compareTo(other.toLocalDate().getChronology()); 467 } 468 } 469 } 470 } 471 return cmp; 472 } 473 474 /** 475 * Checks if the instant of this date-time is before that of the specified date-time. 476 * <p> 477 * This method differs from the comparison in {@link #compareTo} in that it 478 * only compares the instant of the date-time. This is equivalent to using 479 * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}. 480 * <p> 481 * This default implementation performs the comparison based on the epoch-second 482 * and nano-of-second. 483 * 484 * @param other the other date-time to compare to, not null 485 * @return true if this point is before the specified date-time 486 */ 487 public default boolean isBefore(ChronoZonedDateTime<?> other) { 488 long thisEpochSec = toEpochSecond(); 489 long otherEpochSec = other.toEpochSecond(); 490 return thisEpochSec < otherEpochSec || 491 (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano()); 492 } 493 494 /** 495 * Checks if the instant of this date-time is after that of the specified date-time. 496 * <p> 497 * This method differs from the comparison in {@link #compareTo} in that it 498 * only compares the instant of the date-time. This is equivalent to using 499 * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}. 500 * <p> 501 * This default implementation performs the comparison based on the epoch-second 502 * and nano-of-second. 503 * 504 * @param other the other date-time to compare to, not null 505 * @return true if this is after the specified date-time 506 */ 507 public default boolean isAfter(ChronoZonedDateTime<?> other) { 508 long thisEpochSec = toEpochSecond(); 509 long otherEpochSec = other.toEpochSecond(); 510 return thisEpochSec > otherEpochSec || 511 (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano()); 512 } 513 514 /** 515 * Checks if the instant of this date-time is equal to that of the specified date-time. 516 * <p> 517 * This method differs from the comparison in {@link #compareTo} and {@link #equals} 518 * in that it only compares the instant of the date-time. This is equivalent to using 519 * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}. 520 * <p> 521 * This default implementation performs the comparison based on the epoch-second 522 * and nano-of-second. 523 * 524 * @param other the other date-time to compare to, not null 525 * @return true if the instant equals the instant of the specified date-time 526 */ 527 public default boolean isEqual(ChronoZonedDateTime<?> other) { 528 return toEpochSecond() == other.toEpochSecond() && 529 toLocalTime().getNano() == other.toLocalTime().getNano(); 530 } 531 532 //----------------------------------------------------------------------- 533 /** 534 * Checks if this date-time is equal to another date-time. 535 * <p> 536 * The comparison is based on the offset date-time and the zone. 537 * To compare for the same instant on the time-line, use {@link #compareTo}. 538 * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false. 539 * 540 * @param obj the object to check, null returns false 541 * @return true if this is equal to the other date-time 542 */ 543 @Override 544 boolean equals(Object obj); 545 546 /** 547 * A hash code for this date-time. 548 * 549 * @return a suitable hash code 550 */ 551 @Override 552 int hashCode(); 553 554 //----------------------------------------------------------------------- 555 /** 556 * Outputs this date-time as a {@code String}. 557 * <p> 558 * The output will include the full zoned date-time and the chronology ID. 559 * 560 * @return a string representation of this date-time, not null 561 */ 562 @Override 563 String toString(); 564 565 /** 566 * Outputs this date-time as a {@code String} using the formatter. 567 * <p> 568 * The default implementation must behave as follows: 569 * <pre> 570 * return formatter.format(this); 571 * </pre> 572 * 573 * @param formatter the formatter to use, not null 574 * @return the formatted date-time string, not null 575 * @throws DateTimeException if an error occurs during printing 576 */ 577 public default String toString(DateTimeFormatter formatter) { 578 Objects.requireNonNull(formatter, "formatter"); 579 return formatter.format(this); 580 } 581 582 } |