import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
/**
* TimePeriod
*
* A utility class that holds a pair of begin/end times
* These are assumed to be with respect to the same timezone (GMT)
*
* Constructor ensures begin & end are not null
* and that end does not preceede begin.
*
* TimePeriod is immutable.
*
* Simplifies testing for overlapping TimePeriods
*
* @author Jim Pinkham
*/
public class TimePeriod implements Serializable, Cloneable, Comparable
{
protected Date begin;
protected Date end;
static final DateFormat df = DateFormat.getDateTimeInstance();
static final String errorMsg = "TimePeriod end should not precede begin";
/**
* Default Constructor makes 0 length period of time(now-now)
*/
public TimePeriod()
{
this(null, null);
}
/**
* @param begin Time Period start (default now if null)
* @param end Time Period end
* @exception IllegalArgumentException if end < begin
*/
public TimePeriod(Date begin, Date end)
{
// note we make COPIES of input objects
if (end != null)
end = (Date)end.clone();
if (begin != null)
begin = (Date)begin.clone();
if (end == null || begin == null)
{
Date now = new Date();
if (end == null)
end = now;
if (begin == null)
begin = now;
}
if (end.before(begin))
throw new IllegalArgumentException(errorMsg);
this.begin = begin;
this.end = end;
}
/**
* This TimePeriod is the begin/end interval and the other is an event timePeriod .
* This method returns true if the event occurs within the interval.
*
* This does NOT includes events which end exactly at the beginning of the interval,
* or events which begin exactly at the end of the interval.
*
* Instantaneous events with begin == end are a special case. If they occur within
* or at the LEFT (begin) edge, we say they intersect. If they occur at the right edge, they do not.
*
* @param eventPeriod
* @return true if the periods have any overlap
*
*/
public boolean intersects(TimePeriod eventPeriod)
{
if (eventPeriod.begin.equals(eventPeriod.end)) // special case - point events may end AT left edge (begin)
return eventPeriod.begin.before(end) && !eventPeriod.end.before(begin);
if (begin.equals(end)) // special case - interval is a point - event may begin AT point
return !eventPeriod.begin.after(end) && eventPeriod.end.after(begin);
// Event must start before right edge and end after left edge
return eventPeriod.begin.before(end) && eventPeriod.end.after(begin);
}
/**
*
* This does DOES includes events which end exactly at the beginning of the interval,
* or events which begin exactly at the end of the interval.
*
* @param eventPeriod
* @return true if the periods have any overlap inclusively
*
*/
public boolean intersectsInclusive(TimePeriod eventPeriod)
{
if (this.begin.equals(eventPeriod.end)) // events which end exactly at the beginning of the interval
return true;
if (this.end.equals(eventPeriod.begin)) // events which begin exactly at the end of the interval
return true;
// call intersects
return this.intersects(eventPeriod);
}
/**
* @returns true if the given date/time occurs within the interval (endpoints inclusive)
*/
public boolean contains(Date date)
{
return(date.compareTo(begin) >= 0 && date.compareTo(end) <= 0);
}
/**
* @returns the (always positive) number of ms between begin/end
*/
public long getPeriod()
{
return end.getTime() - begin.getTime();
}
/**
* @return the Begin date of the TimePeriod
*/
public Date getBegin()
{
return this.begin;
}
/**
* @return the End date of the TimePeriod
*/
public Date getEnd()
{
return this.end;
}
/**
* @return a printable string
*/
public String toString()
{
return "["+df.format(begin)+","+df.format(end)+"]";
}
/**
* @return 0 if equal, else compares begin dates.
* If begin dates are equal, compares end dates.
*/
public int compareTo(Object obj) //throws ClassCastException
{
TimePeriod otherPeriod = (TimePeriod)obj;
int compareBegin = begin.compareTo(otherPeriod.begin);
if (compareBegin == 0)
return end.compareTo(otherPeriod.end);
return compareBegin;
}
/**
* @return a hashCode
*/
public int hashCode()
{
return (begin.hashCode() ^ (end.hashCode() >>>32));
}
/**
* @return whether the TimePeriods are equal
*/
public boolean equals(Object obj)
{
if (obj instanceof TimePeriod)
{
TimePeriod otherPeriod = (TimePeriod)obj;
return begin.equals(otherPeriod.begin) && end.equals(otherPeriod.end);
}
return false;
}
public Object clone()
throws CloneNotSupportedException
{
return new TimePeriod(begin, end);
}
/**
* @return whether the current time period completely contains the passed in time period
*/
public boolean contains(TimePeriod timePeriod)
{
return begin.before(timePeriod.begin) && end.after(timePeriod.end);
}
/**
* Does this timeperiod "contain" the other (if the endpoints are equal, then include them)
*/
public boolean containsInclusive(TimePeriod timePeriod)
{
boolean b1 = begin.before(timePeriod.begin) || begin.equals(timePeriod.begin);
boolean b2 = end.after(timePeriod.end) || end.equals(timePeriod.end);
return b1 && b2;
}
}