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