1 /*
2 * Copyright 2001-2011 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time.base;
17
18 import org.joda.time.DateTime;
19 import org.joda.time.DateTimeUtils;
20 import org.joda.time.Duration;
21 import org.joda.time.Interval;
22 import org.joda.time.MutableInterval;
23 import org.joda.time.Period;
24 import org.joda.time.PeriodType;
25 import org.joda.time.ReadableInstant;
26 import org.joda.time.ReadableInterval;
27 import org.joda.time.field.FieldUtils;
28 import org.joda.time.format.DateTimeFormatter;
29 import org.joda.time.format.ISODateTimeFormat;
30
31 /**
32 * AbstractInterval provides the common behaviour for time intervals.
33 * <p>
34 * This class should generally not be used directly by API users. The
35 * {@link ReadableInterval} interface should be used when different
36 * kinds of intervals are to be referenced.
37 * <p>
38 * AbstractInterval subclasses may be mutable and not thread-safe.
39 *
40 * @author Brian S O'Neill
41 * @author Stephen Colebourne
42 * @since 1.0
43 */
44 public abstract class AbstractInterval implements ReadableInterval {
45
46 /**
47 * Constructor.
48 */
49 protected AbstractInterval() {
50 super();
51 }
52
53 //-----------------------------------------------------------------------
54 /**
55 * Validates an interval.
56 *
57 * @param start the start instant in milliseconds
58 * @param end the end instant in milliseconds
59 * @throws IllegalArgumentException if the interval is invalid
60 */
61 protected void checkInterval(long start, long end) {
62 if (end < start) {
63 throw new IllegalArgumentException("The end instant must be greater or equal to the start");
64 }
65 }
66
67 //-----------------------------------------------------------------------
68 /**
69 * Gets the start of this time interval, which is inclusive, as a DateTime.
70 *
71 * @return the start of the time interval
72 */
73 public DateTime getStart() {
74 return new DateTime(getStartMillis(), getChronology());
75 }
76
77 /**
78 * Gets the end of this time interval, which is exclusive, as a DateTime.
79 *
80 * @return the end of the time interval
81 */
82 public DateTime getEnd() {
83 return new DateTime(getEndMillis(), getChronology());
84 }
85
86 //-----------------------------------------------------------------------
87 /**
88 * Does this time interval contain the specified millisecond instant.
89 * <p>
90 * Non-zero duration intervals are inclusive of the start instant and
91 * exclusive of the end. A zero duration interval cannot contain anything.
92 *
93 * @param millisInstant the instant to compare to,
94 * millisecond instant from 1970-01-01T00:00:00Z
95 * @return true if this time interval contains the millisecond
96 */
97 public boolean contains(long millisInstant) {
98 long thisStart = getStartMillis();
99 long thisEnd = getEndMillis();
100 return (millisInstant >= thisStart && millisInstant < thisEnd);
101 }
102
103 /**
104 * Does this time interval contain the current instant.
105 * <p>
106 * Non-zero duration intervals are inclusive of the start instant and
107 * exclusive of the end. A zero duration interval cannot contain anything.
108 *
109 * @return true if this time interval contains the current instant
110 */
111 public boolean containsNow() {
112 return contains(DateTimeUtils.currentTimeMillis());
113 }
114
115 /**
116 * Does this time interval contain the specified instant.
117 * <p>
118 * Non-zero duration intervals are inclusive of the start instant and
119 * exclusive of the end. A zero duration interval cannot contain anything.
120 * <p>
121 * For example:
122 * <pre>
123 * [09:00 to 10:00) contains 08:59 = false (before start)
124 * [09:00 to 10:00) contains 09:00 = true
125 * [09:00 to 10:00) contains 09:59 = true
126 * [09:00 to 10:00) contains 10:00 = false (equals end)
127 * [09:00 to 10:00) contains 10:01 = false (after end)
128 *
129 * [14:00 to 14:00) contains 14:00 = false (zero duration contains nothing)
130 * </pre>
131 * Passing in a <code>null</code> parameter will have the same effect as
132 * calling {@link #containsNow()}.
133 *
134 * @param instant the instant, null means now
135 * @return true if this time interval contains the instant
136 */
137 public boolean contains(ReadableInstant instant) {
138 if (instant == null) {
139 return containsNow();
140 }
141 return contains(instant.getMillis());
142 }
143
144 /**
145 * Does this time interval contain the specified time interval.
146 * <p>
147 * Non-zero duration intervals are inclusive of the start instant and
148 * exclusive of the end. The other interval is contained if this interval
149 * wholly contains, starts, finishes or equals it.
150 * A zero duration interval cannot contain anything.
151 * <p>
152 * When two intervals are compared the result is one of three states:
153 * (a) they abut, (b) there is a gap between them, (c) they overlap.
154 * The <code>contains</code> method is not related to these states.
155 * In particular, a zero duration interval is contained at the start of
156 * a larger interval, but does not overlap (it abuts instead).
157 * <p>
158 * For example:
159 * <pre>
160 * [09:00 to 10:00) contains [09:00 to 10:00) = true
161 * [09:00 to 10:00) contains [09:00 to 09:30) = true
162 * [09:00 to 10:00) contains [09:30 to 10:00) = true
163 * [09:00 to 10:00) contains [09:15 to 09:45) = true
164 * [09:00 to 10:00) contains [09:00 to 09:00) = true
165 *
166 * [09:00 to 10:00) contains [08:59 to 10:00) = false (otherStart before thisStart)
167 * [09:00 to 10:00) contains [09:00 to 10:01) = false (otherEnd after thisEnd)
168 * [09:00 to 10:00) contains [10:00 to 10:00) = false (otherStart equals thisEnd)
169 *
170 * [14:00 to 14:00) contains [14:00 to 14:00) = false (zero duration contains nothing)
171 * </pre>
172 * Passing in a <code>null</code> parameter will have the same effect as
173 * calling {@link #containsNow()}.
174 *
175 * @param interval the time interval to compare to, null means a zero duration interval now
176 * @return true if this time interval contains the time interval
177 */
178 public boolean contains(ReadableInterval interval) {
179 if (interval == null) {
180 return containsNow();
181 }
182 long otherStart = interval.getStartMillis();
183 long otherEnd = interval.getEndMillis();
184 long thisStart = getStartMillis();
185 long thisEnd = getEndMillis();
186 return (thisStart <= otherStart && otherStart < thisEnd && otherEnd <= thisEnd);
187 }
188
189 /**
190 * Does this time interval overlap the specified time interval.
191 * <p>
192 * Intervals are inclusive of the start instant and exclusive of the end.
193 * An interval overlaps another if it shares some common part of the
194 * datetime continuum.
195 * <p>
196 * When two intervals are compared the result is one of three states:
197 * (a) they abut, (b) there is a gap between them, (c) they overlap.
198 * The abuts state takes precedence over the other two, thus a zero duration
199 * interval at the start of a larger interval abuts and does not overlap.
200 * <p>
201 * For example:
202 * <pre>
203 * [09:00 to 10:00) overlaps [08:00 to 08:30) = false (completely before)
204 * [09:00 to 10:00) overlaps [08:00 to 09:00) = false (abuts before)
205 * [09:00 to 10:00) overlaps [08:00 to 09:30) = true
206 * [09:00 to 10:00) overlaps [08:00 to 10:00) = true
207 * [09:00 to 10:00) overlaps [08:00 to 11:00) = true
208 *
209 * [09:00 to 10:00) overlaps [09:00 to 09:00) = false (abuts before)
210 * [09:00 to 10:00) overlaps [09:00 to 09:30) = true
211 * [09:00 to 10:00) overlaps [09:00 to 10:00) = true
212 * [09:00 to 10:00) overlaps [09:00 to 11:00) = true
213 *
214 * [09:00 to 10:00) overlaps [09:30 to 09:30) = true
215 * [09:00 to 10:00) overlaps [09:30 to 10:00) = true
216 * [09:00 to 10:00) overlaps [09:30 to 11:00) = true
217 *
218 * [09:00 to 10:00) overlaps [10:00 to 10:00) = false (abuts after)
219 * [09:00 to 10:00) overlaps [10:00 to 11:00) = false (abuts after)
220 *
221 * [09:00 to 10:00) overlaps [10:30 to 11:00) = false (completely after)
222 *
223 * [14:00 to 14:00) overlaps [14:00 to 14:00) = false (abuts before and after)
224 * [14:00 to 14:00) overlaps [13:00 to 15:00) = true
225 * </pre>
226 *
227 * @param interval the time interval to compare to, null means a zero length interval now
228 * @return true if the time intervals overlap
229 */
230 public boolean overlaps(ReadableInterval interval) {
231 long thisStart = getStartMillis();
232 long thisEnd = getEndMillis();
233 if (interval == null) {
234 long now = DateTimeUtils.currentTimeMillis();
235 return (thisStart < now && now < thisEnd);
236 } else {
237 long otherStart = interval.getStartMillis();
238 long otherEnd = interval.getEndMillis();
239 return (thisStart < otherEnd && otherStart < thisEnd);
240 }
241 }
242
243 //-----------------------------------------------------------------------
244 /**
245 * Is this time interval before the specified millisecond instant.
246 * <p>
247 * Intervals are inclusive of the start instant and exclusive of the end.
248 *
249 * @param millisInstant the instant to compare to,
250 * millisecond instant from 1970-01-01T00:00:00Z
251 * @return true if this time interval is before the instant
252 */
253 public boolean isBefore(long millisInstant) {
254 return (getEndMillis() <= millisInstant);
255 }
256
257 /**
258 * Is this time interval before the current instant.
259 * <p>
260 * Intervals are inclusive of the start instant and exclusive of the end.
261 *
262 * @return true if this time interval is before the current instant
263 */
264 public boolean isBeforeNow() {
265 return isBefore(DateTimeUtils.currentTimeMillis());
266 }
267
268 /**
269 * Is this time interval before the specified instant.
270 * <p>
271 * Intervals are inclusive of the start instant and exclusive of the end.
272 *
273 * @param instant the instant to compare to, null means now
274 * @return true if this time interval is before the instant
275 */
276 public boolean isBefore(ReadableInstant instant) {
277 if (instant == null) {
278 return isBeforeNow();
279 }
280 return isBefore(instant.getMillis());
281 }
282
283 /**
284 * Is this time interval entirely before the specified instant.
285 * <p>
286 * Intervals are inclusive of the start instant and exclusive of the end.
287 *
288 * @param interval the interval to compare to, null means now
289 * @return true if this time interval is before the interval specified
290 */
291 public boolean isBefore(ReadableInterval interval) {
292 if (interval == null) {
293 return isBeforeNow();
294 }
295 return isBefore(interval.getStartMillis());
296 }
297
298 //-----------------------------------------------------------------------
299 /**
300 * Is this time interval after the specified millisecond instant.
301 * <p>
302 * Intervals are inclusive of the start instant and exclusive of the end.
303 *
304 * @param millisInstant the instant to compare to,
305 * millisecond instant from 1970-01-01T00:00:00Z
306 * @return true if this time interval is after the instant
307 */
308 public boolean isAfter(long millisInstant) {
309 return (getStartMillis() > millisInstant);
310 }
311
312 /**
313 * Is this time interval after the current instant.
314 * <p>
315 * Intervals are inclusive of the start instant and exclusive of the end.
316 *
317 * @return true if this time interval is after the current instant
318 */
319 public boolean isAfterNow() {
320 return isAfter(DateTimeUtils.currentTimeMillis());
321 }
322
323 /**
324 * Is this time interval after the specified instant.
325 * <p>
326 * Intervals are inclusive of the start instant and exclusive of the end.
327 *
328 * @param instant the instant to compare to, null means now
329 * @return true if this time interval is after the instant
330 */
331 public boolean isAfter(ReadableInstant instant) {
332 if (instant == null) {
333 return isAfterNow();
334 }
335 return isAfter(instant.getMillis());
336 }
337
338 /**
339 * Is this time interval entirely after the specified interval.
340 * <p>
341 * Intervals are inclusive of the start instant and exclusive of the end.
342 * Only the end time of the specified interval is used in the comparison.
343 *
344 * @param interval the interval to compare to, null means now
345 * @return true if this time interval is after the interval specified
346 */
347 public boolean isAfter(ReadableInterval interval) {
348 long endMillis;
349 if (interval == null) {
350 endMillis = DateTimeUtils.currentTimeMillis();
351 } else {
352 endMillis = interval.getEndMillis();
353 }
354 return (getStartMillis() >= endMillis);
355 }
356
357 //-----------------------------------------------------------------------
358 /**
359 * Get this interval as an immutable <code>Interval</code> object.
360 *
361 * @return the interval as an Interval object
362 */
363 public Interval toInterval() {
364 return new Interval(getStartMillis(), getEndMillis(), getChronology());
365 }
366
367 /**
368 * Get this time interval as a <code>MutableInterval</code>.
369 * <p>
370 * This will always return a new <code>MutableInterval</code> with the same interval.
371 *
372 * @return the time interval as a MutableInterval object
373 */
374 public MutableInterval toMutableInterval() {
375 return new MutableInterval(getStartMillis(), getEndMillis(), getChronology());
376 }
377
378 //-----------------------------------------------------------------------
379 /**
380 * Gets the duration of this time interval in milliseconds.
381 * <p>
382 * The duration is equal to the end millis minus the start millis.
383 *
384 * @return the duration of the time interval in milliseconds
385 * @throws ArithmeticException if the duration exceeds the capacity of a long
386 */
387 public long toDurationMillis() {
388 return FieldUtils.safeAdd(getEndMillis(), -getStartMillis());
389 }
390
391 /**
392 * Gets the duration of this time interval.
393 * <p>
394 * The duration is equal to the end millis minus the start millis.
395 *
396 * @return the duration of the time interval
397 * @throws ArithmeticException if the duration exceeds the capacity of a long
398 */
399 public Duration toDuration() {
400 long durMillis = toDurationMillis();
401 if (durMillis == 0) {
402 return Duration.ZERO;
403 } else {
404 return new Duration(durMillis);
405 }
406 }
407
408 //-----------------------------------------------------------------------
409 /**
410 * Converts the duration of the interval to a <code>Period</code> using the
411 * All period type.
412 * <p>
413 * This method should be used to exract the field values describing the
414 * difference between the start and end instants.
415 *
416 * @return a time period derived from the interval
417 */
418 public Period toPeriod() {
419 return new Period(getStartMillis(), getEndMillis(), getChronology());
420 }
421
422 /**
423 * Converts the duration of the interval to a <code>Period</code> using the
424 * specified period type.
425 * <p>
426 * This method should be used to exract the field values describing the
427 * difference between the start and end instants.
428 *
429 * @param type the requested type of the duration, null means AllType
430 * @return a time period derived from the interval
431 */
432 public Period toPeriod(PeriodType type) {
433 return new Period(getStartMillis(), getEndMillis(), type, getChronology());
434 }
435
436 //-----------------------------------------------------------------------
437 /**
438 * Compares this object with the specified object for equality based
439 * on start and end millis plus the chronology.
440 * All ReadableInterval instances are accepted.
441 * <p>
442 * To compare the duration of two time intervals, use {@link #toDuration()}
443 * to get the durations and compare those.
444 *
445 * @param readableInterval a readable interval to check against
446 * @return true if the start and end millis are equal
447 */
448 public boolean equals(Object readableInterval) {
449 if (this == readableInterval) {
450 return true;
451 }
452 if (readableInterval instanceof ReadableInterval == false) {
453 return false;
454 }
455 ReadableInterval other = (ReadableInterval) readableInterval;
456 return
457 getStartMillis() == other.getStartMillis() &&
458 getEndMillis() == other.getEndMillis() &&
459 FieldUtils.equals(getChronology(), other.getChronology());
460 }
461
462 /**
463 * Hashcode compatible with equals method.
464 *
465 * @return suitable hashcode
466 */
467 public int hashCode() {
468 long start = getStartMillis();
469 long end = getEndMillis();
470 int result = 97;
471 result = 31 * result + ((int) (start ^ (start >>> 32)));
472 result = 31 * result + ((int) (end ^ (end >>> 32)));
473 result = 31 * result + getChronology().hashCode();
474 return result;
475 }
476
477 /**
478 * Output a string in ISO8601 interval format.
479 * <p>
480 * From version 2.1, the string includes the time zone offset.
481 *
482 * @return re-parsable string (in the default zone)
483 */
484 public String toString() {
485 DateTimeFormatter printer = ISODateTimeFormat.dateTime();
486 printer = printer.withChronology(getChronology());
487 StringBuffer buf = new StringBuffer(48);
488 printer.printTo(buf, getStartMillis());
489 buf.append('/');
490 printer.printTo(buf, getEndMillis());
491 return buf.toString();
492 }
493
494 }