1 /* 2 * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.chrono; 63 64 import static java.time.temporal.ChronoField.EPOCH_DAY; 65 66 import java.io.IOException; 67 import java.io.InvalidObjectException; 68 import java.io.ObjectInput; 69 import java.io.ObjectInputStream; 70 import java.io.ObjectOutput; 71 import java.io.Serializable; 72 import java.time.LocalTime; 73 import java.time.ZoneId; 74 import java.time.temporal.ChronoField; 75 import java.time.temporal.ChronoUnit; 76 import java.time.temporal.Temporal; 77 import java.time.temporal.TemporalAdjuster; 78 import java.time.temporal.TemporalField; 79 import java.time.temporal.TemporalUnit; 80 import java.time.temporal.ValueRange; 81 import java.util.Objects; 82 83 /** 84 * A date-time without a time-zone for the calendar neutral API. 85 * <p> 86 * {@code ChronoLocalDateTime} is an immutable date-time object that represents a date-time, often 87 * viewed as year-month-day-hour-minute-second. This object can also access other 88 * fields such as day-of-year, day-of-week and week-of-year. 89 * <p> 90 * This class stores all date and time fields, to a precision of nanoseconds. 91 * It does not store or represent a time-zone. For example, the value 92 * "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}. 93 * 94 * @implSpec 95 * This class is immutable and thread-safe. 96 * @serial 97 * @param <D> the concrete type for the date of this date-time 98 * @since 1.8 99 */ 100 final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate> 101 implements ChronoLocalDateTime<D>, Temporal, TemporalAdjuster, Serializable { 102 103 /** 104 * Serialization version. 105 */ 106 @java.io.Serial 107 private static final long serialVersionUID = 4556003607393004514L; 108 /** 109 * Hours per day. 110 */ 111 static final int HOURS_PER_DAY = 24; 112 /** 113 * Minutes per hour. 114 */ 115 static final int MINUTES_PER_HOUR = 60; 116 /** 117 * Minutes per day. 118 */ 119 static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY; 120 /** 121 * Seconds per minute. 122 */ 123 static final int SECONDS_PER_MINUTE = 60; 124 /** 125 * Seconds per hour. 126 */ 127 static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; 128 /** 129 * Seconds per day. 130 */ 131 static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; 132 /** 133 * Milliseconds per day. 134 */ 135 static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L; 136 /** 137 * Microseconds per day. 138 */ 139 static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L; 140 /** 141 * Nanos per second. 142 */ 143 static final long NANOS_PER_SECOND = 1000_000_000L; 144 /** 145 * Nanos per minute. 146 */ 147 static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE; 148 /** 149 * Nanos per hour. 150 */ 151 static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR; 152 /** 153 * Nanos per day. 154 */ 155 static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY; 156 157 /** 158 * The date part. 159 */ 160 private final transient D date; 161 /** 162 * The time part. 163 */ 164 private final transient LocalTime time; 165 166 //----------------------------------------------------------------------- 167 /** 168 * Obtains an instance of {@code ChronoLocalDateTime} from a date and time. 169 * 170 * @param date the local date, not null 171 * @param time the local time, not null 172 * @return the local date-time, not null 173 */ 174 static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> of(R date, LocalTime time) { 175 return new ChronoLocalDateTimeImpl<>(date, time); 176 } 177 178 /** 179 * Casts the {@code Temporal} to {@code ChronoLocalDateTime} ensuring it bas the specified chronology. 180 * 181 * @param chrono the chronology to check for, not null 182 * @param temporal a date-time to cast, not null 183 * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null 184 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl 185 * or the chronology is not equal this Chronology 186 */ 187 static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) { 188 @SuppressWarnings("unchecked") 189 ChronoLocalDateTimeImpl<R> other = (ChronoLocalDateTimeImpl<R>) temporal; 190 if (chrono.equals(other.getChronology()) == false) { 191 throw new ClassCastException("Chronology mismatch, required: " + chrono.getId() 192 + ", actual: " + other.getChronology().getId()); 193 } 194 return other; 195 } 196 197 /** 198 * Constructor. 199 * 200 * @param date the date part of the date-time, not null 201 * @param time the time part of the date-time, not null 202 */ 203 private ChronoLocalDateTimeImpl(D date, LocalTime time) { 204 Objects.requireNonNull(date, "date"); 205 Objects.requireNonNull(time, "time"); 206 this.date = date; 207 this.time = time; 208 } 209 210 /** 211 * Returns a copy of this date-time with the new date and time, checking 212 * to see if a new object is in fact required. 213 * 214 * @param newDate the date of the new date-time, not null 215 * @param newTime the time of the new date-time, not null 216 * @return the date-time, not null 217 */ 218 private ChronoLocalDateTimeImpl<D> with(Temporal newDate, LocalTime newTime) { 219 if (date == newDate && time == newTime) { 220 return this; 221 } 222 // Validate that the new Temporal is a ChronoLocalDate (and not something else) 223 D cd = ChronoLocalDateImpl.ensureValid(date.getChronology(), newDate); 224 return new ChronoLocalDateTimeImpl<>(cd, newTime); 225 } 226 227 //----------------------------------------------------------------------- 228 @Override 229 public D toLocalDate() { 230 return date; 231 } 232 233 @Override 234 public LocalTime toLocalTime() { 235 return time; 236 } 237 238 //----------------------------------------------------------------------- 239 @Override 240 public boolean isSupported(TemporalField field) { 241 if (field instanceof ChronoField) { 242 ChronoField f = (ChronoField) field; 243 return f.isDateBased() || f.isTimeBased(); 244 } 245 return field != null && field.isSupportedBy(this); 246 } 247 248 @Override 249 public ValueRange range(TemporalField field) { 250 if (field instanceof ChronoField) { 251 ChronoField f = (ChronoField) field; 252 return (f.isTimeBased() ? time.range(field) : date.range(field)); 253 } 254 return field.rangeRefinedBy(this); 255 } 256 257 @Override 258 public int get(TemporalField field) { 259 if (field instanceof ChronoField) { 260 ChronoField f = (ChronoField) field; 261 return (f.isTimeBased() ? time.get(field) : date.get(field)); 262 } 263 return range(field).checkValidIntValue(getLong(field), field); 264 } 265 266 @Override 267 public long getLong(TemporalField field) { 268 if (field instanceof ChronoField) { 269 ChronoField f = (ChronoField) field; 270 return (f.isTimeBased() ? time.getLong(field) : date.getLong(field)); 271 } 272 return field.getFrom(this); 273 } 274 275 //----------------------------------------------------------------------- 276 @SuppressWarnings("unchecked") 277 @Override 278 public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) { 279 if (adjuster instanceof ChronoLocalDate) { 280 // The Chronology is checked in with(date,time) 281 return with((ChronoLocalDate) adjuster, time); 282 } else if (adjuster instanceof LocalTime) { 283 return with(date, (LocalTime) adjuster); 284 } else if (adjuster instanceof ChronoLocalDateTimeImpl) { 285 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster); 286 } 287 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this)); 288 } 289 290 @Override 291 public ChronoLocalDateTimeImpl<D> with(TemporalField field, long newValue) { 292 if (field instanceof ChronoField) { 293 ChronoField f = (ChronoField) field; 294 if (f.isTimeBased()) { 295 return with(date, time.with(field, newValue)); 296 } else { 297 return with(date.with(field, newValue), time); 298 } 299 } 300 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue)); 301 } 302 303 //----------------------------------------------------------------------- 304 @Override 305 public ChronoLocalDateTimeImpl<D> plus(long amountToAdd, TemporalUnit unit) { 306 if (unit instanceof ChronoUnit) { 307 ChronoUnit f = (ChronoUnit) unit; 308 switch (f) { 309 case NANOS: return plusNanos(amountToAdd); 310 case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000); 311 case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000); 312 case SECONDS: return plusSeconds(amountToAdd); 313 case MINUTES: return plusMinutes(amountToAdd); 314 case HOURS: return plusHours(amountToAdd); 315 case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2) 316 } 317 return with(date.plus(amountToAdd, unit), time); 318 } 319 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd)); 320 } 321 322 private ChronoLocalDateTimeImpl<D> plusDays(long days) { 323 return with(date.plus(days, ChronoUnit.DAYS), time); 324 } 325 326 private ChronoLocalDateTimeImpl<D> plusHours(long hours) { 327 return plusWithOverflow(date, hours, 0, 0, 0); 328 } 329 330 private ChronoLocalDateTimeImpl<D> plusMinutes(long minutes) { 331 return plusWithOverflow(date, 0, minutes, 0, 0); 332 } 333 334 ChronoLocalDateTimeImpl<D> plusSeconds(long seconds) { 335 return plusWithOverflow(date, 0, 0, seconds, 0); 336 } 337 338 private ChronoLocalDateTimeImpl<D> plusNanos(long nanos) { 339 return plusWithOverflow(date, 0, 0, 0, nanos); 340 } 341 342 //----------------------------------------------------------------------- 343 private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) { 344 // 9223372036854775808 long, 2147483648 int 345 if ((hours | minutes | seconds | nanos) == 0) { 346 return with(newDate, time); 347 } 348 long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B 349 seconds / SECONDS_PER_DAY + // max/24*60*60 350 minutes / MINUTES_PER_DAY + // max/24*60 351 hours / HOURS_PER_DAY; // max/24 352 long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000 353 (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000 354 (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000 355 (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000 356 long curNoD = time.toNanoOfDay(); // max 86400000000000 357 totNanos = totNanos + curNoD; // total 432000000000000 358 totDays += Math.floorDiv(totNanos, NANOS_PER_DAY); 359 long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY); 360 LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD)); 361 return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime); 362 } 363 364 //----------------------------------------------------------------------- 365 @Override 366 public ChronoZonedDateTime<D> atZone(ZoneId zone) { 367 return ChronoZonedDateTimeImpl.ofBest(this, zone, null); 368 } 369 370 //----------------------------------------------------------------------- 371 @Override 372 public long until(Temporal endExclusive, TemporalUnit unit) { 373 Objects.requireNonNull(endExclusive, "endExclusive"); 374 @SuppressWarnings("unchecked") 375 ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) getChronology().localDateTime(endExclusive); 376 if (unit instanceof ChronoUnit) { 377 if (unit.isTimeBased()) { 378 long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); 379 switch ((ChronoUnit) unit) { 380 case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break; 381 case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break; 382 case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break; 383 case SECONDS: amount = Math.multiplyExact(amount, SECONDS_PER_DAY); break; 384 case MINUTES: amount = Math.multiplyExact(amount, MINUTES_PER_DAY); break; 385 case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break; 386 case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break; 387 } 388 return Math.addExact(amount, time.until(end.toLocalTime(), unit)); 389 } 390 ChronoLocalDate endDate = end.toLocalDate(); 391 if (end.toLocalTime().isBefore(time)) { 392 endDate = endDate.minus(1, ChronoUnit.DAYS); 393 } 394 return date.until(endDate, unit); 395 } 396 Objects.requireNonNull(unit, "unit"); 397 return unit.between(this, end); 398 } 399 400 //----------------------------------------------------------------------- 401 /** 402 * Writes the ChronoLocalDateTime using a 403 * <a href="{@docRoot}/serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. 404 * @serialData 405 * <pre> 406 * out.writeByte(2); // identifies a ChronoLocalDateTime 407 * out.writeObject(toLocalDate()); 408 * out.writeObject(toLocalTime()); 409 * </pre> 410 * 411 * @return the instance of {@code Ser}, not null 412 */ 413 @java.io.Serial 414 private Object writeReplace() { 415 return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this); 416 } 417 418 /** 419 * Defend against malicious streams. 420 * 421 * @param s the stream to read 422 * @throws InvalidObjectException always 423 */ 424 @java.io.Serial 425 private void readObject(ObjectInputStream s) throws InvalidObjectException { 426 throw new InvalidObjectException("Deserialization via serialization delegate"); 427 } 428 429 void writeExternal(ObjectOutput out) throws IOException { 430 out.writeObject(date); 431 out.writeObject(time); 432 } 433 434 static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 435 ChronoLocalDate date = (ChronoLocalDate) in.readObject(); 436 LocalTime time = (LocalTime) in.readObject(); 437 return date.atTime(time); 438 } 439 440 //----------------------------------------------------------------------- 441 @Override 442 public boolean equals(Object obj) { 443 if (this == obj) { 444 return true; 445 } 446 if (obj instanceof ChronoLocalDateTime) { 447 return compareTo((ChronoLocalDateTime<?>) obj) == 0; 448 } 449 return false; 450 } 451 452 @Override 453 public int hashCode() { 454 return toLocalDate().hashCode() ^ toLocalTime().hashCode(); 455 } 456 457 @Override 458 public String toString() { 459 return toLocalDate().toString() + 'T' + toLocalTime().toString(); 460 } 461 462 }