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