net.fortuna.ical4j.model.Period Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ical4j Show documentation
Show all versions of ical4j Show documentation
A Java library for reading and writing iCalendar (*.ics) files
/**
* Copyright (c) 2012, 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.text.ParseException;
import java.time.temporal.TemporalAmount;
import java.util.Date;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* $Id$ [Apr 14, 2004]
*
* 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 extends DateRange implements Comparable {
private static final long serialVersionUID = 7321090422911676490L;
private TemporalAmountAdapter duration;
private Component component;
/**
* 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 {
super(parseStartDate(aValue), parseEndDate(aValue, true));
// period may end in either a date-time or a duration..
try {
parseEndDate(aValue, false);
}
catch (ParseException pe) {
// duration = DurationFormat.getInstance().parse(aValue);
duration = parseDuration(aValue);
}
normalise();
}
/**
* 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) {
super(start, end);
normalise();
}
/**
* 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
*/
@Deprecated
public Period(final DateTime start, final Dur duration) {
this(start, TemporalAmountAdapter.from(duration).getDuration());
}
/**
* 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 TemporalAmount duration) {
super(start, new DateTime(Date.from(start.toInstant().plus(duration))));
this.duration = new TemporalAmountAdapter(duration);
normalise();
}
private static DateTime parseStartDate(String value) throws ParseException {
return new DateTime(value.substring(0, value.indexOf('/')));
}
private static DateTime parseEndDate(String value, boolean resolve) throws ParseException {
DateTime end;
try {
end = new DateTime(value.substring(value.indexOf('/') + 1));
}
catch (ParseException e) {
if (resolve) {
final TemporalAmount duration = parseDuration(value).getDuration();
end = new DateTime(Date.from(parseStartDate(value).toInstant().plus(duration)));
}
else {
throw e;
}
}
return end;
}
private static TemporalAmountAdapter parseDuration(String value) {
String durationString = value.substring(value.indexOf('/') + 1);
return TemporalAmountAdapter.parse(durationString);
}
private void normalise() {
// ensure the end timezone is the same as the start..
if (getStart().isUtc()) {
getEnd().setUtc(true);
}
else {
getEnd().setTimeZone(getStart().getTimeZone());
}
}
/**
* 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 TemporalAmount getDuration() {
if (duration == null) {
return TemporalAmountAdapter.fromDateRange(getStart(), getEnd()).getDuration();
}
return duration.getDuration();
}
/**
* 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() {
return (DateTime) getRangeEnd();
}
/**
* @return Returns the start.
*/
public final DateTime getStart() {
return (DateTime) getRangeStart();
}
/**
* @param date a date to test for inclusion
* @param inclusive indicates if the start and end of the period are included in the test
* @return true if the specified date occurs within the current period
* @deprecated use {@link Period#includes(Date, int)} instead.
*/
@Deprecated
public final boolean includes(final Date date, final boolean inclusive) {
if (inclusive) {
return includes(date, INCLUSIVE_START | INCLUSIVE_END);
}
else {
return includes(date, 0);
}
}
/**
* 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;
DateTime newPeriodEnd;
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);
}
/**
* Creates a set of periods resulting from the subtraction of the specified
* period from this one. If the specified period is completely contained
* in this period, the resulting list will contain two periods. Otherwise
* it will contain one. If the specified period does not interest this period
* a list containing this period is returned. If this period is completely
* contained within the specified period an empty period list is returned.
* @param period a period to subtract from this one
* @return a list containing zero, one or two periods.
*/
public final PeriodList subtract(final Period period) {
final PeriodList result = new PeriodList();
if (period.contains(this)) {
return result;
}
else if (!period.intersects(this)) {
result.add(this);
return result;
}
DateTime newPeriodStart;
DateTime newPeriodEnd;
if (!period.getStart().after(getStart())) {
newPeriodStart = period.getEnd();
newPeriodEnd = getEnd();
}
else if (!period.getEnd().before(getEnd())) {
newPeriodStart = getStart();
newPeriodEnd = period.getStart();
}
else {
// subtraction consumed by this period..
// initialise and add head period..
newPeriodStart = getStart();
newPeriodEnd = period.getStart();
result.add(new Period(newPeriodStart, newPeriodEnd));
// initialise tail period..
newPeriodStart = period.getEnd();
newPeriodEnd = getEnd();
}
result.add(new Period(newPeriodStart, newPeriodEnd));
return result;
}
/**
* An empty period is one that consumes no time.
* @return true if this period consumes no time, otherwise false
*/
public final boolean isEmpty() {
return getStart().equals(getEnd());
}
/**
* Updates the start and (possible) end times of this period to reflect
* the specified UTC timezone status.
* @param utc indicates whether the period is in UTC time
*/
public void setUtc(final boolean utc) {
getStart().setUtc(utc);
getEnd().setUtc(utc);
}
/**
* Updates the start and (possible) end times of this period to reflect
* the specified timezone status.
* @param timezone a timezone for the period
*/
public final void setTimeZone(final TimeZone timezone) {
getStart().setUtc(false);
getStart().setTimeZone(timezone);
getEnd().setUtc(false);
getEnd().setTimeZone(timezone);
}
/**
* {@inheritDoc}
*/
@Override
public final String toString() {
final StringBuilder b = new StringBuilder();
b.append(getStart());
b.append('/');
if (duration == null) {
b.append(getEnd());
}
else {
// b.append(DurationFormat.getInstance().format(duration));
b.append(duration);
}
return b.toString();
}
/**
* Compares the specified period with this period.
* First, compare the start dates. If they are the same, compare the end dates.
*
* @param arg0 a period to compare with this one
* @return a postive value if this period is greater, negative if the other is
* greater, or zero if they are equal
*/
@Override
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");
}
final int startCompare = getStart().compareTo(arg0.getStart());
if (startCompare != 0) {
return startCompare;
}
// start dates are equal, compare end dates..
else if (duration == null) {
final int endCompare = getEnd().compareTo(arg0.getEnd());
if (endCompare != 0) {
return endCompare;
}
}
// ..or durations
return new TemporalAmountComparator().compare(getDuration(), arg0.getDuration());
}
/**
* {@inheritDoc}
*/
@Override
public final boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Period)) {
return false;
}
final Period period = (Period) o;
return new EqualsBuilder().append(getStart(), period.getStart())
.append((duration == null) ? getEnd() : duration,
(period.duration == null) ? period.getEnd() : period.duration).isEquals();
}
/**
* {@inheritDoc}
*/
@Override
public final int hashCode() {
return new HashCodeBuilder().append(getStart())
.append((duration == null) ? getEnd() : duration)
.toHashCode();
}
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
}