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 }