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; } }