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 }