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
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    }