/* * $Id: Period.java,v 1.12 2006/07/23 08:24:10 fortuna Exp $ [Apr 14, 2004] * * Copyright (c) 2005, Ben Fortuna * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * o Neither the name of Ben Fortuna nor the names of any other contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.fortuna.ical4j.model; import java.io.Serializable; import java.text.ParseException; import java.util.Date; /** * Defines a period of time. A period may be specified as either a start date * and end date, or a start date and duration. NOTE: End dates and durations are * implicitly derived when not explicitly specified. This means that you cannot * rely on the returned values from the getters to deduce whether a period has * an explicit end date or duration. * * @author Ben Fortuna */ public class Period implements Serializable, Comparable { private static final long serialVersionUID = 7321090422911676490L; private DateTime start; private DateTime end; private Dur duration; /** * Constructor. * * @param aValue * a string representation of a period * @throws ParseException * where the specified string is not a valid representation */ public Period(final String aValue) throws ParseException { start = new DateTime(aValue.substring(0, aValue.indexOf('/'))); // period may end in either a date-time or a duration.. try { end = new DateTime(aValue.substring(aValue.indexOf('/') + 1)); } catch (ParseException pe) { // duration = DurationFormat.getInstance().parse(aValue); duration = new Dur(aValue); } } /** * Constructs a new period with the specied start and end date. * * @param start * the start date of the period * @param end * the end date of the period */ public Period(final DateTime start, final DateTime end) { this.start = start; this.end = end; } /** * Constructs a new period with the specified start date and duration. * * @param start * the start date of the period * @param duration * the duration of the period */ public Period(final DateTime start, final Dur duration) { this.start = start; this.duration = duration; } /** * Returns the duration of this period. If an explicit duration is not * specified, the duration is derived from the end date. * * @return the duration of this period in milliseconds. */ public final Dur getDuration() { if (end != null) { return new Dur(start, end); } return duration; } /** * Returns the end date of this period. If an explicit end date is not * specified, the end date is derived from the duration. * * @return the end date of this period. */ public final DateTime getEnd() { if (end == null) { DateTime derived = new DateTime(duration.getTime(start).getTime()); derived.setUtc(start.isUtc()); return derived; } return end; } /** * @return Returns the start. */ public final DateTime getStart() { return start; } /** * Determines if the specified date occurs within this period (inclusive of * period start and end). * @param date * @return true if the specified date occurs within the current period * */ public final boolean includes(final Date date) { return includes(date, true); } /** * Decides whether a date falls within this period. * @param date the date to be tested * @param inclusive specifies whether period start and end are included * in the calculation * @return true if the date is in the perod, false otherwise */ public final boolean includes(final Date date, final boolean inclusive) { if (inclusive) { return (!getStart().after(date) && !getEnd().before(date)); } return (getStart().before(date) && getEnd().after(date)); } /** * Decides whether this period is completed before the given period starts. * * @param period * a period that may or may not start after this period ends * @return true if the specified period starts after this periods ends, * otherwise false */ public final boolean before(final Period period) { return (getEnd().before(period.getStart())); } /** * Decides whether this period starts after the given period ends. * * @param period * a period that may or may not end before this period starts * @return true if the specified period end before this periods starts, * otherwise false */ public final boolean after(final Period period) { return (getStart().after(period.getEnd())); } /** * Decides whether this period intersects with another one. * * @param period * a possible intersecting period * @return true if the specified period intersects this one, false * otherwise. */ public final boolean intersects(final Period period) { // Test for our start date in period // (Exclude if it is the end date of test range) if (period.includes(getStart()) && !period.getEnd().equals(getStart())) { return true; } // Test for test range's start date in our range // (Exclude if it is the end date of our range) else if (includes(period.getStart()) && !getEnd().equals(period.getStart())) { return true; } return false; } /** * Decides whether these periods are serial without a gap. * * @return true if one period immediately follows the other, false otherwise */ public final boolean adjacent(final Period period) { if (getStart().equals(period.getEnd())) { return true; } else if (getEnd().equals(period.getStart())) { return true; } return false; } /** * Decides whether the given period is completely contained within this one. * * @param period * the period that may be contained by this one * @return true if this period covers all the dates of the specified period, * otherwise false */ public final boolean contains(final Period period) { // Test for period's start and end dates in our range return (includes(period.getStart()) && includes(period.getEnd())); } /** * Creates a period that encompasses both this period and another one. If * the other period is null, return a copy of this period. NOTE: Resulting * periods are specified by explicitly setting a start date and end date * (i.e. durations are implied). * * @param period * the period to add to this one * @return a period */ public final Period add(final Period period) { DateTime newPeriodStart = null; DateTime newPeriodEnd = null; if (period == null) { newPeriodStart = getStart(); newPeriodEnd = getEnd(); } else { if (getStart().before(period.getStart())) { newPeriodStart = getStart(); } else { newPeriodStart = period.getStart(); } if (getEnd().after(period.getEnd())) { newPeriodEnd = getEnd(); } else { newPeriodEnd = period.getEnd(); } } return new Period(newPeriodStart, newPeriodEnd); } /** * @see java.lang.Object#toString() */ public final String toString() { StringBuffer b = new StringBuffer(); b.append(start); b.append('/'); if (end != null) { b.append(end); } else { // b.append(DurationFormat.getInstance().format(duration)); b.append(duration); } return b.toString(); } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public final int compareTo(final Object arg0) { return compareTo((Period) arg0); } /** * Compares the specified period with this period. * * @param arg0 * @return */ public final int compareTo(final Period arg0) { // Throws documented exception if type is wrong or parameter is null if (arg0 == null) { throw new ClassCastException("Cannot compare this object to null"); } int startCompare = getStart().compareTo(arg0.getStart()); if (startCompare != 0) { return startCompare; } // start dates are equal, compare end dates.. else if (end != null) { int endCompare = end.compareTo(arg0.getEnd()); if (endCompare != 0) { return endCompare; } } // ..or durations return getDuration().compareTo(arg0.getDuration()); } /** * Overrides the equality test, compares fields of instances for equality. * * @param o * object being compared for equality * @return true if the objects are equal, false otherwise */ public final boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof Period)) { return false; } final Period period = (Period) o; if (!getStart().equals(period.getStart())) { return false; } else if (!getEnd().equals(period.getEnd())) { return false; } return true; } /** * Override hashCode() with code that checks fields in this object. * * @return hascode for this object */ public final int hashCode() { int result; result = getStart().hashCode(); result = 29 * result + getEnd().hashCode(); return result; } }