001: /*
002: * Copyright 2001-2006 Stephen Colebourne
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.joda.time.base;
017:
018: import org.joda.time.DateTime;
019: import org.joda.time.DateTimeUtils;
020: import org.joda.time.Duration;
021: import org.joda.time.Interval;
022: import org.joda.time.MutableInterval;
023: import org.joda.time.Period;
024: import org.joda.time.PeriodType;
025: import org.joda.time.ReadableInstant;
026: import org.joda.time.ReadableInterval;
027: import org.joda.time.field.FieldUtils;
028: import org.joda.time.format.DateTimeFormatter;
029: import org.joda.time.format.ISODateTimeFormat;
030:
031: /**
032: * AbstractInterval provides the common behaviour for time intervals.
033: * <p>
034: * This class should generally not be used directly by API users. The
035: * {@link ReadableInterval} interface should be used when different
036: * kinds of intervals are to be referenced.
037: * <p>
038: * AbstractInterval subclasses may be mutable and not thread-safe.
039: *
040: * @author Brian S O'Neill
041: * @author Stephen Colebourne
042: * @since 1.0
043: */
044: public abstract class AbstractInterval implements ReadableInterval {
045:
046: /**
047: * Constructor.
048: */
049: protected AbstractInterval() {
050: super ();
051: }
052:
053: //-----------------------------------------------------------------------
054: /**
055: * Validates an interval.
056: *
057: * @param start the start instant in milliseconds
058: * @param end the end instant in milliseconds
059: * @throws IllegalArgumentException if the interval is invalid
060: */
061: protected void checkInterval(long start, long end) {
062: if (end < start) {
063: throw new IllegalArgumentException(
064: "The end instant must be greater or equal to the start");
065: }
066: }
067:
068: //-----------------------------------------------------------------------
069: /**
070: * Gets the start of this time interval, which is inclusive, as a DateTime.
071: *
072: * @return the start of the time interval
073: */
074: public DateTime getStart() {
075: return new DateTime(getStartMillis(), getChronology());
076: }
077:
078: /**
079: * Gets the end of this time interval, which is exclusive, as a DateTime.
080: *
081: * @return the end of the time interval
082: */
083: public DateTime getEnd() {
084: return new DateTime(getEndMillis(), getChronology());
085: }
086:
087: //-----------------------------------------------------------------------
088: /**
089: * Does this time interval contain the specified millisecond instant.
090: * <p>
091: * Non-zero duration intervals are inclusive of the start instant and
092: * exclusive of the end. A zero duration interval cannot contain anything.
093: *
094: * @param millisInstant the instant to compare to,
095: * millisecond instant from 1970-01-01T00:00:00Z
096: * @return true if this time interval contains the millisecond
097: */
098: public boolean contains(long millisInstant) {
099: long this Start = getStartMillis();
100: long this End = getEndMillis();
101: return (millisInstant >= this Start && millisInstant < this End);
102: }
103:
104: /**
105: * Does this time interval contain the current instant.
106: * <p>
107: * Non-zero duration intervals are inclusive of the start instant and
108: * exclusive of the end. A zero duration interval cannot contain anything.
109: *
110: * @return true if this time interval contains the current instant
111: */
112: public boolean containsNow() {
113: return contains(DateTimeUtils.currentTimeMillis());
114: }
115:
116: /**
117: * Does this time interval contain the specified instant.
118: * <p>
119: * Non-zero duration intervals are inclusive of the start instant and
120: * exclusive of the end. A zero duration interval cannot contain anything.
121: * <p>
122: * For example:
123: * <pre>
124: * [09:00 to 10:00) contains 08:59 = false (before start)
125: * [09:00 to 10:00) contains 09:00 = true
126: * [09:00 to 10:00) contains 09:59 = true
127: * [09:00 to 10:00) contains 10:00 = false (equals end)
128: * [09:00 to 10:00) contains 10:01 = false (after end)
129: *
130: * [14:00 to 14:00) contains 14:00 = false (zero duration contains nothing)
131: * </pre>
132: * Passing in a <code>null</code> parameter will have the same effect as
133: * calling {@link #containsNow()}.
134: *
135: * @param instant the instant, null means now
136: * @return true if this time interval contains the instant
137: */
138: public boolean contains(ReadableInstant instant) {
139: if (instant == null) {
140: return containsNow();
141: }
142: return contains(instant.getMillis());
143: }
144:
145: /**
146: * Does this time interval contain the specified time interval.
147: * <p>
148: * Non-zero duration intervals are inclusive of the start instant and
149: * exclusive of the end. The other interval is contained if this interval
150: * wholly contains, starts, finishes or equals it.
151: * A zero duration interval cannot contain anything.
152: * <p>
153: * When two intervals are compared the result is one of three states:
154: * (a) they abut, (b) there is a gap between them, (c) they overlap.
155: * The <code>contains</code> method is not related to these states.
156: * In particular, a zero duration interval is contained at the start of
157: * a larger interval, but does not overlap (it abuts instead).
158: * <p>
159: * For example:
160: * <pre>
161: * [09:00 to 10:00) contains [09:00 to 10:00) = true
162: * [09:00 to 10:00) contains [09:00 to 09:30) = true
163: * [09:00 to 10:00) contains [09:30 to 10:00) = true
164: * [09:00 to 10:00) contains [09:15 to 09:45) = true
165: * [09:00 to 10:00) contains [09:00 to 09:00) = true
166: *
167: * [09:00 to 10:00) contains [08:59 to 10:00) = false (otherStart before thisStart)
168: * [09:00 to 10:00) contains [09:00 to 10:01) = false (otherEnd after thisEnd)
169: * [09:00 to 10:00) contains [10:00 to 10:00) = false (otherStart equals thisEnd)
170: *
171: * [14:00 to 14:00) contains [14:00 to 14:00) = false (zero duration contains nothing)
172: * </pre>
173: * Passing in a <code>null</code> parameter will have the same effect as
174: * calling {@link #containsNow()}.
175: *
176: * @param interval the time interval to compare to, null means a zero duration interval now
177: * @return true if this time interval contains the time interval
178: */
179: public boolean contains(ReadableInterval interval) {
180: if (interval == null) {
181: return containsNow();
182: }
183: long otherStart = interval.getStartMillis();
184: long otherEnd = interval.getEndMillis();
185: long this Start = getStartMillis();
186: long this End = getEndMillis();
187: return (this Start <= otherStart && otherStart < this End && otherEnd <= this End);
188: }
189:
190: /**
191: * Does this time interval overlap the specified time interval.
192: * <p>
193: * Intervals are inclusive of the start instant and exclusive of the end.
194: * An interval overlaps another if it shares some common part of the
195: * datetime continuum.
196: * <p>
197: * When two intervals are compared the result is one of three states:
198: * (a) they abut, (b) there is a gap between them, (c) they overlap.
199: * The abuts state takes precedence over the other two, thus a zero duration
200: * interval at the start of a larger interval abuts and does not overlap.
201: * <p>
202: * For example:
203: * <pre>
204: * [09:00 to 10:00) overlaps [08:00 to 08:30) = false (completely before)
205: * [09:00 to 10:00) overlaps [08:00 to 09:00) = false (abuts before)
206: * [09:00 to 10:00) overlaps [08:00 to 09:30) = true
207: * [09:00 to 10:00) overlaps [08:00 to 10:00) = true
208: * [09:00 to 10:00) overlaps [08:00 to 11:00) = true
209: *
210: * [09:00 to 10:00) overlaps [09:00 to 09:00) = false (abuts before)
211: * [09:00 to 10:00) overlaps [09:00 to 09:30) = true
212: * [09:00 to 10:00) overlaps [09:00 to 10:00) = true
213: * [09:00 to 10:00) overlaps [09:00 to 11:00) = true
214: *
215: * [09:00 to 10:00) overlaps [09:30 to 09:30) = true
216: * [09:00 to 10:00) overlaps [09:30 to 10:00) = true
217: * [09:00 to 10:00) overlaps [09:30 to 11:00) = true
218: *
219: * [09:00 to 10:00) overlaps [10:00 to 10:00) = false (abuts after)
220: * [09:00 to 10:00) overlaps [10:00 to 11:00) = false (abuts after)
221: *
222: * [09:00 to 10:00) overlaps [10:30 to 11:00) = false (completely after)
223: *
224: * [14:00 to 14:00) overlaps [14:00 to 14:00) = false (abuts before and after)
225: * [14:00 to 14:00) overlaps [13:00 to 15:00) = true
226: * </pre>
227: *
228: * @param interval the time interval to compare to, null means a zero length interval now
229: * @return true if the time intervals overlap
230: */
231: public boolean overlaps(ReadableInterval interval) {
232: long this Start = getStartMillis();
233: long this End = getEndMillis();
234: if (interval == null) {
235: long now = DateTimeUtils.currentTimeMillis();
236: return (this Start < now && now < this End);
237: } else {
238: long otherStart = interval.getStartMillis();
239: long otherEnd = interval.getEndMillis();
240: return (this Start < otherEnd && otherStart < this End);
241: }
242: }
243:
244: //-----------------------------------------------------------------------
245: /**
246: * Is this time interval before the specified millisecond instant.
247: * <p>
248: * Intervals are inclusive of the start instant and exclusive of the end.
249: *
250: * @param millisInstant the instant to compare to,
251: * millisecond instant from 1970-01-01T00:00:00Z
252: * @return true if this time interval is before the instant
253: */
254: public boolean isBefore(long millisInstant) {
255: return (getEndMillis() <= millisInstant);
256: }
257:
258: /**
259: * Is this time interval before the current instant.
260: * <p>
261: * Intervals are inclusive of the start instant and exclusive of the end.
262: *
263: * @return true if this time interval is before the current instant
264: */
265: public boolean isBeforeNow() {
266: return isBefore(DateTimeUtils.currentTimeMillis());
267: }
268:
269: /**
270: * Is this time interval before the specified instant.
271: * <p>
272: * Intervals are inclusive of the start instant and exclusive of the end.
273: *
274: * @param instant the instant to compare to, null means now
275: * @return true if this time interval is before the instant
276: */
277: public boolean isBefore(ReadableInstant instant) {
278: if (instant == null) {
279: return isBeforeNow();
280: }
281: return isBefore(instant.getMillis());
282: }
283:
284: /**
285: * Is this time interval entirely before the specified instant.
286: * <p>
287: * Intervals are inclusive of the start instant and exclusive of the end.
288: *
289: * @param interval the interval to compare to, null means now
290: * @return true if this time interval is before the interval specified
291: */
292: public boolean isBefore(ReadableInterval interval) {
293: if (interval == null) {
294: return isBeforeNow();
295: }
296: return isBefore(interval.getStartMillis());
297: }
298:
299: //-----------------------------------------------------------------------
300: /**
301: * Is this time interval after the specified millisecond instant.
302: * <p>
303: * Intervals are inclusive of the start instant and exclusive of the end.
304: *
305: * @param millisInstant the instant to compare to,
306: * millisecond instant from 1970-01-01T00:00:00Z
307: * @return true if this time interval is after the instant
308: */
309: public boolean isAfter(long millisInstant) {
310: return (getStartMillis() > millisInstant);
311: }
312:
313: /**
314: * Is this time interval after the current instant.
315: * <p>
316: * Intervals are inclusive of the start instant and exclusive of the end.
317: *
318: * @return true if this time interval is after the current instant
319: */
320: public boolean isAfterNow() {
321: return isAfter(DateTimeUtils.currentTimeMillis());
322: }
323:
324: /**
325: * Is this time interval after the specified instant.
326: * <p>
327: * Intervals are inclusive of the start instant and exclusive of the end.
328: *
329: * @param instant the instant to compare to, null means now
330: * @return true if this time interval is after the instant
331: */
332: public boolean isAfter(ReadableInstant instant) {
333: if (instant == null) {
334: return isAfterNow();
335: }
336: return isAfter(instant.getMillis());
337: }
338:
339: /**
340: * Is this time interval entirely after the specified interval.
341: * <p>
342: * Intervals are inclusive of the start instant and exclusive of the end.
343: * Only the end time of the specified interval is used in the comparison.
344: *
345: * @param interval the interval to compare to, null means now
346: * @return true if this time interval is after the interval specified
347: */
348: public boolean isAfter(ReadableInterval interval) {
349: long endMillis;
350: if (interval == null) {
351: endMillis = DateTimeUtils.currentTimeMillis();
352: } else {
353: endMillis = interval.getEndMillis();
354: }
355: return (getStartMillis() >= endMillis);
356: }
357:
358: //-----------------------------------------------------------------------
359: /**
360: * Get this interval as an immutable <code>Interval</code> object.
361: *
362: * @return the interval as an Interval object
363: */
364: public Interval toInterval() {
365: return new Interval(getStartMillis(), getEndMillis(),
366: getChronology());
367: }
368:
369: /**
370: * Get this time interval as a <code>MutableInterval</code>.
371: * <p>
372: * This will always return a new <code>MutableInterval</code> with the same interval.
373: *
374: * @return the time interval as a MutableInterval object
375: */
376: public MutableInterval toMutableInterval() {
377: return new MutableInterval(getStartMillis(), getEndMillis(),
378: getChronology());
379: }
380:
381: //-----------------------------------------------------------------------
382: /**
383: * Gets the duration of this time interval in milliseconds.
384: * <p>
385: * The duration is equal to the end millis minus the start millis.
386: *
387: * @return the duration of the time interval in milliseconds
388: * @throws ArithmeticException if the duration exceeds the capacity of a long
389: */
390: public long toDurationMillis() {
391: return FieldUtils.safeAdd(getEndMillis(), -getStartMillis());
392: }
393:
394: /**
395: * Gets the duration of this time interval.
396: * <p>
397: * The duration is equal to the end millis minus the start millis.
398: *
399: * @return the duration of the time interval
400: * @throws ArithmeticException if the duration exceeds the capacity of a long
401: */
402: public Duration toDuration() {
403: long durMillis = toDurationMillis();
404: if (durMillis == 0) {
405: return Duration.ZERO;
406: } else {
407: return new Duration(durMillis);
408: }
409: }
410:
411: //-----------------------------------------------------------------------
412: /**
413: * Converts the duration of the interval to a <code>Period</code> using the
414: * All period type.
415: * <p>
416: * This method should be used to exract the field values describing the
417: * difference between the start and end instants.
418: *
419: * @return a time period derived from the interval
420: */
421: public Period toPeriod() {
422: return new Period(getStartMillis(), getEndMillis(),
423: getChronology());
424: }
425:
426: /**
427: * Converts the duration of the interval to a <code>Period</code> using the
428: * specified period type.
429: * <p>
430: * This method should be used to exract the field values describing the
431: * difference between the start and end instants.
432: *
433: * @param type the requested type of the duration, null means AllType
434: * @return a time period derived from the interval
435: */
436: public Period toPeriod(PeriodType type) {
437: return new Period(getStartMillis(), getEndMillis(), type,
438: getChronology());
439: }
440:
441: //-----------------------------------------------------------------------
442: /**
443: * Compares this object with the specified object for equality based
444: * on start and end millis plus the chronology.
445: * All ReadableInterval instances are accepted.
446: * <p>
447: * To compare the duration of two time intervals, use {@link #toDuration()}
448: * to get the durations and compare those.
449: *
450: * @param readableInterval a readable interval to check against
451: * @return true if the start and end millis are equal
452: */
453: public boolean equals(Object readableInterval) {
454: if (this == readableInterval) {
455: return true;
456: }
457: if (readableInterval instanceof ReadableInterval == false) {
458: return false;
459: }
460: ReadableInterval other = (ReadableInterval) readableInterval;
461: return getStartMillis() == other.getStartMillis()
462: && getEndMillis() == other.getEndMillis()
463: && FieldUtils.equals(getChronology(), other
464: .getChronology());
465: }
466:
467: /**
468: * Hashcode compatible with equals method.
469: *
470: * @return suitable hashcode
471: */
472: public int hashCode() {
473: long start = getStartMillis();
474: long end = getEndMillis();
475: int result = 97;
476: result = 31 * result + ((int) (start ^ (start >>> 32)));
477: result = 31 * result + ((int) (end ^ (end >>> 32)));
478: result = 31 * result + getChronology().hashCode();
479: return result;
480: }
481:
482: /**
483: * Output a string in ISO8601 interval format.
484: *
485: * @return re-parsable string
486: */
487: public String toString() {
488: DateTimeFormatter printer = ISODateTimeFormat
489: .dateHourMinuteSecondFraction();
490: printer = printer.withChronology(getChronology());
491: StringBuffer buf = new StringBuffer(48);
492: printer.printTo(buf, getStartMillis());
493: buf.append('/');
494: printer.printTo(buf, getEndMillis());
495: return buf.toString();
496: }
497:
498: }
|