1 | /* |
2 | * Copyright 2001-2006 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 | * |
480 | * @return re-parsable string |
481 | */ |
482 | public String toString() { |
483 | DateTimeFormatter printer = ISODateTimeFormat.dateHourMinuteSecondFraction(); |
484 | printer = printer.withChronology(getChronology()); |
485 | StringBuffer buf = new StringBuffer(48); |
486 | printer.printTo(buf, getStartMillis()); |
487 | buf.append('/'); |
488 | printer.printTo(buf, getEndMillis()); |
489 | return buf.toString(); |
490 | } |
491 | |
492 | } |