1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp.chrono; 33 34 import static org.threeten.bp.temporal.ChronoField.EPOCH_DAY; 35 import static org.threeten.bp.temporal.ChronoField.NANO_OF_DAY; 36 import static org.threeten.bp.temporal.ChronoUnit.NANOS; 37 38 import java.util.Comparator; 39 40 import org.threeten.bp.DateTimeException; 41 import org.threeten.bp.Instant; 42 import org.threeten.bp.LocalDate; 43 import org.threeten.bp.LocalDateTime; 44 import org.threeten.bp.LocalTime; 45 import org.threeten.bp.ZoneId; 46 import org.threeten.bp.ZoneOffset; 47 import org.threeten.bp.format.DateTimeFormatter; 48 import org.threeten.bp.jdk8.DefaultInterfaceTemporal; 49 import org.threeten.bp.jdk8.Jdk8Methods; 50 import org.threeten.bp.temporal.ChronoField; 51 import org.threeten.bp.temporal.Temporal; 52 import org.threeten.bp.temporal.TemporalAccessor; 53 import org.threeten.bp.temporal.TemporalAdjuster; 54 import org.threeten.bp.temporal.TemporalAmount; 55 import org.threeten.bp.temporal.TemporalField; 56 import org.threeten.bp.temporal.TemporalQueries; 57 import org.threeten.bp.temporal.TemporalQuery; 58 import org.threeten.bp.temporal.TemporalUnit; 59 import org.threeten.bp.zone.ZoneRules; 60 61 /** 62 * A date-time without a time-zone in an arbitrary chronology, intended 63 * for advanced globalization use cases. 64 * <p> 65 * <b>Most applications should declare method signatures, fields and variables 66 * as {@link LocalDateTime}, not this interface.</b> 67 * <p> 68 * A {@code ChronoLocalDateTime} is the abstract representation of a local date-time 69 * where the {@code Chronology chronology}, or calendar system, is pluggable. 70 * The date-time is defined in terms of fields expressed by {@link TemporalField}, 71 * where most common implementations are defined in {@link ChronoField}. 72 * The chronology defines how the calendar system operates and the meaning of 73 * the standard fields. 74 * 75 * <h4>When to use this interface</h4> 76 * The design of the API encourages the use of {@code LocalDateTime} rather than this 77 * interface, even in the case where the application needs to deal with multiple 78 * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. 79 * <p> 80 * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood 81 * before using this interface. 82 * 83 * <h3>Specification for implementors</h3> 84 * This interface must be implemented with care to ensure other classes operate correctly. 85 * All implementations that can be instantiated must be final, immutable and thread-safe. 86 * Subclasses should be Serializable wherever possible. 87 * <p> 88 * In JDK 8, this is an interface with default methods. 89 * Since there are no default methods in JDK 7, an abstract class is used. 90 * 91 * @param <D> the date type 92 */ 93 public abstract class ChronoLocalDateTime<D extends ChronoLocalDate> 94 extends DefaultInterfaceTemporal 95 implements Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> { 96 97 /** 98 * Gets a comparator that compares {@code ChronoLocalDateTime} in 99 * time-line order ignoring the chronology. 100 * <p> 101 * This comparator differs from the comparison in {@link #compareTo} in that it 102 * only compares the underlying date-time and not the chronology. 103 * This allows dates in different calendar systems to be compared based 104 * on the position of the date-time on the local time-line. 105 * The underlying comparison is equivalent to comparing the epoch-day and nano-of-day. 106 * 107 * @return a comparator that compares in time-line order ignoring the chronology 108 * @see #isAfter 109 * @see #isBefore 110 * @see #isEqual 111 */ timeLineOrder()112 public static Comparator<ChronoLocalDateTime<?>> timeLineOrder() { 113 return DATE_TIME_COMPARATOR; 114 } 115 private static final Comparator<ChronoLocalDateTime<?>> DATE_TIME_COMPARATOR = 116 new Comparator<ChronoLocalDateTime<?>>() { 117 @Override 118 public int compare(ChronoLocalDateTime<?> datetime1, ChronoLocalDateTime<?> datetime2) { 119 int cmp = Jdk8Methods.compareLongs(datetime1.toLocalDate().toEpochDay(), datetime2.toLocalDate().toEpochDay()); 120 if (cmp == 0) { 121 cmp = Jdk8Methods.compareLongs(datetime1.toLocalTime().toNanoOfDay(), datetime2.toLocalTime().toNanoOfDay()); 122 } 123 return cmp; 124 } 125 }; 126 127 //----------------------------------------------------------------------- 128 /** 129 * Obtains an instance of {@code ChronoLocalDateTime} from a temporal object. 130 * <p> 131 * This obtains a local date-time based on the specified temporal. 132 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 133 * which this factory converts to an instance of {@code ChronoLocalDateTime}. 134 * <p> 135 * The conversion extracts and combines the chronology and the date-time 136 * from the temporal object. The behavior is equivalent to using 137 * {@link Chronology#localDateTime(TemporalAccessor)} with the extracted chronology. 138 * Implementations are permitted to perform optimizations such as accessing 139 * those fields that are equivalent to the relevant objects. 140 * <p> 141 * This method matches the signature of the functional interface {@link TemporalQuery} 142 * allowing it to be used as a query via method reference, {@code ChronoLocalDateTime::from}. 143 * 144 * @param temporal the temporal object to convert, not null 145 * @return the date-time, not null 146 * @throws DateTimeException if unable to convert to a {@code ChronoLocalDateTime} 147 * @see Chronology#localDateTime(TemporalAccessor) 148 */ from(TemporalAccessor temporal)149 public static ChronoLocalDateTime<?> from(TemporalAccessor temporal) { 150 Jdk8Methods.requireNonNull(temporal, "temporal"); 151 if (temporal instanceof ChronoLocalDateTime) { 152 return (ChronoLocalDateTime<?>) temporal; 153 } 154 Chronology chrono = temporal.query(TemporalQueries.chronology()); 155 if (chrono == null) { 156 throw new DateTimeException("No Chronology found to create ChronoLocalDateTime: " + temporal.getClass()); 157 } 158 return chrono.localDateTime(temporal); 159 } 160 161 //----------------------------------------------------------------------- 162 /** 163 * Gets the chronology of this date-time. 164 * <p> 165 * The {@code Chronology} represents the calendar system in use. 166 * The era and other fields in {@link ChronoField} are defined by the chronology. 167 * 168 * @return the chronology, not null 169 */ getChronology()170 public Chronology getChronology() { 171 return toLocalDate().getChronology(); 172 } 173 174 /** 175 * Gets the local date part of this date-time. 176 * <p> 177 * This returns a local date with the same year, month and day 178 * as this date-time. 179 * 180 * @return the date part of this date-time, not null 181 */ toLocalDate()182 public abstract D toLocalDate() ; 183 184 /** 185 * Gets the local time part of this date-time. 186 * <p> 187 * This returns a local time with the same hour, minute, second and 188 * nanosecond as this date-time. 189 * 190 * @return the time part of this date-time, not null 191 */ toLocalTime()192 public abstract LocalTime toLocalTime(); 193 194 //------------------------------------------------------------------------- 195 // override for covariant return type 196 @Override with(TemporalAdjuster adjuster)197 public ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) { 198 return toLocalDate().getChronology().ensureChronoLocalDateTime(super.with(adjuster)); 199 } 200 201 @Override with(TemporalField field, long newValue)202 public abstract ChronoLocalDateTime<D> with(TemporalField field, long newValue); 203 204 @Override plus(TemporalAmount amount)205 public ChronoLocalDateTime<D> plus(TemporalAmount amount) { 206 return toLocalDate().getChronology().ensureChronoLocalDateTime(super.plus(amount)); 207 } 208 209 @Override plus(long amountToAdd, TemporalUnit unit)210 public abstract ChronoLocalDateTime<D> plus(long amountToAdd, TemporalUnit unit); 211 212 @Override minus(TemporalAmount amount)213 public ChronoLocalDateTime<D> minus(TemporalAmount amount) { 214 return toLocalDate().getChronology().ensureChronoLocalDateTime(super.minus(amount)); 215 } 216 217 @Override minus(long amountToSubtract, TemporalUnit unit)218 public ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { 219 return toLocalDate().getChronology().ensureChronoLocalDateTime(super.minus(amountToSubtract, unit)); 220 } 221 222 //----------------------------------------------------------------------- 223 @SuppressWarnings("unchecked") 224 @Override query(TemporalQuery<R> query)225 public <R> R query(TemporalQuery<R> query) { 226 if (query == TemporalQueries.chronology()) { 227 return (R) getChronology(); 228 } else if (query == TemporalQueries.precision()) { 229 return (R) NANOS; 230 } else if (query == TemporalQueries.localDate()) { 231 return (R) LocalDate.ofEpochDay(toLocalDate().toEpochDay()); 232 } else if (query == TemporalQueries.localTime()) { 233 return (R) toLocalTime(); 234 } else if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId() || query == TemporalQueries.offset()) { 235 return null; 236 } 237 return super.query(query); 238 } 239 240 @Override adjustInto(Temporal temporal)241 public Temporal adjustInto(Temporal temporal) { 242 return temporal 243 .with(EPOCH_DAY, toLocalDate().toEpochDay()) 244 .with(NANO_OF_DAY, toLocalTime().toNanoOfDay()); 245 } 246 247 /** 248 * Formats this date-time using the specified formatter. 249 * <p> 250 * This date-time will be passed to the formatter to produce a string. 251 * <p> 252 * The default implementation must behave as follows: 253 * <pre> 254 * return formatter.format(this); 255 * </pre> 256 * 257 * @param formatter the formatter to use, not null 258 * @return the formatted date-time string, not null 259 * @throws DateTimeException if an error occurs during printing 260 */ format(DateTimeFormatter formatter)261 public String format(DateTimeFormatter formatter) { 262 Jdk8Methods.requireNonNull(formatter, "formatter"); 263 return formatter.format(this); 264 } 265 266 //----------------------------------------------------------------------- 267 /** 268 * Combines this time with a time-zone to create a {@code ChronoZonedDateTime}. 269 * <p> 270 * This returns a {@code ChronoZonedDateTime} formed from this date-time at the 271 * specified time-zone. The result will match this date-time as closely as possible. 272 * Time-zone rules, such as daylight savings, mean that not every local date-time 273 * is valid for the specified zone, thus the local date-time may be adjusted. 274 * <p> 275 * The local date-time is resolved to a single instant on the time-line. 276 * This is achieved by finding a valid offset from UTC/Greenwich for the local 277 * date-time as defined by the {@link ZoneRules rules} of the zone ID. 278 *<p> 279 * In most cases, there is only one valid offset for a local date-time. 280 * In the case of an overlap, where clocks are set back, there are two valid offsets. 281 * This method uses the earlier offset typically corresponding to "summer". 282 * <p> 283 * In the case of a gap, where clocks jump forward, there is no valid offset. 284 * Instead, the local date-time is adjusted to be later by the length of the gap. 285 * For a typical one hour daylight savings change, the local date-time will be 286 * moved one hour later into the offset typically corresponding to "summer". 287 * <p> 288 * To obtain the later offset during an overlap, call 289 * {@link ChronoZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method. 290 * 291 * @param zone the time-zone to use, not null 292 * @return the zoned date-time formed from this date-time, not null 293 */ atZone(ZoneId zone)294 public abstract ChronoZonedDateTime<D> atZone(ZoneId zone); 295 296 //----------------------------------------------------------------------- 297 /** 298 * Converts this date-time to an {@code Instant}. 299 * <p> 300 * This combines this local date-time and the specified offset to form 301 * an {@code Instant}. 302 * 303 * @param offset the offset to use for the conversion, not null 304 * @return an {@code Instant} representing the same instant, not null 305 */ toInstant(ZoneOffset offset)306 public Instant toInstant(ZoneOffset offset) { 307 return Instant.ofEpochSecond(toEpochSecond(offset), toLocalTime().getNano()); 308 } 309 310 /** 311 * Converts this date-time to the number of seconds from the epoch 312 * of 1970-01-01T00:00:00Z. 313 * <p> 314 * This combines this local date-time and the specified offset to calculate the 315 * epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z. 316 * Instants on the time-line after the epoch are positive, earlier are negative. 317 * 318 * @param offset the offset to use for the conversion, not null 319 * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z 320 */ toEpochSecond(ZoneOffset offset)321 public long toEpochSecond(ZoneOffset offset) { 322 Jdk8Methods.requireNonNull(offset, "offset"); 323 long epochDay = toLocalDate().toEpochDay(); 324 long secs = epochDay * 86400 + toLocalTime().toSecondOfDay(); 325 secs -= offset.getTotalSeconds(); 326 return secs; 327 } 328 329 //----------------------------------------------------------------------- 330 /** 331 * Compares this date-time to another date-time, including the chronology. 332 * <p> 333 * The comparison is based first on the underlying time-line date-time, then 334 * on the chronology. 335 * It is "consistent with equals", as defined by {@link Comparable}. 336 * <p> 337 * For example, the following is the comparator order: 338 * <ol> 339 * <li>{@code 2012-12-03T12:00 (ISO)}</li> 340 * <li>{@code 2012-12-04T12:00 (ISO)}</li> 341 * <li>{@code 2555-12-04T12:00 (ThaiBuddhist)}</li> 342 * <li>{@code 2012-12-05T12:00 (ISO)}</li> 343 * </ol> 344 * Values #2 and #3 represent the same date-time on the time-line. 345 * When two values represent the same date-time, the chronology ID is compared to distinguish them. 346 * This step is needed to make the ordering "consistent with equals". 347 * <p> 348 * If all the date-time objects being compared are in the same chronology, then the 349 * additional chronology stage is not required and only the local date-time is used. 350 * 351 * @param other the other date-time to compare to, not null 352 * @return the comparator value, negative if less, positive if greater 353 */ 354 @Override compareTo(ChronoLocalDateTime<?> other)355 public int compareTo(ChronoLocalDateTime<?> other) { 356 int cmp = toLocalDate().compareTo(other.toLocalDate()); 357 if (cmp == 0) { 358 cmp = toLocalTime().compareTo(other.toLocalTime()); 359 if (cmp == 0) { 360 cmp = getChronology().compareTo(other.getChronology()); 361 } 362 } 363 return cmp; 364 } 365 366 /** 367 * Checks if this date-time is after the specified date-time ignoring the chronology. 368 * <p> 369 * This method differs from the comparison in {@link #compareTo} in that it 370 * only compares the underlying date-time and not the chronology. 371 * This allows dates in different calendar systems to be compared based 372 * on the time-line position. 373 * 374 * @param other the other date-time to compare to, not null 375 * @return true if this is after the specified date-time 376 */ isAfter(ChronoLocalDateTime<?> other)377 public boolean isAfter(ChronoLocalDateTime<?> other) { 378 long thisEpDay = this.toLocalDate().toEpochDay(); 379 long otherEpDay = other.toLocalDate().toEpochDay(); 380 return thisEpDay > otherEpDay || 381 (thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() > other.toLocalTime().toNanoOfDay()); 382 } 383 384 /** 385 * Checks if this date-time is before the specified date-time ignoring the chronology. 386 * <p> 387 * This method differs from the comparison in {@link #compareTo} in that it 388 * only compares the underlying date-time and not the chronology. 389 * This allows dates in different calendar systems to be compared based 390 * on the time-line position. 391 * 392 * @param other the other date-time to compare to, not null 393 * @return true if this is before the specified date-time 394 */ isBefore(ChronoLocalDateTime<?> other)395 public boolean isBefore(ChronoLocalDateTime<?> other) { 396 long thisEpDay = this.toLocalDate().toEpochDay(); 397 long otherEpDay = other.toLocalDate().toEpochDay(); 398 return thisEpDay < otherEpDay || 399 (thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() < other.toLocalTime().toNanoOfDay()); 400 } 401 402 /** 403 * Checks if this date-time is equal to the specified date-time ignoring the chronology. 404 * <p> 405 * This method differs from the comparison in {@link #compareTo} in that it 406 * only compares the underlying date and time and not the chronology. 407 * This allows date-times in different calendar systems to be compared based 408 * on the time-line position. 409 * 410 * @param other the other date-time to compare to, not null 411 * @return true if the underlying date-time is equal to the specified date-time on the timeline 412 */ isEqual(ChronoLocalDateTime<?> other)413 public boolean isEqual(ChronoLocalDateTime<?> other) { 414 // Do the time check first, it is cheaper than computing EPOCH day. 415 return this.toLocalTime().toNanoOfDay() == other.toLocalTime().toNanoOfDay() && 416 this.toLocalDate().toEpochDay() == other.toLocalDate().toEpochDay(); 417 } 418 419 //----------------------------------------------------------------------- 420 /** 421 * Checks if this date-time is equal to another date-time, including the chronology. 422 * <p> 423 * Compares this date-time with another ensuring that the date-time and chronology are the same. 424 * 425 * @param obj the object to check, null returns false 426 * @return true if this is equal to the other date 427 */ 428 @Override equals(Object obj)429 public boolean equals(Object obj) { 430 if (this == obj) { 431 return true; 432 } 433 if (obj instanceof ChronoLocalDateTime) { 434 return compareTo((ChronoLocalDateTime<?>) obj) == 0; 435 } 436 return false; 437 } 438 439 /** 440 * A hash code for this date-time. 441 * 442 * @return a suitable hash code 443 */ 444 @Override hashCode()445 public int hashCode() { 446 return toLocalDate().hashCode() ^ toLocalTime().hashCode(); 447 } 448 449 //----------------------------------------------------------------------- 450 /** 451 * Outputs this date-time as a {@code String}. 452 * <p> 453 * The output will include the full local date-time and the chronology ID. 454 * 455 * @return a string representation of this date-time, not null 456 */ 457 @Override toString()458 public String toString() { 459 return toLocalDate().toString() + 'T' + toLocalTime().toString(); 460 } 461 462 } 463