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