diff -r 5ae3e5665a0b -r cc93757aeca3 src/net/fortuna/ical4j/model/DateTime.java --- a/src/net/fortuna/ical4j/model/DateTime.java Thu Feb 12 18:02:00 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,526 +0,0 @@ -/** - * 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.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Map; -import java.util.WeakHashMap; - -import net.fortuna.ical4j.util.CompatibilityHints; -import net.fortuna.ical4j.util.Dates; -import net.fortuna.ical4j.util.TimeZones; - -import org.apache.commons.lang3.builder.EqualsBuilder; - -/** - * $Id$ - * - * Created on 26/06/2005 - * - * Represents a time of day on a specific date. - * - *
- * 4.3.5   Date-Time
- * 
- *    Value Name: DATE-TIME
- * 
- *    Purpose: This value type is used to identify values that specify a
- *    precise calendar date and time of day.
- * 
- *    Formal Definition: The value type is defined by the following
- *    notation:
- * 
- *      date-time  = date "T" time ;As specified in the date and time
- *                                 ;value definitions
- * 
- *    Description: If the property permits, multiple "date-time" values are
- *    specified as a COMMA character (US-ASCII decimal 44) separated list
- *    of values. No additional content value encoding (i.e., BACKSLASH
- *    character encoding) is defined for this value type.
- * 
- *    The "DATE-TIME" data type is used to identify values that contain a
- *    precise calendar date and time of day. The format is based on the
- *    [ISO 8601] complete representation, basic format for a calendar date
- *    and time of day. The text format is a concatenation of the "date",
- *    followed by the LATIN CAPITAL LETTER T character (US-ASCII decimal
- *    84) time designator, followed by the "time" format.
- * 
- *    The "DATE-TIME" data type expresses time values in three forms:
- * 
- *    The form of date and time with UTC offset MUST NOT be used. For
- *    example, the following is not valid for a date-time value:
- * 
- *      DTSTART:19980119T230000-0800       ;Invalid time format
- * 
- *    FORM #1: DATE WITH LOCAL TIME
- * 
- *    The date with local time form is simply a date-time value that does
- *    not contain the UTC designator nor does it reference a time zone. For
- *    example, the following represents Janurary 18, 1998, at 11 PM:
- * 
- *      DTSTART:19980118T230000
- * 
- *    Date-time values of this type are said to be "floating" and are not
- *    bound to any time zone in particular. They are used to represent the
- *    same hour, minute, and second value regardless of which time zone is
- *    currently being observed. For example, an event can be defined that
- *    indicates that an individual will be busy from 11:00 AM to 1:00 PM
- *    every day, no matter which time zone the person is in. In these
- *    cases, a local time can be specified. The recipient of an iCalendar
- *    object with a property value consisting of a local time, without any
- *    relative time zone information, SHOULD interpret the value as being
- *    fixed to whatever time zone the ATTENDEE is in at any given moment.
- *    This means that two ATTENDEEs, in different time zones, receiving the
- *    same event definition as a floating time, may be participating in the
- *    event at different actual times. Floating time SHOULD only be used
- *    where that is the reasonable behavior.
- * 
- *    In most cases, a fixed time is desired. To properly communicate a
- *    fixed time in a property value, either UTC time or local time with
- *    time zone reference MUST be specified.
- * 
- *    The use of local time in a DATE-TIME value without the TZID property
- *    parameter is to be interpreted as floating time, regardless of the
- *    existence of "VTIMEZONE" calendar components in the iCalendar object.
- * 
- *    FORM #2: DATE WITH UTC TIME
- * 
- *    The date with UTC time, or absolute time, is identified by a LATIN
- *    CAPITAL LETTER Z suffix character (US-ASCII decimal 90), the UTC
- *    designator, appended to the time value. For example, the following
- *    represents January 19, 1998, at 0700 UTC:
- * 
- *      DTSTART:19980119T070000Z
- * 
- *    The TZID property parameter MUST NOT be applied to DATE-TIME
- *    properties whose time values are specified in UTC.
- * 
- *    FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE
- * 
- *    The date and local time with reference to time zone information is
- *    identified by the use the TZID property parameter to reference the
- *    appropriate time zone definition. TZID is discussed in detail in the
- *    section on Time Zone. For example, the following represents 2 AM in
- *    New York on Janurary 19, 1998:
- * 
- *           DTSTART;TZID=US-Eastern:19980119T020000
- * 
- *    Example: The following represents July 14, 1997, at 1:30 PM in New
- *    York City in each of the three time formats, using the "DTSTART"
- *    property.
- * 
- *      DTSTART:19970714T133000            ;Local time
- *      DTSTART:19970714T173000Z           ;UTC time
- *      DTSTART;TZID=US-Eastern:19970714T133000    ;Local time and time
- *                         ; zone reference
- * 
- *    A time value MUST ONLY specify 60 seconds when specifying the
- *    periodic "leap second" in the time value. For example:
- * 
- *      COMPLETED:19970630T235960Z
- * 
- * - * @author Ben Fortuna - */ -public class DateTime extends Date { - - private static final long serialVersionUID = -6407231357919440387L; - - private static final String DEFAULT_PATTERN = "yyyyMMdd'T'HHmmss"; - - private static final String UTC_PATTERN = "yyyyMMdd'T'HHmmss'Z'"; - - private static final String VCARD_PATTERN = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"; - - private static final String RELAXED_PATTERN = "yyyyMMdd"; - - /** - * Used for parsing times in a UTC date-time representation. - */ - private static final DateFormatCache UTC_FORMAT; - static { - final DateFormat format = new SimpleDateFormat(UTC_PATTERN); - format.setTimeZone(TimeZones.getUtcTimeZone()); - format.setLenient(false); - - UTC_FORMAT = new DateFormatCache(format); - } - - /** - * Used for parsing times in a local date-time representation. - */ - private static final DateFormatCache DEFAULT_FORMAT; - static { - final DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN); - format.setLenient(false); - DEFAULT_FORMAT = new DateFormatCache(format); - } - - private static final DateFormatCache LENIENT_DEFAULT_FORMAT; - static { - final DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN); - LENIENT_DEFAULT_FORMAT = new DateFormatCache(format); - } - - private static final DateFormatCache RELAXED_FORMAT; - static { - final DateFormat format = new SimpleDateFormat(RELAXED_PATTERN); - format.setLenient(true); - RELAXED_FORMAT = new DateFormatCache(format); - } - - private static final DateFormatCache VCARD_FORMAT; - static { - final DateFormat format = new SimpleDateFormat(VCARD_PATTERN); - VCARD_FORMAT = new DateFormatCache(format); - } - - private Time time; - - private TimeZone timezone; - - /** - * Default constructor. - */ - public DateTime() { - super(Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); - this.time = new Time(getTime(), getFormat().getTimeZone()); - } - - /** - * @param utc - * indicates if the date is in UTC time - */ - public DateTime(final boolean utc) { - this(); - setUtc(utc); - } - - /** - * @param time - * a date-time value in milliseconds - */ - public DateTime(final long time) { - super(time, Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); - this.time = new Time(time, getFormat().getTimeZone()); - } - - /** - * @param date - * a date-time value - */ - public DateTime(final java.util.Date date) { - super(date.getTime(), Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); - this.time = new Time(date.getTime(), getFormat().getTimeZone()); - // copy timezone information if applicable.. - if (date instanceof DateTime) { - final DateTime dateTime = (DateTime) date; - if (dateTime.isUtc()) { - setUtc(true); - } else { - setTimeZone(dateTime.getTimeZone()); - } - } - } - - /** - * Constructs a new DateTime instance from parsing the specified string - * representation in the default (local) timezone. - * - * @param value - * a string representation of a date-time - * @throws ParseException - * where the specified string is not a valid date-time - */ - public DateTime(final String value) throws ParseException { - this(value, null); - /* - * long time = 0; try { synchronized (UTC_FORMAT) { time = - * UTC_FORMAT.parse(value).getTime(); } setUtc(true); } catch - * (ParseException pe) { synchronized (DEFAULT_FORMAT) { - * DEFAULT_FORMAT.setTimeZone(getFormat().getTimeZone()); time = - * DEFAULT_FORMAT.parse(value).getTime(); } this.time = new Time(time, - * getFormat().getTimeZone()); } setTime(time); - */ - } - - /** - * Creates a new date-time instance from the specified value in the given - * timezone. If a timezone is not specified, the default timezone (as - * returned by {@link java.util.TimeZone#getDefault()}) is used. - * - * @param value - * a string representation of a date-time - * @param timezone - * the timezone for the date-time instance - * @throws ParseException - * where the specified string is not a valid date-time - */ - public DateTime(final String value, final TimeZone timezone) - throws ParseException { - // setting the time to 0 since we are going to reset it anyway - super(0, Dates.PRECISION_SECOND, timezone != null ? timezone - : java.util.TimeZone.getDefault()); - this.time = new Time(getTime(), getFormat().getTimeZone()); - - try { - if (value.endsWith("Z")) { - setTime(value, (DateFormat) UTC_FORMAT.get(), null); - setUtc(true); - } else { - if (timezone != null) { - setTime(value, (DateFormat) DEFAULT_FORMAT.get(), timezone); - } else { - // Use lenient parsing for floating times. This is to - // overcome - // the problem of parsing VTimeZone dates that specify dates - // that the strict parser does not accept. - setTime(value, (DateFormat) LENIENT_DEFAULT_FORMAT.get(), - getFormat().getTimeZone()); - } - setTimeZone(timezone); - } - } catch (ParseException pe) { - if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_VCARD_COMPATIBILITY)) { - - try { - setTime(value, (DateFormat) VCARD_FORMAT.get(), timezone); - setTimeZone(timezone); - } catch (ParseException pe2) { - if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { - setTime(value, (DateFormat) RELAXED_FORMAT.get(), timezone); - setTimeZone(timezone); - } - } - } else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { - setTime(value, (DateFormat) RELAXED_FORMAT.get(), timezone); - setTimeZone(timezone); - } else { - throw pe; - } - } - } - - /** - * @param value - * a string representation of a date-time - * @param pattern - * a pattern to apply when parsing the date-time value - * @param timezone - * the timezone for the date-time instance - * @throws ParseException - * where the specified string is not a valid date-time - */ - public DateTime(String value, String pattern, TimeZone timezone) - throws ParseException { - // setting the time to 0 since we are going to reset it anyway - super(0, Dates.PRECISION_SECOND, timezone != null ? timezone - : java.util.TimeZone.getDefault()); - this.time = new Time(getTime(), getFormat().getTimeZone()); - - final DateFormat format = CalendarDateFormatFactory - .getInstance(pattern); - setTime(value, format, timezone); - } - - /** - * @param value - * a string representation of a date-time - * @param pattern - * a pattern to apply when parsing the date-time value - * @param utc - * indicates whether the date-time is in UTC time - * @throws ParseException - * where the specified string is not a valid date-time - */ - public DateTime(String value, String pattern, boolean utc) - throws ParseException { - // setting the time to 0 since we are going to reset it anyway - this(0); - final DateFormat format = CalendarDateFormatFactory - .getInstance(pattern); - if (utc) { - setTime(value, format, - ((DateFormat) UTC_FORMAT.get()).getTimeZone()); - } else { - setTime(value, format, null); - } - setUtc(utc); - } - - /** - * Internal set of time by parsing value string. - * - * @param value - * @param format - * a {@code DateFormat}, protected by the use of a ThreadLocal. - * @param tz - * @throws ParseException - */ - private void setTime(final String value, final DateFormat format, - final java.util.TimeZone tz) throws ParseException { - - if (tz != null) { - format.setTimeZone(tz); - } - setTime(format.parse(value).getTime()); - } - - /** - * {@inheritDoc} - */ - public final void setTime(final long time) { - super.setTime(time); - // need to check for null time due to Android java.util.Date(long) - // constructor - // calling this method.. - if (this.time != null) { - this.time.setTime(time); - } - } - - /** - * @return Returns the utc. - */ - public final boolean isUtc() { - return time.isUtc(); - } - - /** - * Updates this date-time to display in UTC time if the argument is true. - * Otherwise, resets to the default timezone. - * - * @param utc - * The utc to set. - */ - public final void setUtc(final boolean utc) { - // reset the timezone associated with this instance.. - this.timezone = null; - if (utc) { - getFormat().setTimeZone(TimeZones.getUtcTimeZone()); - } else { - resetTimeZone(); - } - time = new Time(time, getFormat().getTimeZone(), utc); - } - - /** - * Sets the timezone associated with this date-time instance. If the - * specified timezone is null, it will reset to the default timezone. If the - * date-time instance is utc, it will turn into either a floating (no - * timezone) date-time, or a date-time with a timezone. - * - * @param timezone - * a timezone to apply to the instance - */ - public final void setTimeZone(final TimeZone timezone) { - this.timezone = timezone; - if (timezone != null) { - getFormat().setTimeZone(timezone); - } else { - resetTimeZone(); - } - time = new Time(time, getFormat().getTimeZone(), false); - } - - /** - * Reset the timezone to default. - */ - private void resetTimeZone() { - // use GMT timezone to avoid daylight savings rules affecting floating - // time values.. - getFormat().setTimeZone(TimeZone.getDefault()); - // getFormat().setTimeZone(TimeZone.getTimeZone(TimeZones.GMT_ID)); - } - - /** - * Returns the current timezone associated with this date-time value. - * - * @return a Java timezone - */ - public final TimeZone getTimeZone() { - return timezone; - } - - /** - * {@inheritDoc} - */ - public final String toString() { - final StringBuffer b = new StringBuffer(super.toString()); - b.append('T'); - b.append(time.toString()); - return b.toString(); - } - - /** - * {@inheritDoc} - */ - public boolean equals(final Object arg0) { - // TODO: what about compareTo, before, after, etc.? - - if (arg0 instanceof DateTime) { - return new EqualsBuilder().append(time, ((DateTime) arg0).time) - .isEquals(); - } - return super.equals(arg0); - } - - /** - * {@inheritDoc} - */ - public int hashCode() { - return super.hashCode(); - } - - private static class DateFormatCache { - - private final Map threadMap = new WeakHashMap(); - - private final DateFormat templateFormat; - - private DateFormatCache(DateFormat dateFormat) { - this.templateFormat = dateFormat; - } - - public DateFormat get() { - DateFormat dateFormat = (DateFormat) threadMap.get(Thread - .currentThread()); - if (dateFormat == null) { - dateFormat = (DateFormat) templateFormat.clone(); - threadMap.put(Thread.currentThread(), dateFormat); - } - return dateFormat; - } - } -}