1 /* 2 * Copyright (c) 2012, 2020, 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 */ of(R date, LocalTime time)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 */ ensureValid(Chronology chrono, Temporal temporal)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 */ ChronoLocalDateTimeImpl(D date, LocalTime time)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 */ with(Temporal newDate, LocalTime newTime)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 toLocalDate()229 public D toLocalDate() { 230 return date; 231 } 232 233 @Override toLocalTime()234 public LocalTime toLocalTime() { 235 return time; 236 } 237 238 //----------------------------------------------------------------------- 239 @Override isSupported(TemporalField field)240 public boolean isSupported(TemporalField field) { 241 if (field instanceof ChronoField chronoField) { 242 return chronoField.isDateBased() || chronoField.isTimeBased(); 243 } 244 return field != null && field.isSupportedBy(this); 245 } 246 247 @Override range(TemporalField field)248 public ValueRange range(TemporalField field) { 249 if (field instanceof ChronoField chronoField) { 250 return (chronoField.isTimeBased() ? time.range(field) : date.range(field)); 251 } 252 return field.rangeRefinedBy(this); 253 } 254 255 @Override get(TemporalField field)256 public int get(TemporalField field) { 257 if (field instanceof ChronoField chronoField) { 258 return (chronoField.isTimeBased() ? time.get(field) : date.get(field)); 259 } 260 return range(field).checkValidIntValue(getLong(field), field); 261 } 262 263 @Override getLong(TemporalField field)264 public long getLong(TemporalField field) { 265 if (field instanceof ChronoField chronoField) { 266 return (chronoField.isTimeBased() ? time.getLong(field) : date.getLong(field)); 267 } 268 return field.getFrom(this); 269 } 270 271 //----------------------------------------------------------------------- 272 @SuppressWarnings("unchecked") 273 @Override with(TemporalAdjuster adjuster)274 public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) { 275 if (adjuster instanceof ChronoLocalDate) { 276 // The Chronology is checked in with(date,time) 277 return with((ChronoLocalDate) adjuster, time); 278 } else if (adjuster instanceof LocalTime) { 279 return with(date, (LocalTime) adjuster); 280 } else if (adjuster instanceof ChronoLocalDateTimeImpl) { 281 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster); 282 } 283 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this)); 284 } 285 286 @Override with(TemporalField field, long newValue)287 public ChronoLocalDateTimeImpl<D> with(TemporalField field, long newValue) { 288 if (field instanceof ChronoField chronoField) { 289 if (chronoField.isTimeBased()) { 290 return with(date, time.with(field, newValue)); 291 } else { 292 return with(date.with(field, newValue), time); 293 } 294 } 295 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue)); 296 } 297 298 //----------------------------------------------------------------------- 299 @Override plus(long amountToAdd, TemporalUnit unit)300 public ChronoLocalDateTimeImpl<D> plus(long amountToAdd, TemporalUnit unit) { 301 if (unit instanceof ChronoUnit chronoUnit) { 302 switch (chronoUnit) { 303 case NANOS: return plusNanos(amountToAdd); 304 case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000); 305 case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000); 306 case SECONDS: return plusSeconds(amountToAdd); 307 case MINUTES: return plusMinutes(amountToAdd); 308 case HOURS: return plusHours(amountToAdd); 309 case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2) 310 } 311 return with(date.plus(amountToAdd, unit), time); 312 } 313 return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd)); 314 } 315 plusDays(long days)316 private ChronoLocalDateTimeImpl<D> plusDays(long days) { 317 return with(date.plus(days, ChronoUnit.DAYS), time); 318 } 319 plusHours(long hours)320 private ChronoLocalDateTimeImpl<D> plusHours(long hours) { 321 return plusWithOverflow(date, hours, 0, 0, 0); 322 } 323 plusMinutes(long minutes)324 private ChronoLocalDateTimeImpl<D> plusMinutes(long minutes) { 325 return plusWithOverflow(date, 0, minutes, 0, 0); 326 } 327 plusSeconds(long seconds)328 ChronoLocalDateTimeImpl<D> plusSeconds(long seconds) { 329 return plusWithOverflow(date, 0, 0, seconds, 0); 330 } 331 plusNanos(long nanos)332 private ChronoLocalDateTimeImpl<D> plusNanos(long nanos) { 333 return plusWithOverflow(date, 0, 0, 0, nanos); 334 } 335 336 //----------------------------------------------------------------------- plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos)337 private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) { 338 // 9223372036854775808 long, 2147483648 int 339 if ((hours | minutes | seconds | nanos) == 0) { 340 return with(newDate, time); 341 } 342 long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B 343 seconds / SECONDS_PER_DAY + // max/24*60*60 344 minutes / MINUTES_PER_DAY + // max/24*60 345 hours / HOURS_PER_DAY; // max/24 346 long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000 347 (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000 348 (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000 349 (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000 350 long curNoD = time.toNanoOfDay(); // max 86400000000000 351 totNanos = totNanos + curNoD; // total 432000000000000 352 totDays += Math.floorDiv(totNanos, NANOS_PER_DAY); 353 long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY); 354 LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD)); 355 return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime); 356 } 357 358 //----------------------------------------------------------------------- 359 @Override atZone(ZoneId zone)360 public ChronoZonedDateTime<D> atZone(ZoneId zone) { 361 return ChronoZonedDateTimeImpl.ofBest(this, zone, null); 362 } 363 364 //----------------------------------------------------------------------- 365 @Override until(Temporal endExclusive, TemporalUnit unit)366 public long until(Temporal endExclusive, TemporalUnit unit) { 367 Objects.requireNonNull(endExclusive, "endExclusive"); 368 @SuppressWarnings("unchecked") 369 ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) getChronology().localDateTime(endExclusive); 370 if (unit instanceof ChronoUnit chronoUnit) { 371 if (unit.isTimeBased()) { 372 long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); 373 switch (chronoUnit) { 374 case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break; 375 case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break; 376 case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break; 377 case SECONDS: amount = Math.multiplyExact(amount, SECONDS_PER_DAY); break; 378 case MINUTES: amount = Math.multiplyExact(amount, MINUTES_PER_DAY); break; 379 case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break; 380 case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break; 381 } 382 return Math.addExact(amount, time.until(end.toLocalTime(), unit)); 383 } 384 ChronoLocalDate endDate = end.toLocalDate(); 385 if (end.toLocalTime().isBefore(time)) { 386 endDate = endDate.minus(1, ChronoUnit.DAYS); 387 } 388 return date.until(endDate, unit); 389 } 390 Objects.requireNonNull(unit, "unit"); 391 return unit.between(this, end); 392 } 393 394 //----------------------------------------------------------------------- 395 /** 396 * Writes the ChronoLocalDateTime using a 397 * <a href="{@docRoot}/serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. 398 * @serialData 399 * <pre> 400 * out.writeByte(2); // identifies a ChronoLocalDateTime 401 * out.writeObject(toLocalDate()); 402 * out.writeObject(toLocalTime()); 403 * </pre> 404 * 405 * @return the instance of {@code Ser}, not null 406 */ 407 @java.io.Serial writeReplace()408 private Object writeReplace() { 409 return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this); 410 } 411 412 /** 413 * Defend against malicious streams. 414 * 415 * @param s the stream to read 416 * @throws InvalidObjectException always 417 */ 418 @java.io.Serial readObject(ObjectInputStream s)419 private void readObject(ObjectInputStream s) throws InvalidObjectException { 420 throw new InvalidObjectException("Deserialization via serialization delegate"); 421 } 422 writeExternal(ObjectOutput out)423 void writeExternal(ObjectOutput out) throws IOException { 424 out.writeObject(date); 425 out.writeObject(time); 426 } 427 readExternal(ObjectInput in)428 static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 429 ChronoLocalDate date = (ChronoLocalDate) in.readObject(); 430 LocalTime time = (LocalTime) in.readObject(); 431 return date.atTime(time); 432 } 433 434 //----------------------------------------------------------------------- 435 @Override equals(Object obj)436 public boolean equals(Object obj) { 437 if (this == obj) { 438 return true; 439 } 440 if (obj instanceof ChronoLocalDateTime) { 441 return compareTo((ChronoLocalDateTime<?>) obj) == 0; 442 } 443 return false; 444 } 445 446 @Override hashCode()447 public int hashCode() { 448 return toLocalDate().hashCode() ^ toLocalTime().hashCode(); 449 } 450 451 @Override toString()452 public String toString() { 453 return toLocalDate().toString() + 'T' + toLocalTime().toString(); 454 } 455 456 } 457