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 36 import java.io.IOException; 37 import java.io.ObjectInput; 38 import java.io.ObjectOutput; 39 import java.io.Serializable; 40 41 import org.threeten.bp.LocalTime; 42 import org.threeten.bp.ZoneId; 43 import org.threeten.bp.jdk8.Jdk8Methods; 44 import org.threeten.bp.temporal.ChronoField; 45 import org.threeten.bp.temporal.ChronoUnit; 46 import org.threeten.bp.temporal.Temporal; 47 import org.threeten.bp.temporal.TemporalAdjuster; 48 import org.threeten.bp.temporal.TemporalField; 49 import org.threeten.bp.temporal.TemporalUnit; 50 import org.threeten.bp.temporal.ValueRange; 51 52 /** 53 * A date-time without a time-zone for the calendar neutral API. 54 * <p> 55 * {@code ChronoLocalDateTime} is an immutable date-time object that represents a date-time, often 56 * viewed as year-month-day-hour-minute-second. This object can also access other 57 * fields such as day-of-year, day-of-week and week-of-year. 58 * <p> 59 * This class stores all date and time fields, to a precision of nanoseconds. 60 * It does not store or represent a time-zone. For example, the value 61 * "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}. 62 * 63 * <h3>Specification for implementors</h3> 64 * This class is immutable and thread-safe. 65 * 66 * @param <D> the date type 67 */ 68 final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate> 69 extends ChronoLocalDateTime<D> 70 implements Temporal, TemporalAdjuster, Serializable { 71 72 /** 73 * Serialization version. 74 */ 75 private static final long serialVersionUID = 4556003607393004514L; 76 /** 77 * Hours per minute. 78 */ 79 private static final int HOURS_PER_DAY = 24; 80 /** 81 * Minutes per hour. 82 */ 83 private static final int MINUTES_PER_HOUR = 60; 84 /** 85 * Minutes per day. 86 */ 87 private static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY; 88 /** 89 * Seconds per minute. 90 */ 91 private static final int SECONDS_PER_MINUTE = 60; 92 /** 93 * Seconds per hour. 94 */ 95 private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; 96 /** 97 * Seconds per day. 98 */ 99 private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; 100 /** 101 * Milliseconds per day. 102 */ 103 private static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L; 104 /** 105 * Microseconds per day. 106 */ 107 private static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000000L; 108 /** 109 * Nanos per second. 110 */ 111 private static final long NANOS_PER_SECOND = 1000000000L; 112 /** 113 * Nanos per minute. 114 */ 115 private static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE; 116 /** 117 * Nanos per hour. 118 */ 119 private static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR; 120 /** 121 * Nanos per day. 122 */ 123 private static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY; 124 125 /** 126 * The date part. 127 */ 128 private final D date; 129 /** 130 * The time part. 131 */ 132 private final LocalTime time; 133 134 //----------------------------------------------------------------------- 135 /** 136 * Obtains an instance of {@code ChronoLocalDateTime} from a date and time. 137 * 138 * @param date the local date, not null 139 * @param time the local time, not null 140 * @return the local date-time, not null 141 */ of(R date, LocalTime time)142 static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> of(R date, LocalTime time) { 143 return new ChronoLocalDateTimeImpl<R>(date, time); 144 } 145 146 /** 147 * Constructor. 148 * 149 * @param date the date part of the date-time, not null 150 * @param time the time part of the date-time, not null 151 */ ChronoLocalDateTimeImpl(D date, LocalTime time)152 private ChronoLocalDateTimeImpl(D date, LocalTime time) { 153 Jdk8Methods.requireNonNull(date, "date"); 154 Jdk8Methods.requireNonNull(time, "time"); 155 this.date = date; 156 this.time = time; 157 } 158 159 /** 160 * Returns a copy of this date-time with the new date and time, checking 161 * to see if a new object is in fact required. 162 * 163 * @param newDate the date of the new date-time, not null 164 * @param newTime the time of the new date-time, not null 165 * @return the date-time, not null 166 */ with(Temporal newDate, LocalTime newTime)167 private ChronoLocalDateTimeImpl<D> with(Temporal newDate, LocalTime newTime) { 168 if (date == newDate && time == newTime) { 169 return this; 170 } 171 // Validate that the new DateTime is a ChronoLocalDate (and not something else) 172 D cd = date.getChronology().ensureChronoLocalDate(newDate); 173 return new ChronoLocalDateTimeImpl<D>(cd, newTime); 174 } 175 176 //----------------------------------------------------------------------- 177 @Override toLocalDate()178 public D toLocalDate() { 179 return date; 180 } 181 182 @Override toLocalTime()183 public LocalTime toLocalTime() { 184 return time; 185 } 186 187 //----------------------------------------------------------------------- 188 @Override isSupported(TemporalField field)189 public boolean isSupported(TemporalField field) { 190 if (field instanceof ChronoField) { 191 return field.isDateBased() || field.isTimeBased(); 192 } 193 return field != null && field.isSupportedBy(this); 194 } 195 196 @Override isSupported(TemporalUnit unit)197 public boolean isSupported(TemporalUnit unit) { 198 if (unit instanceof ChronoUnit) { 199 return unit.isDateBased() || unit.isTimeBased(); 200 } 201 return unit != null && unit.isSupportedBy(this); 202 } 203 204 @Override range(TemporalField field)205 public ValueRange range(TemporalField field) { 206 if (field instanceof ChronoField) { 207 return (field.isTimeBased() ? time.range(field) : date.range(field)); 208 } 209 return field.rangeRefinedBy(this); 210 } 211 212 @Override get(TemporalField field)213 public int get(TemporalField field) { 214 if (field instanceof ChronoField) { 215 return (field.isTimeBased() ? time.get(field) : date.get(field)); 216 } 217 return range(field).checkValidIntValue(getLong(field), field); 218 } 219 220 @Override getLong(TemporalField field)221 public long getLong(TemporalField field) { 222 if (field instanceof ChronoField) { 223 return (field.isTimeBased() ? time.getLong(field) : date.getLong(field)); 224 } 225 return field.getFrom(this); 226 } 227 228 //----------------------------------------------------------------------- 229 @Override with(TemporalAdjuster adjuster)230 public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) { 231 if (adjuster instanceof ChronoLocalDate) { 232 // The Chrono is checked in with(date,time) 233 return with((ChronoLocalDate) adjuster, time); 234 } else if (adjuster instanceof LocalTime) { 235 return with(date, (LocalTime) adjuster); 236 } else if (adjuster instanceof ChronoLocalDateTimeImpl) { 237 return date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl<?>) adjuster); 238 } 239 return date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this)); 240 } 241 242 @Override with(TemporalField field, long newValue)243 public ChronoLocalDateTimeImpl<D> with(TemporalField field, long newValue) { 244 if (field instanceof ChronoField) { 245 if (field.isTimeBased()) { 246 return with(date, time.with(field, newValue)); 247 } else { 248 return with(date.with(field, newValue), time); 249 } 250 } 251 return date.getChronology().ensureChronoLocalDateTime(field.adjustInto(this, newValue)); 252 } 253 254 //----------------------------------------------------------------------- 255 @Override plus(long amountToAdd, TemporalUnit unit)256 public ChronoLocalDateTimeImpl<D> plus(long amountToAdd, TemporalUnit unit) { 257 if (unit instanceof ChronoUnit) { 258 ChronoUnit f = (ChronoUnit) unit; 259 switch (f) { 260 case NANOS: return plusNanos(amountToAdd); 261 case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000); 262 case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000); 263 case SECONDS: return plusSeconds(amountToAdd); 264 case MINUTES: return plusMinutes(amountToAdd); 265 case HOURS: return plusHours(amountToAdd); 266 case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2) 267 } 268 return with(date.plus(amountToAdd, unit), time); 269 } 270 return date.getChronology().ensureChronoLocalDateTime(unit.addTo(this, amountToAdd)); 271 } 272 plusDays(long days)273 private ChronoLocalDateTimeImpl<D> plusDays(long days) { 274 return with(date.plus(days, ChronoUnit.DAYS), time); 275 } 276 plusHours(long hours)277 private ChronoLocalDateTimeImpl<D> plusHours(long hours) { 278 return plusWithOverflow(date, hours, 0, 0, 0); 279 } 280 plusMinutes(long minutes)281 private ChronoLocalDateTimeImpl<D> plusMinutes(long minutes) { 282 return plusWithOverflow(date, 0, minutes, 0, 0); 283 } 284 plusSeconds(long seconds)285 ChronoLocalDateTimeImpl<D> plusSeconds(long seconds) { 286 return plusWithOverflow(date, 0, 0, seconds, 0); 287 } 288 plusNanos(long nanos)289 private ChronoLocalDateTimeImpl<D> plusNanos(long nanos) { 290 return plusWithOverflow(date, 0, 0, 0, nanos); 291 } 292 293 //----------------------------------------------------------------------- plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos)294 private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) { 295 // 9223372036854775808 long, 2147483648 int 296 if ((hours | minutes | seconds | nanos) == 0) { 297 return with(newDate, time); 298 } 299 long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B 300 seconds / SECONDS_PER_DAY + // max/24*60*60 301 minutes / MINUTES_PER_DAY + // max/24*60 302 hours / HOURS_PER_DAY; // max/24 303 long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000 304 (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000 305 (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000 306 (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000 307 long curNoD = time.toNanoOfDay(); // max 86400000000000 308 totNanos = totNanos + curNoD; // total 432000000000000 309 totDays += Jdk8Methods.floorDiv(totNanos, NANOS_PER_DAY); 310 long newNoD = Jdk8Methods.floorMod(totNanos, NANOS_PER_DAY); 311 LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD)); 312 return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime); 313 } 314 315 //----------------------------------------------------------------------- 316 @Override atZone(ZoneId zoneId)317 public ChronoZonedDateTime<D> atZone(ZoneId zoneId) { 318 return ChronoZonedDateTimeImpl.ofBest(this, zoneId, null); 319 } 320 321 //----------------------------------------------------------------------- 322 @Override until(Temporal endExclusive, TemporalUnit unit)323 public long until(Temporal endExclusive, TemporalUnit unit) { 324 @SuppressWarnings("unchecked") 325 ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) toLocalDate().getChronology().localDateTime(endExclusive); 326 if (unit instanceof ChronoUnit) { 327 ChronoUnit f = (ChronoUnit) unit; 328 if (f.isTimeBased()) { 329 long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); 330 switch (f) { 331 case NANOS: amount = Jdk8Methods.safeMultiply(amount, NANOS_PER_DAY); break; 332 case MICROS: amount = Jdk8Methods.safeMultiply(amount, MICROS_PER_DAY); break; 333 case MILLIS: amount = Jdk8Methods.safeMultiply(amount, MILLIS_PER_DAY); break; 334 case SECONDS: amount = Jdk8Methods.safeMultiply(amount, SECONDS_PER_DAY); break; 335 case MINUTES: amount = Jdk8Methods.safeMultiply(amount, MINUTES_PER_DAY); break; 336 case HOURS: amount = Jdk8Methods.safeMultiply(amount, HOURS_PER_DAY); break; 337 case HALF_DAYS: amount = Jdk8Methods.safeMultiply(amount, 2); break; 338 } 339 return Jdk8Methods.safeAdd(amount, time.until(end.toLocalTime(), unit)); 340 } 341 ChronoLocalDate endDate = end.toLocalDate(); 342 if (end.toLocalTime().isBefore(time)) { 343 endDate = endDate.minus(1, ChronoUnit.DAYS); 344 } 345 return date.until(endDate, unit); 346 } 347 return unit.between(this, end); 348 } 349 350 //----------------------------------------------------------------------- writeReplace()351 private Object writeReplace() { 352 return new Ser(Ser.CHRONO_LOCALDATETIME_TYPE, this); 353 } 354 writeExternal(ObjectOutput out)355 void writeExternal(ObjectOutput out) throws IOException { 356 out.writeObject(date); 357 out.writeObject(time); 358 } 359 readExternal(ObjectInput in)360 static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 361 ChronoLocalDate date = (ChronoLocalDate) in.readObject(); 362 LocalTime time = (LocalTime) in.readObject(); 363 return date.atTime(time); 364 } 365 366 } 367