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;
017:
018: import java.io.Serializable;
019:
020: import org.joda.time.base.BaseInterval;
021: import org.joda.time.chrono.ISOChronology;
022: import org.joda.time.format.ISODateTimeFormat;
023: import org.joda.time.format.ISOPeriodFormat;
024:
025: /**
026: * Interval is the standard implementation of an immutable time interval.
027: * <p>
028: * A time interval represents a period of time between two instants.
029: * Intervals are inclusive of the start instant and exclusive of the end.
030: * The end instant is always greater than or equal to the start instant.
031: * <p>
032: * Intervals have a fixed millisecond duration.
033: * This is the difference between the start and end instants.
034: * The duration is represented separately by {@link ReadableDuration}.
035: * As a result, intervals are not comparable.
036: * To compare the length of two intervals, you should compare their durations.
037: * <p>
038: * An interval can also be converted to a {@link ReadablePeriod}.
039: * This represents the difference between the start and end points in terms of fields
040: * such as years and days.
041: * <p>
042: * Interval is thread-safe and immutable.
043: *
044: * @author Brian S O'Neill
045: * @author Sean Geoghegan
046: * @author Stephen Colebourne
047: * @author Julen Parra
048: * @since 1.0
049: */
050: public final class Interval extends BaseInterval implements
051: ReadableInterval, Serializable {
052:
053: /** Serialization version */
054: private static final long serialVersionUID = 4922451897541386752L;
055:
056: //-----------------------------------------------------------------------
057: /**
058: * Constructs an interval from a start and end instant with the ISO
059: * default chronology in the default time zone.
060: *
061: * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
062: * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
063: * @throws IllegalArgumentException if the end is before the start
064: */
065: public Interval(long startInstant, long endInstant) {
066: super (startInstant, endInstant, null);
067: }
068:
069: /**
070: * Constructs an interval from a start and end instant with the ISO
071: * default chronology in the specified time zone.
072: *
073: * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
074: * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
075: * @param zone the time zone to use, null means default zone
076: * @throws IllegalArgumentException if the end is before the start
077: * @since 1.5
078: */
079: public Interval(long startInstant, long endInstant,
080: DateTimeZone zone) {
081: super (startInstant, endInstant, ISOChronology.getInstance(zone));
082: }
083:
084: /**
085: * Constructs an interval from a start and end instant with the
086: * specified chronology.
087: *
088: * @param chronology the chronology to use, null is ISO default
089: * @param startInstant start of this interval, as milliseconds from 1970-01-01T00:00:00Z.
090: * @param endInstant end of this interval, as milliseconds from 1970-01-01T00:00:00Z.
091: * @throws IllegalArgumentException if the end is before the start
092: */
093: public Interval(long startInstant, long endInstant,
094: Chronology chronology) {
095: super (startInstant, endInstant, chronology);
096: }
097:
098: /**
099: * Constructs an interval from a start and end instant.
100: * <p>
101: * The chronology used is that of the start instant.
102: *
103: * @param start start of this interval, null means now
104: * @param end end of this interval, null means now
105: * @throws IllegalArgumentException if the end is before the start
106: */
107: public Interval(ReadableInstant start, ReadableInstant end) {
108: super (start, end);
109: }
110:
111: /**
112: * Constructs an interval from a start instant and a duration.
113: *
114: * @param start start of this interval, null means now
115: * @param duration the duration of this interval, null means zero length
116: * @throws IllegalArgumentException if the end is before the start
117: * @throws ArithmeticException if the end instant exceeds the capacity of a long
118: */
119: public Interval(ReadableInstant start, ReadableDuration duration) {
120: super (start, duration);
121: }
122:
123: /**
124: * Constructs an interval from a millisecond duration and an end instant.
125: *
126: * @param duration the duration of this interval, null means zero length
127: * @param end end of this interval, null means now
128: * @throws IllegalArgumentException if the end is before the start
129: * @throws ArithmeticException if the start instant exceeds the capacity of a long
130: */
131: public Interval(ReadableDuration duration, ReadableInstant end) {
132: super (duration, end);
133: }
134:
135: /**
136: * Constructs an interval from a start instant and a time period.
137: * <p>
138: * When forming the interval, the chronology from the instant is used
139: * if present, otherwise the chronology of the period is used.
140: *
141: * @param start start of this interval, null means now
142: * @param period the period of this interval, null means zero length
143: * @throws IllegalArgumentException if the end is before the start
144: * @throws ArithmeticException if the end instant exceeds the capacity of a long
145: */
146: public Interval(ReadableInstant start, ReadablePeriod period) {
147: super (start, period);
148: }
149:
150: /**
151: * Constructs an interval from a time period and an end instant.
152: * <p>
153: * When forming the interval, the chronology from the instant is used
154: * if present, otherwise the chronology of the period is used.
155: *
156: * @param period the period of this interval, null means zero length
157: * @param end end of this interval, null means now
158: * @throws IllegalArgumentException if the end is before the start
159: * @throws ArithmeticException if the start instant exceeds the capacity of a long
160: */
161: public Interval(ReadablePeriod period, ReadableInstant end) {
162: super (period, end);
163: }
164:
165: /**
166: * Constructs a time interval by converting or copying from another object.
167: * <p>
168: * The recognised object types are defined in
169: * {@link org.joda.time.convert.ConverterManager ConverterManager} and
170: * include ReadableInterval and String.
171: * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()}
172: * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime',
173: * 'datetime/period' or 'period/datetime'.
174: *
175: * @param interval the time interval to copy
176: * @throws IllegalArgumentException if the interval is invalid
177: */
178: public Interval(Object interval) {
179: super (interval, null);
180: }
181:
182: /**
183: * Constructs a time interval by converting or copying from another object,
184: * overriding the chronology.
185: * <p>
186: * The recognised object types are defined in
187: * {@link org.joda.time.convert.ConverterManager ConverterManager} and
188: * include ReadableInterval and String.
189: * The String formats are described by {@link ISODateTimeFormat#dateTimeParser()}
190: * and {@link ISOPeriodFormat#standard()}, and may be 'datetime/datetime',
191: * 'datetime/period' or 'period/datetime'.
192: *
193: * @param interval the time interval to copy
194: * @param chronology the chronology to use, null means ISO default
195: * @throws IllegalArgumentException if the interval is invalid
196: */
197: public Interval(Object interval, Chronology chronology) {
198: super (interval, chronology);
199: }
200:
201: //-----------------------------------------------------------------------
202: /**
203: * Get this interval as an immutable <code>Interval</code> object
204: * by returning <code>this</code>.
205: *
206: * @return <code>this</code>
207: */
208: public Interval toInterval() {
209: return this ;
210: }
211:
212: //-----------------------------------------------------------------------
213: /**
214: * Gets the overlap between this interval and another interval.
215: * <p>
216: * Intervals are inclusive of the start instant and exclusive of the end.
217: * An interval overlaps another if it shares some common part of the
218: * datetime continuum. This method returns the amount of the overlap,
219: * only if the intervals actually do overlap.
220: * If the intervals do not overlap, then null is returned.
221: * <p>
222: * When two intervals are compared the result is one of three states:
223: * (a) they abut, (b) there is a gap between them, (c) they overlap.
224: * The abuts state takes precedence over the other two, thus a zero duration
225: * interval at the start of a larger interval abuts and does not overlap.
226: * <p>
227: * The chronology of the returned interval is the same as that of
228: * this interval (the chronology of the interval parameter is not used).
229: * Note that the use of the chronology was only correctly implemented
230: * in version 1.3.
231: *
232: * @param interval the interval to examine, null means now
233: * @return the overlap interval, null if no overlap
234: * @since 1.1
235: */
236: public Interval overlap(ReadableInterval interval) {
237: interval = DateTimeUtils.getReadableInterval(interval);
238: if (overlaps(interval) == false) {
239: return null;
240: }
241: long start = Math.max(getStartMillis(), interval
242: .getStartMillis());
243: long end = Math.min(getEndMillis(), interval.getEndMillis());
244: return new Interval(start, end, getChronology());
245: }
246:
247: //-----------------------------------------------------------------------
248: /**
249: * Gets the gap between this interval and another interval.
250: * The other interval can be either before or after this interval.
251: * <p>
252: * Intervals are inclusive of the start instant and exclusive of the end.
253: * An interval has a gap to another interval if there is a non-zero
254: * duration between them. This method returns the amount of the gap only
255: * if the intervals do actually have a gap between them.
256: * If the intervals overlap or abut, then null is returned.
257: * <p>
258: * When two intervals are compared the result is one of three states:
259: * (a) they abut, (b) there is a gap between them, (c) they overlap.
260: * The abuts state takes precedence over the other two, thus a zero duration
261: * interval at the start of a larger interval abuts and does not overlap.
262: * <p>
263: * The chronology of the returned interval is the same as that of
264: * this interval (the chronology of the interval parameter is not used).
265: * Note that the use of the chronology was only correctly implemented
266: * in version 1.3.
267: *
268: * @param interval the interval to examine, null means now
269: * @return the gap interval, null if no gap
270: * @since 1.1
271: */
272: public Interval gap(ReadableInterval interval) {
273: interval = DateTimeUtils.getReadableInterval(interval);
274: long otherStart = interval.getStartMillis();
275: long otherEnd = interval.getEndMillis();
276: long this Start = getStartMillis();
277: long this End = getEndMillis();
278: if (this Start > otherEnd) {
279: return new Interval(otherEnd, this Start, getChronology());
280: } else if (otherStart > this End) {
281: return new Interval(this End, otherStart, getChronology());
282: } else {
283: return null;
284: }
285: }
286:
287: //-----------------------------------------------------------------------
288: /**
289: * Does this interval abut with the interval specified.
290: * <p>
291: * Intervals are inclusive of the start instant and exclusive of the end.
292: * An interval abuts if it starts immediately after, or ends immediately
293: * before this interval without overlap.
294: * A zero duration interval abuts with itself.
295: * <p>
296: * When two intervals are compared the result is one of three states:
297: * (a) they abut, (b) there is a gap between them, (c) they overlap.
298: * The abuts state takes precedence over the other two, thus a zero duration
299: * interval at the start of a larger interval abuts and does not overlap.
300: * <p>
301: * For example:
302: * <pre>
303: * [09:00 to 10:00) abuts [08:00 to 08:30) = false (completely before)
304: * [09:00 to 10:00) abuts [08:00 to 09:00) = true
305: * [09:00 to 10:00) abuts [08:00 to 09:01) = false (overlaps)
306: *
307: * [09:00 to 10:00) abuts [09:00 to 09:00) = true
308: * [09:00 to 10:00) abuts [09:00 to 09:01) = false (overlaps)
309: *
310: * [09:00 to 10:00) abuts [10:00 to 10:00) = true
311: * [09:00 to 10:00) abuts [10:00 to 10:30) = true
312: *
313: * [09:00 to 10:00) abuts [10:30 to 11:00) = false (completely after)
314: *
315: * [14:00 to 14:00) abuts [14:00 to 14:00) = true
316: * [14:00 to 14:00) abuts [14:00 to 15:00) = true
317: * [14:00 to 14:00) abuts [13:00 to 14:00) = true
318: * </pre>
319: *
320: * @param interval the interval to examine, null means now
321: * @return true if the interval abuts
322: * @since 1.1
323: */
324: public boolean abuts(ReadableInterval interval) {
325: if (interval == null) {
326: long now = DateTimeUtils.currentTimeMillis();
327: return (getStartMillis() == now || getEndMillis() == now);
328: } else {
329: return (interval.getEndMillis() == getStartMillis() || getEndMillis() == interval
330: .getStartMillis());
331: }
332: }
333:
334: //-----------------------------------------------------------------------
335: /**
336: * Creates a new interval with the same start and end, but a different chronology.
337: *
338: * @param chronology the chronology to use, null means ISO default
339: * @return an interval with a different chronology
340: */
341: public Interval withChronology(Chronology chronology) {
342: if (getChronology() == chronology) {
343: return this ;
344: }
345: return new Interval(getStartMillis(), getEndMillis(),
346: chronology);
347: }
348:
349: /**
350: * Creates a new interval with the specified start millisecond instant.
351: *
352: * @param startInstant the start instant for the new interval
353: * @return an interval with the end from this interval and the specified start
354: * @throws IllegalArgumentException if the resulting interval has end before start
355: */
356: public Interval withStartMillis(long startInstant) {
357: if (startInstant == getStartMillis()) {
358: return this ;
359: }
360: return new Interval(startInstant, getEndMillis(),
361: getChronology());
362: }
363:
364: /**
365: * Creates a new interval with the specified start instant.
366: *
367: * @param start the start instant for the new interval, null means now
368: * @return an interval with the end from this interval and the specified start
369: * @throws IllegalArgumentException if the resulting interval has end before start
370: */
371: public Interval withStart(ReadableInstant start) {
372: long startMillis = DateTimeUtils.getInstantMillis(start);
373: return withStartMillis(startMillis);
374: }
375:
376: /**
377: * Creates a new interval with the specified start millisecond instant.
378: *
379: * @param endInstant the end instant for the new interval
380: * @return an interval with the start from this interval and the specified end
381: * @throws IllegalArgumentException if the resulting interval has end before start
382: */
383: public Interval withEndMillis(long endInstant) {
384: if (endInstant == getEndMillis()) {
385: return this ;
386: }
387: return new Interval(getStartMillis(), endInstant,
388: getChronology());
389: }
390:
391: /**
392: * Creates a new interval with the specified end instant.
393: *
394: * @param end the end instant for the new interval, null means now
395: * @return an interval with the start from this interval and the specified end
396: * @throws IllegalArgumentException if the resulting interval has end before start
397: */
398: public Interval withEnd(ReadableInstant end) {
399: long endMillis = DateTimeUtils.getInstantMillis(end);
400: return withEndMillis(endMillis);
401: }
402:
403: //-----------------------------------------------------------------------
404: /**
405: * Creates a new interval with the specified duration after the start instant.
406: *
407: * @param duration the duration to add to the start to get the new end instant, null means zero
408: * @return an interval with the start from this interval and a calculated end
409: * @throws IllegalArgumentException if the duration is negative
410: */
411: public Interval withDurationAfterStart(ReadableDuration duration) {
412: long durationMillis = DateTimeUtils.getDurationMillis(duration);
413: if (durationMillis == toDurationMillis()) {
414: return this ;
415: }
416: Chronology chrono = getChronology();
417: long startMillis = getStartMillis();
418: long endMillis = chrono.add(startMillis, durationMillis, 1);
419: return new Interval(startMillis, endMillis, chrono);
420: }
421:
422: /**
423: * Creates a new interval with the specified duration before the end instant.
424: *
425: * @param duration the duration to add to the start to get the new end instant, null means zero
426: * @return an interval with the start from this interval and a calculated end
427: * @throws IllegalArgumentException if the duration is negative
428: */
429: public Interval withDurationBeforeEnd(ReadableDuration duration) {
430: long durationMillis = DateTimeUtils.getDurationMillis(duration);
431: if (durationMillis == toDurationMillis()) {
432: return this ;
433: }
434: Chronology chrono = getChronology();
435: long endMillis = getEndMillis();
436: long startMillis = chrono.add(endMillis, durationMillis, -1);
437: return new Interval(startMillis, endMillis, chrono);
438: }
439:
440: //-----------------------------------------------------------------------
441: /**
442: * Creates a new interval with the specified period after the start instant.
443: *
444: * @param period the period to add to the start to get the new end instant, null means zero
445: * @return an interval with the start from this interval and a calculated end
446: * @throws IllegalArgumentException if the period is negative
447: */
448: public Interval withPeriodAfterStart(ReadablePeriod period) {
449: if (period == null) {
450: return withDurationAfterStart(null);
451: }
452: Chronology chrono = getChronology();
453: long startMillis = getStartMillis();
454: long endMillis = chrono.add(period, startMillis, 1);
455: return new Interval(startMillis, endMillis, chrono);
456: }
457:
458: /**
459: * Creates a new interval with the specified period before the end instant.
460: *
461: * @param period the period to add to the start to get the new end instant, null means zero
462: * @return an interval with the start from this interval and a calculated end
463: * @throws IllegalArgumentException if the period is negative
464: */
465: public Interval withPeriodBeforeEnd(ReadablePeriod period) {
466: if (period == null) {
467: return withDurationBeforeEnd(null);
468: }
469: Chronology chrono = getChronology();
470: long endMillis = getEndMillis();
471: long startMillis = chrono.add(period, endMillis, -1);
472: return new Interval(startMillis, endMillis, chrono);
473: }
474:
475: }
|