• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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