1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.chrono; 63 64 import static java.time.temporal.ChronoField.INSTANT_SECONDS; 65 import static java.time.temporal.ChronoField.OFFSET_SECONDS; 66 import static java.time.temporal.ChronoUnit.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 }