/* * Copyright 2001-2011 Stephen Colebourne * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.joda.time.base; import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; import org.joda.time.Duration; import org.joda.time.Interval; import org.joda.time.MutableInterval; import org.joda.time.Period; import org.joda.time.PeriodType; import org.joda.time.ReadableInstant; import org.joda.time.ReadableInterval; import org.joda.time.field.FieldUtils; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; /** * AbstractInterval provides the common behaviour for time intervals. *
* This class should generally not be used directly by API users. The * {@link ReadableInterval} interface should be used when different * kinds of intervals are to be referenced. *
* AbstractInterval subclasses may be mutable and not thread-safe. * * @author Brian S O'Neill * @author Stephen Colebourne * @since 1.0 */ public abstract class AbstractInterval implements ReadableInterval { /** * Constructor. */ protected AbstractInterval() { super(); } //----------------------------------------------------------------------- /** * Validates an interval. * * @param start the start instant in milliseconds * @param end the end instant in milliseconds * @throws IllegalArgumentException if the interval is invalid */ protected void checkInterval(long start, long end) { if (end < start) { throw new IllegalArgumentException("The end instant must be greater or equal to the start"); } } //----------------------------------------------------------------------- /** * Gets the start of this time interval, which is inclusive, as a DateTime. * * @return the start of the time interval */ public DateTime getStart() { return new DateTime(getStartMillis(), getChronology()); } /** * Gets the end of this time interval, which is exclusive, as a DateTime. * * @return the end of the time interval */ public DateTime getEnd() { return new DateTime(getEndMillis(), getChronology()); } //----------------------------------------------------------------------- /** * Does this time interval contain the specified millisecond instant. *
* Non-zero duration intervals are inclusive of the start instant and * exclusive of the end. A zero duration interval cannot contain anything. * * @param millisInstant the instant to compare to, * millisecond instant from 1970-01-01T00:00:00Z * @return true if this time interval contains the millisecond */ public boolean contains(long millisInstant) { long thisStart = getStartMillis(); long thisEnd = getEndMillis(); return (millisInstant >= thisStart && millisInstant < thisEnd); } /** * Does this time interval contain the current instant. *
* Non-zero duration intervals are inclusive of the start instant and * exclusive of the end. A zero duration interval cannot contain anything. * * @return true if this time interval contains the current instant */ public boolean containsNow() { return contains(DateTimeUtils.currentTimeMillis()); } /** * Does this time interval contain the specified instant. *
* Non-zero duration intervals are inclusive of the start instant and * exclusive of the end. A zero duration interval cannot contain anything. *
* For example: *
* [09:00 to 10:00) contains 08:59 = false (before start) * [09:00 to 10:00) contains 09:00 = true * [09:00 to 10:00) contains 09:59 = true * [09:00 to 10:00) contains 10:00 = false (equals end) * [09:00 to 10:00) contains 10:01 = false (after end) * * [14:00 to 14:00) contains 14:00 = false (zero duration contains nothing) ** Passing in a
null
parameter will have the same effect as
* calling {@link #containsNow()}.
*
* @param instant the instant, null means now
* @return true if this time interval contains the instant
*/
public boolean contains(ReadableInstant instant) {
if (instant == null) {
return containsNow();
}
return contains(instant.getMillis());
}
/**
* Does this time interval contain the specified time interval.
* * Non-zero duration intervals are inclusive of the start instant and * exclusive of the end. The other interval is contained if this interval * wholly contains, starts, finishes or equals it. * A zero duration interval cannot contain anything. *
* When two intervals are compared the result is one of three states:
* (a) they abut, (b) there is a gap between them, (c) they overlap.
* The contains
method is not related to these states.
* In particular, a zero duration interval is contained at the start of
* a larger interval, but does not overlap (it abuts instead).
*
* For example: *
* [09:00 to 10:00) contains [09:00 to 10:00) = true * [09:00 to 10:00) contains [09:00 to 09:30) = true * [09:00 to 10:00) contains [09:30 to 10:00) = true * [09:00 to 10:00) contains [09:15 to 09:45) = true * [09:00 to 10:00) contains [09:00 to 09:00) = true * * [09:00 to 10:00) contains [08:59 to 10:00) = false (otherStart before thisStart) * [09:00 to 10:00) contains [09:00 to 10:01) = false (otherEnd after thisEnd) * [09:00 to 10:00) contains [10:00 to 10:00) = false (otherStart equals thisEnd) * * [14:00 to 14:00) contains [14:00 to 14:00) = false (zero duration contains nothing) ** Passing in a
null
parameter will have the same effect as
* calling {@link #containsNow()}.
*
* @param interval the time interval to compare to, null means a zero duration interval now
* @return true if this time interval contains the time interval
*/
public boolean contains(ReadableInterval interval) {
if (interval == null) {
return containsNow();
}
long otherStart = interval.getStartMillis();
long otherEnd = interval.getEndMillis();
long thisStart = getStartMillis();
long thisEnd = getEndMillis();
return (thisStart <= otherStart && otherStart < thisEnd && otherEnd <= thisEnd);
}
/**
* Does this time interval overlap the specified time interval.
* * Intervals are inclusive of the start instant and exclusive of the end. * An interval overlaps another if it shares some common part of the * datetime continuum. *
* When two intervals are compared the result is one of three states: * (a) they abut, (b) there is a gap between them, (c) they overlap. * The abuts state takes precedence over the other two, thus a zero duration * interval at the start of a larger interval abuts and does not overlap. *
* For example: *
* [09:00 to 10:00) overlaps [08:00 to 08:30) = false (completely before) * [09:00 to 10:00) overlaps [08:00 to 09:00) = false (abuts before) * [09:00 to 10:00) overlaps [08:00 to 09:30) = true * [09:00 to 10:00) overlaps [08:00 to 10:00) = true * [09:00 to 10:00) overlaps [08:00 to 11:00) = true * * [09:00 to 10:00) overlaps [09:00 to 09:00) = false (abuts before) * [09:00 to 10:00) overlaps [09:00 to 09:30) = true * [09:00 to 10:00) overlaps [09:00 to 10:00) = true * [09:00 to 10:00) overlaps [09:00 to 11:00) = true * * [09:00 to 10:00) overlaps [09:30 to 09:30) = true * [09:00 to 10:00) overlaps [09:30 to 10:00) = true * [09:00 to 10:00) overlaps [09:30 to 11:00) = true * * [09:00 to 10:00) overlaps [10:00 to 10:00) = false (abuts after) * [09:00 to 10:00) overlaps [10:00 to 11:00) = false (abuts after) * * [09:00 to 10:00) overlaps [10:30 to 11:00) = false (completely after) * * [14:00 to 14:00) overlaps [14:00 to 14:00) = false (abuts before and after) * [14:00 to 14:00) overlaps [13:00 to 15:00) = true ** * @param interval the time interval to compare to, null means a zero length interval now * @return true if the time intervals overlap */ public boolean overlaps(ReadableInterval interval) { long thisStart = getStartMillis(); long thisEnd = getEndMillis(); if (interval == null) { long now = DateTimeUtils.currentTimeMillis(); return (thisStart < now && now < thisEnd); } else { long otherStart = interval.getStartMillis(); long otherEnd = interval.getEndMillis(); return (thisStart < otherEnd && otherStart < thisEnd); } } //----------------------------------------------------------------------- /** * Is this time interval before the specified millisecond instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @param millisInstant the instant to compare to, * millisecond instant from 1970-01-01T00:00:00Z * @return true if this time interval is before the instant */ public boolean isBefore(long millisInstant) { return (getEndMillis() <= millisInstant); } /** * Is this time interval before the current instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @return true if this time interval is before the current instant */ public boolean isBeforeNow() { return isBefore(DateTimeUtils.currentTimeMillis()); } /** * Is this time interval before the specified instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @param instant the instant to compare to, null means now * @return true if this time interval is before the instant */ public boolean isBefore(ReadableInstant instant) { if (instant == null) { return isBeforeNow(); } return isBefore(instant.getMillis()); } /** * Is this time interval entirely before the specified instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @param interval the interval to compare to, null means now * @return true if this time interval is before the interval specified */ public boolean isBefore(ReadableInterval interval) { if (interval == null) { return isBeforeNow(); } return isBefore(interval.getStartMillis()); } //----------------------------------------------------------------------- /** * Is this time interval after the specified millisecond instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @param millisInstant the instant to compare to, * millisecond instant from 1970-01-01T00:00:00Z * @return true if this time interval is after the instant */ public boolean isAfter(long millisInstant) { return (getStartMillis() > millisInstant); } /** * Is this time interval after the current instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @return true if this time interval is after the current instant */ public boolean isAfterNow() { return isAfter(DateTimeUtils.currentTimeMillis()); } /** * Is this time interval after the specified instant. *
* Intervals are inclusive of the start instant and exclusive of the end. * * @param instant the instant to compare to, null means now * @return true if this time interval is after the instant */ public boolean isAfter(ReadableInstant instant) { if (instant == null) { return isAfterNow(); } return isAfter(instant.getMillis()); } /** * Is this time interval entirely after the specified interval. *
* Intervals are inclusive of the start instant and exclusive of the end.
* Only the end time of the specified interval is used in the comparison.
*
* @param interval the interval to compare to, null means now
* @return true if this time interval is after the interval specified
*/
public boolean isAfter(ReadableInterval interval) {
long endMillis;
if (interval == null) {
endMillis = DateTimeUtils.currentTimeMillis();
} else {
endMillis = interval.getEndMillis();
}
return (getStartMillis() >= endMillis);
}
//-----------------------------------------------------------------------
/**
* Get this interval as an immutable Interval
object.
*
* @return the interval as an Interval object
*/
public Interval toInterval() {
return new Interval(getStartMillis(), getEndMillis(), getChronology());
}
/**
* Get this time interval as a MutableInterval
.
*
* This will always return a new MutableInterval
with the same interval.
*
* @return the time interval as a MutableInterval object
*/
public MutableInterval toMutableInterval() {
return new MutableInterval(getStartMillis(), getEndMillis(), getChronology());
}
//-----------------------------------------------------------------------
/**
* Gets the duration of this time interval in milliseconds.
*
* The duration is equal to the end millis minus the start millis. * * @return the duration of the time interval in milliseconds * @throws ArithmeticException if the duration exceeds the capacity of a long */ public long toDurationMillis() { return FieldUtils.safeAdd(getEndMillis(), -getStartMillis()); } /** * Gets the duration of this time interval. *
* The duration is equal to the end millis minus the start millis.
*
* @return the duration of the time interval
* @throws ArithmeticException if the duration exceeds the capacity of a long
*/
public Duration toDuration() {
long durMillis = toDurationMillis();
if (durMillis == 0) {
return Duration.ZERO;
} else {
return new Duration(durMillis);
}
}
//-----------------------------------------------------------------------
/**
* Converts the duration of the interval to a Period
using the
* All period type.
*
* This method should be used to exract the field values describing the
* difference between the start and end instants.
*
* @return a time period derived from the interval
*/
public Period toPeriod() {
return new Period(getStartMillis(), getEndMillis(), getChronology());
}
/**
* Converts the duration of the interval to a Period
using the
* specified period type.
*
* This method should be used to exract the field values describing the * difference between the start and end instants. * * @param type the requested type of the duration, null means AllType * @return a time period derived from the interval */ public Period toPeriod(PeriodType type) { return new Period(getStartMillis(), getEndMillis(), type, getChronology()); } //----------------------------------------------------------------------- /** * Compares this object with the specified object for equality based * on start and end millis plus the chronology. * All ReadableInterval instances are accepted. *
* To compare the duration of two time intervals, use {@link #toDuration()} * to get the durations and compare those. * * @param readableInterval a readable interval to check against * @return true if the start and end millis are equal */ public boolean equals(Object readableInterval) { if (this == readableInterval) { return true; } if (readableInterval instanceof ReadableInterval == false) { return false; } ReadableInterval other = (ReadableInterval) readableInterval; return getStartMillis() == other.getStartMillis() && getEndMillis() == other.getEndMillis() && FieldUtils.equals(getChronology(), other.getChronology()); } /** * Hashcode compatible with equals method. * * @return suitable hashcode */ public int hashCode() { long start = getStartMillis(); long end = getEndMillis(); int result = 97; result = 31 * result + ((int) (start ^ (start >>> 32))); result = 31 * result + ((int) (end ^ (end >>> 32))); result = 31 * result + getChronology().hashCode(); return result; } /** * Output a string in ISO8601 interval format. *
* From version 2.1, the string includes the time zone offset. * * @return re-parsable string (in the default zone) */ public String toString() { DateTimeFormatter printer = ISODateTimeFormat.dateTime(); printer = printer.withChronology(getChronology()); StringBuffer buf = new StringBuffer(48); printer.printTo(buf, getStartMillis()); buf.append('/'); printer.printTo(buf, getEndMillis()); return buf.toString(); } }