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 }