1.1 --- a/src/net/fortuna/ical4j/model/DateTime.java Thu Feb 12 18:02:00 2015 +0100 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,526 +0,0 @@ 1.4 -/** 1.5 - * Copyright (c) 2012, Ben Fortuna 1.6 - * All rights reserved. 1.7 - * 1.8 - * Redistribution and use in source and binary forms, with or without 1.9 - * modification, are permitted provided that the following conditions 1.10 - * are met: 1.11 - * 1.12 - * o Redistributions of source code must retain the above copyright 1.13 - * notice, this list of conditions and the following disclaimer. 1.14 - * 1.15 - * o Redistributions in binary form must reproduce the above copyright 1.16 - * notice, this list of conditions and the following disclaimer in the 1.17 - * documentation and/or other materials provided with the distribution. 1.18 - * 1.19 - * o Neither the name of Ben Fortuna nor the names of any other contributors 1.20 - * may be used to endorse or promote products derived from this software 1.21 - * without specific prior written permission. 1.22 - * 1.23 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.24 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.25 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.26 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 1.27 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 1.28 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 1.29 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 1.30 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 1.31 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 1.32 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.33 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.34 - */ 1.35 -package net.fortuna.ical4j.model; 1.36 - 1.37 -import java.text.DateFormat; 1.38 -import java.text.ParseException; 1.39 -import java.text.SimpleDateFormat; 1.40 -import java.util.Map; 1.41 -import java.util.WeakHashMap; 1.42 - 1.43 -import net.fortuna.ical4j.util.CompatibilityHints; 1.44 -import net.fortuna.ical4j.util.Dates; 1.45 -import net.fortuna.ical4j.util.TimeZones; 1.46 - 1.47 -import org.apache.commons.lang3.builder.EqualsBuilder; 1.48 - 1.49 -/** 1.50 - * $Id$ 1.51 - * 1.52 - * Created on 26/06/2005 1.53 - * 1.54 - * Represents a time of day on a specific date. 1.55 - * 1.56 - * <pre> 1.57 - * 4.3.5 Date-Time 1.58 - * 1.59 - * Value Name: DATE-TIME 1.60 - * 1.61 - * Purpose: This value type is used to identify values that specify a 1.62 - * precise calendar date and time of day. 1.63 - * 1.64 - * Formal Definition: The value type is defined by the following 1.65 - * notation: 1.66 - * 1.67 - * date-time = date "T" time ;As specified in the date and time 1.68 - * ;value definitions 1.69 - * 1.70 - * Description: If the property permits, multiple "date-time" values are 1.71 - * specified as a COMMA character (US-ASCII decimal 44) separated list 1.72 - * of values. No additional content value encoding (i.e., BACKSLASH 1.73 - * character encoding) is defined for this value type. 1.74 - * 1.75 - * The "DATE-TIME" data type is used to identify values that contain a 1.76 - * precise calendar date and time of day. The format is based on the 1.77 - * [ISO 8601] complete representation, basic format for a calendar date 1.78 - * and time of day. The text format is a concatenation of the "date", 1.79 - * followed by the LATIN CAPITAL LETTER T character (US-ASCII decimal 1.80 - * 84) time designator, followed by the "time" format. 1.81 - * 1.82 - * The "DATE-TIME" data type expresses time values in three forms: 1.83 - * 1.84 - * The form of date and time with UTC offset MUST NOT be used. For 1.85 - * example, the following is not valid for a date-time value: 1.86 - * 1.87 - * DTSTART:19980119T230000-0800 ;Invalid time format 1.88 - * 1.89 - * FORM #1: DATE WITH LOCAL TIME 1.90 - * 1.91 - * The date with local time form is simply a date-time value that does 1.92 - * not contain the UTC designator nor does it reference a time zone. For 1.93 - * example, the following represents Janurary 18, 1998, at 11 PM: 1.94 - * 1.95 - * DTSTART:19980118T230000 1.96 - * 1.97 - * Date-time values of this type are said to be "floating" and are not 1.98 - * bound to any time zone in particular. They are used to represent the 1.99 - * same hour, minute, and second value regardless of which time zone is 1.100 - * currently being observed. For example, an event can be defined that 1.101 - * indicates that an individual will be busy from 11:00 AM to 1:00 PM 1.102 - * every day, no matter which time zone the person is in. In these 1.103 - * cases, a local time can be specified. The recipient of an iCalendar 1.104 - * object with a property value consisting of a local time, without any 1.105 - * relative time zone information, SHOULD interpret the value as being 1.106 - * fixed to whatever time zone the ATTENDEE is in at any given moment. 1.107 - * This means that two ATTENDEEs, in different time zones, receiving the 1.108 - * same event definition as a floating time, may be participating in the 1.109 - * event at different actual times. Floating time SHOULD only be used 1.110 - * where that is the reasonable behavior. 1.111 - * 1.112 - * In most cases, a fixed time is desired. To properly communicate a 1.113 - * fixed time in a property value, either UTC time or local time with 1.114 - * time zone reference MUST be specified. 1.115 - * 1.116 - * The use of local time in a DATE-TIME value without the TZID property 1.117 - * parameter is to be interpreted as floating time, regardless of the 1.118 - * existence of "VTIMEZONE" calendar components in the iCalendar object. 1.119 - * 1.120 - * FORM #2: DATE WITH UTC TIME 1.121 - * 1.122 - * The date with UTC time, or absolute time, is identified by a LATIN 1.123 - * CAPITAL LETTER Z suffix character (US-ASCII decimal 90), the UTC 1.124 - * designator, appended to the time value. For example, the following 1.125 - * represents January 19, 1998, at 0700 UTC: 1.126 - * 1.127 - * DTSTART:19980119T070000Z 1.128 - * 1.129 - * The TZID property parameter MUST NOT be applied to DATE-TIME 1.130 - * properties whose time values are specified in UTC. 1.131 - * 1.132 - * FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE 1.133 - * 1.134 - * The date and local time with reference to time zone information is 1.135 - * identified by the use the TZID property parameter to reference the 1.136 - * appropriate time zone definition. TZID is discussed in detail in the 1.137 - * section on Time Zone. For example, the following represents 2 AM in 1.138 - * New York on Janurary 19, 1998: 1.139 - * 1.140 - * DTSTART;TZID=US-Eastern:19980119T020000 1.141 - * 1.142 - * Example: The following represents July 14, 1997, at 1:30 PM in New 1.143 - * York City in each of the three time formats, using the "DTSTART" 1.144 - * property. 1.145 - * 1.146 - * DTSTART:19970714T133000 ;Local time 1.147 - * DTSTART:19970714T173000Z ;UTC time 1.148 - * DTSTART;TZID=US-Eastern:19970714T133000 ;Local time and time 1.149 - * ; zone reference 1.150 - * 1.151 - * A time value MUST ONLY specify 60 seconds when specifying the 1.152 - * periodic "leap second" in the time value. For example: 1.153 - * 1.154 - * COMPLETED:19970630T235960Z 1.155 - * </pre> 1.156 - * 1.157 - * @author Ben Fortuna 1.158 - */ 1.159 -public class DateTime extends Date { 1.160 - 1.161 - private static final long serialVersionUID = -6407231357919440387L; 1.162 - 1.163 - private static final String DEFAULT_PATTERN = "yyyyMMdd'T'HHmmss"; 1.164 - 1.165 - private static final String UTC_PATTERN = "yyyyMMdd'T'HHmmss'Z'"; 1.166 - 1.167 - private static final String VCARD_PATTERN = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"; 1.168 - 1.169 - private static final String RELAXED_PATTERN = "yyyyMMdd"; 1.170 - 1.171 - /** 1.172 - * Used for parsing times in a UTC date-time representation. 1.173 - */ 1.174 - private static final DateFormatCache UTC_FORMAT; 1.175 - static { 1.176 - final DateFormat format = new SimpleDateFormat(UTC_PATTERN); 1.177 - format.setTimeZone(TimeZones.getUtcTimeZone()); 1.178 - format.setLenient(false); 1.179 - 1.180 - UTC_FORMAT = new DateFormatCache(format); 1.181 - } 1.182 - 1.183 - /** 1.184 - * Used for parsing times in a local date-time representation. 1.185 - */ 1.186 - private static final DateFormatCache DEFAULT_FORMAT; 1.187 - static { 1.188 - final DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN); 1.189 - format.setLenient(false); 1.190 - DEFAULT_FORMAT = new DateFormatCache(format); 1.191 - } 1.192 - 1.193 - private static final DateFormatCache LENIENT_DEFAULT_FORMAT; 1.194 - static { 1.195 - final DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN); 1.196 - LENIENT_DEFAULT_FORMAT = new DateFormatCache(format); 1.197 - } 1.198 - 1.199 - private static final DateFormatCache RELAXED_FORMAT; 1.200 - static { 1.201 - final DateFormat format = new SimpleDateFormat(RELAXED_PATTERN); 1.202 - format.setLenient(true); 1.203 - RELAXED_FORMAT = new DateFormatCache(format); 1.204 - } 1.205 - 1.206 - private static final DateFormatCache VCARD_FORMAT; 1.207 - static { 1.208 - final DateFormat format = new SimpleDateFormat(VCARD_PATTERN); 1.209 - VCARD_FORMAT = new DateFormatCache(format); 1.210 - } 1.211 - 1.212 - private Time time; 1.213 - 1.214 - private TimeZone timezone; 1.215 - 1.216 - /** 1.217 - * Default constructor. 1.218 - */ 1.219 - public DateTime() { 1.220 - super(Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); 1.221 - this.time = new Time(getTime(), getFormat().getTimeZone()); 1.222 - } 1.223 - 1.224 - /** 1.225 - * @param utc 1.226 - * indicates if the date is in UTC time 1.227 - */ 1.228 - public DateTime(final boolean utc) { 1.229 - this(); 1.230 - setUtc(utc); 1.231 - } 1.232 - 1.233 - /** 1.234 - * @param time 1.235 - * a date-time value in milliseconds 1.236 - */ 1.237 - public DateTime(final long time) { 1.238 - super(time, Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); 1.239 - this.time = new Time(time, getFormat().getTimeZone()); 1.240 - } 1.241 - 1.242 - /** 1.243 - * @param date 1.244 - * a date-time value 1.245 - */ 1.246 - public DateTime(final java.util.Date date) { 1.247 - super(date.getTime(), Dates.PRECISION_SECOND, java.util.TimeZone.getDefault()); 1.248 - this.time = new Time(date.getTime(), getFormat().getTimeZone()); 1.249 - // copy timezone information if applicable.. 1.250 - if (date instanceof DateTime) { 1.251 - final DateTime dateTime = (DateTime) date; 1.252 - if (dateTime.isUtc()) { 1.253 - setUtc(true); 1.254 - } else { 1.255 - setTimeZone(dateTime.getTimeZone()); 1.256 - } 1.257 - } 1.258 - } 1.259 - 1.260 - /** 1.261 - * Constructs a new DateTime instance from parsing the specified string 1.262 - * representation in the default (local) timezone. 1.263 - * 1.264 - * @param value 1.265 - * a string representation of a date-time 1.266 - * @throws ParseException 1.267 - * where the specified string is not a valid date-time 1.268 - */ 1.269 - public DateTime(final String value) throws ParseException { 1.270 - this(value, null); 1.271 - /* 1.272 - * long time = 0; try { synchronized (UTC_FORMAT) { time = 1.273 - * UTC_FORMAT.parse(value).getTime(); } setUtc(true); } catch 1.274 - * (ParseException pe) { synchronized (DEFAULT_FORMAT) { 1.275 - * DEFAULT_FORMAT.setTimeZone(getFormat().getTimeZone()); time = 1.276 - * DEFAULT_FORMAT.parse(value).getTime(); } this.time = new Time(time, 1.277 - * getFormat().getTimeZone()); } setTime(time); 1.278 - */ 1.279 - } 1.280 - 1.281 - /** 1.282 - * Creates a new date-time instance from the specified value in the given 1.283 - * timezone. If a timezone is not specified, the default timezone (as 1.284 - * returned by {@link java.util.TimeZone#getDefault()}) is used. 1.285 - * 1.286 - * @param value 1.287 - * a string representation of a date-time 1.288 - * @param timezone 1.289 - * the timezone for the date-time instance 1.290 - * @throws ParseException 1.291 - * where the specified string is not a valid date-time 1.292 - */ 1.293 - public DateTime(final String value, final TimeZone timezone) 1.294 - throws ParseException { 1.295 - // setting the time to 0 since we are going to reset it anyway 1.296 - super(0, Dates.PRECISION_SECOND, timezone != null ? timezone 1.297 - : java.util.TimeZone.getDefault()); 1.298 - this.time = new Time(getTime(), getFormat().getTimeZone()); 1.299 - 1.300 - try { 1.301 - if (value.endsWith("Z")) { 1.302 - setTime(value, (DateFormat) UTC_FORMAT.get(), null); 1.303 - setUtc(true); 1.304 - } else { 1.305 - if (timezone != null) { 1.306 - setTime(value, (DateFormat) DEFAULT_FORMAT.get(), timezone); 1.307 - } else { 1.308 - // Use lenient parsing for floating times. This is to 1.309 - // overcome 1.310 - // the problem of parsing VTimeZone dates that specify dates 1.311 - // that the strict parser does not accept. 1.312 - setTime(value, (DateFormat) LENIENT_DEFAULT_FORMAT.get(), 1.313 - getFormat().getTimeZone()); 1.314 - } 1.315 - setTimeZone(timezone); 1.316 - } 1.317 - } catch (ParseException pe) { 1.318 - if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_VCARD_COMPATIBILITY)) { 1.319 - 1.320 - try { 1.321 - setTime(value, (DateFormat) VCARD_FORMAT.get(), timezone); 1.322 - setTimeZone(timezone); 1.323 - } catch (ParseException pe2) { 1.324 - if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { 1.325 - setTime(value, (DateFormat) RELAXED_FORMAT.get(), timezone); 1.326 - setTimeZone(timezone); 1.327 - } 1.328 - } 1.329 - } else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { 1.330 - setTime(value, (DateFormat) RELAXED_FORMAT.get(), timezone); 1.331 - setTimeZone(timezone); 1.332 - } else { 1.333 - throw pe; 1.334 - } 1.335 - } 1.336 - } 1.337 - 1.338 - /** 1.339 - * @param value 1.340 - * a string representation of a date-time 1.341 - * @param pattern 1.342 - * a pattern to apply when parsing the date-time value 1.343 - * @param timezone 1.344 - * the timezone for the date-time instance 1.345 - * @throws ParseException 1.346 - * where the specified string is not a valid date-time 1.347 - */ 1.348 - public DateTime(String value, String pattern, TimeZone timezone) 1.349 - throws ParseException { 1.350 - // setting the time to 0 since we are going to reset it anyway 1.351 - super(0, Dates.PRECISION_SECOND, timezone != null ? timezone 1.352 - : java.util.TimeZone.getDefault()); 1.353 - this.time = new Time(getTime(), getFormat().getTimeZone()); 1.354 - 1.355 - final DateFormat format = CalendarDateFormatFactory 1.356 - .getInstance(pattern); 1.357 - setTime(value, format, timezone); 1.358 - } 1.359 - 1.360 - /** 1.361 - * @param value 1.362 - * a string representation of a date-time 1.363 - * @param pattern 1.364 - * a pattern to apply when parsing the date-time value 1.365 - * @param utc 1.366 - * indicates whether the date-time is in UTC time 1.367 - * @throws ParseException 1.368 - * where the specified string is not a valid date-time 1.369 - */ 1.370 - public DateTime(String value, String pattern, boolean utc) 1.371 - throws ParseException { 1.372 - // setting the time to 0 since we are going to reset it anyway 1.373 - this(0); 1.374 - final DateFormat format = CalendarDateFormatFactory 1.375 - .getInstance(pattern); 1.376 - if (utc) { 1.377 - setTime(value, format, 1.378 - ((DateFormat) UTC_FORMAT.get()).getTimeZone()); 1.379 - } else { 1.380 - setTime(value, format, null); 1.381 - } 1.382 - setUtc(utc); 1.383 - } 1.384 - 1.385 - /** 1.386 - * Internal set of time by parsing value string. 1.387 - * 1.388 - * @param value 1.389 - * @param format 1.390 - * a {@code DateFormat}, protected by the use of a ThreadLocal. 1.391 - * @param tz 1.392 - * @throws ParseException 1.393 - */ 1.394 - private void setTime(final String value, final DateFormat format, 1.395 - final java.util.TimeZone tz) throws ParseException { 1.396 - 1.397 - if (tz != null) { 1.398 - format.setTimeZone(tz); 1.399 - } 1.400 - setTime(format.parse(value).getTime()); 1.401 - } 1.402 - 1.403 - /** 1.404 - * {@inheritDoc} 1.405 - */ 1.406 - public final void setTime(final long time) { 1.407 - super.setTime(time); 1.408 - // need to check for null time due to Android java.util.Date(long) 1.409 - // constructor 1.410 - // calling this method.. 1.411 - if (this.time != null) { 1.412 - this.time.setTime(time); 1.413 - } 1.414 - } 1.415 - 1.416 - /** 1.417 - * @return Returns the utc. 1.418 - */ 1.419 - public final boolean isUtc() { 1.420 - return time.isUtc(); 1.421 - } 1.422 - 1.423 - /** 1.424 - * Updates this date-time to display in UTC time if the argument is true. 1.425 - * Otherwise, resets to the default timezone. 1.426 - * 1.427 - * @param utc 1.428 - * The utc to set. 1.429 - */ 1.430 - public final void setUtc(final boolean utc) { 1.431 - // reset the timezone associated with this instance.. 1.432 - this.timezone = null; 1.433 - if (utc) { 1.434 - getFormat().setTimeZone(TimeZones.getUtcTimeZone()); 1.435 - } else { 1.436 - resetTimeZone(); 1.437 - } 1.438 - time = new Time(time, getFormat().getTimeZone(), utc); 1.439 - } 1.440 - 1.441 - /** 1.442 - * Sets the timezone associated with this date-time instance. If the 1.443 - * specified timezone is null, it will reset to the default timezone. If the 1.444 - * date-time instance is utc, it will turn into either a floating (no 1.445 - * timezone) date-time, or a date-time with a timezone. 1.446 - * 1.447 - * @param timezone 1.448 - * a timezone to apply to the instance 1.449 - */ 1.450 - public final void setTimeZone(final TimeZone timezone) { 1.451 - this.timezone = timezone; 1.452 - if (timezone != null) { 1.453 - getFormat().setTimeZone(timezone); 1.454 - } else { 1.455 - resetTimeZone(); 1.456 - } 1.457 - time = new Time(time, getFormat().getTimeZone(), false); 1.458 - } 1.459 - 1.460 - /** 1.461 - * Reset the timezone to default. 1.462 - */ 1.463 - private void resetTimeZone() { 1.464 - // use GMT timezone to avoid daylight savings rules affecting floating 1.465 - // time values.. 1.466 - getFormat().setTimeZone(TimeZone.getDefault()); 1.467 - // getFormat().setTimeZone(TimeZone.getTimeZone(TimeZones.GMT_ID)); 1.468 - } 1.469 - 1.470 - /** 1.471 - * Returns the current timezone associated with this date-time value. 1.472 - * 1.473 - * @return a Java timezone 1.474 - */ 1.475 - public final TimeZone getTimeZone() { 1.476 - return timezone; 1.477 - } 1.478 - 1.479 - /** 1.480 - * {@inheritDoc} 1.481 - */ 1.482 - public final String toString() { 1.483 - final StringBuffer b = new StringBuffer(super.toString()); 1.484 - b.append('T'); 1.485 - b.append(time.toString()); 1.486 - return b.toString(); 1.487 - } 1.488 - 1.489 - /** 1.490 - * {@inheritDoc} 1.491 - */ 1.492 - public boolean equals(final Object arg0) { 1.493 - // TODO: what about compareTo, before, after, etc.? 1.494 - 1.495 - if (arg0 instanceof DateTime) { 1.496 - return new EqualsBuilder().append(time, ((DateTime) arg0).time) 1.497 - .isEquals(); 1.498 - } 1.499 - return super.equals(arg0); 1.500 - } 1.501 - 1.502 - /** 1.503 - * {@inheritDoc} 1.504 - */ 1.505 - public int hashCode() { 1.506 - return super.hashCode(); 1.507 - } 1.508 - 1.509 - private static class DateFormatCache { 1.510 - 1.511 - private final Map threadMap = new WeakHashMap(); 1.512 - 1.513 - private final DateFormat templateFormat; 1.514 - 1.515 - private DateFormatCache(DateFormat dateFormat) { 1.516 - this.templateFormat = dateFormat; 1.517 - } 1.518 - 1.519 - public DateFormat get() { 1.520 - DateFormat dateFormat = (DateFormat) threadMap.get(Thread 1.521 - .currentThread()); 1.522 - if (dateFormat == null) { 1.523 - dateFormat = (DateFormat) templateFormat.clone(); 1.524 - threadMap.put(Thread.currentThread(), dateFormat); 1.525 - } 1.526 - return dateFormat; 1.527 - } 1.528 - } 1.529 -}