<NME> DateTime.java
<BEF> /**
 * 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 net.fortuna.ical4j.util.CompatibilityHints;
import net.fortuna.ical4j.util.Dates;
import net.fortuna.ical4j.util.TimeZones;
import org.apache.commons.lang3.builder.EqualsBuilder;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * A representation of the DATE-TIME object defined in RFC5445.
 * <p/>
 * <em>Extract from RFC5545:</em>
 * <p/>
 * <pre>
 * 3.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.
 *
 * Format Definition:  This 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-separated list of values.  No additional
 * content value encoding (i.e., BACKSLASH character encoding, see
 * Section 3.3.11) is defined for this value type.
 *
 * The "DATE-TIME" value type is used to identify values that contain
 * a precise calendar date and time of day.  The format is based on
 * the [ISO.8601.2004] 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,
 * the time designator, followed by the "time" format.
 *
 * The "DATE-TIME" value 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:
 *
 * 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 January 18, 1998, at
 * 11 PM:
 *
 * 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, the UTC designator, appended to
 * the time value.  For example, the following represents January 19,
 * 1998, at 0700 UTC:
 *
 * 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 Section 3.2.19.  For example, the following represents
 * 2:00 A.M. in New York on January 19, 1998:
 *
 * TZID=America/New_York:19980119T020000
 *
 * If, based on the definition of the referenced time zone, the local
 * time described occurs more than once (when changing from daylight
 * to standard time), the DATE-TIME value refers to the first
 * occurrence of the referenced time.  Thus, TZID=America/
 * New_York:20071104T013000 indicates November 4, 2007 at 1:30 A.M.
 * EDT (UTC-04:00).  If the local time described does not occur (when
 * changing from standard to daylight time), the DATE-TIME value is
 * interpreted using the UTC offset before the gap in local times.
 * Thus, TZID=America/New_York:20070311T023000 indicates March 11,
 * 2007 at 3:30 A.M. EDT (UTC-04:00), one hour after 1:30 A.M. EST
 * (UTC-05:00).
 *
 * A time value MUST only specify the second 60 when specifying a
 * positive leap second.  For example:
 *
 * 19970630T235960Z
 *
 * Implementations that do not support leap seconds SHOULD interpret
 * the second 60 as equivalent to the second 59.
 *
 * 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=America/New_York:19970714T133000
 * ; Local time and time
 * ; zone reference
 * </pre>
 * 
 * @author Ben Fortuna
 * @version 2.0
 */
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());
			}
		}
	}

	public DateTime(final java.util.Date date, TimeZone timeZone) {
		this(date);
		setTimeZone(timeZone);
	}

	/**
	 * 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 TimeZones#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 : TimeZones.getDefault());
		this.time = new Time(getTime(), getFormat().getTimeZone());

        try {
            if (value.endsWith("Z")) {
                setTime(value, UTC_FORMAT.get(), null);
                setUtc(true);
            } else {
                if (timezone != null) {
                    setTime(value, 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, LENIENT_DEFAULT_FORMAT.get(),
                            getFormat().getTimeZone());
                }
                setTimeZone(timezone);
            }
        } catch (ParseException pe) {
            if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_VCARD_COMPATIBILITY)) {

            	try {
	                setTime(value, VCARD_FORMAT.get(), timezone);
	                setTimeZone(timezone);
            	} catch (ParseException pe2) {
                    if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
    	                setTime(value, RELAXED_FORMAT.get(), timezone);
    	                setTimeZone(timezone);
                    }            		
            	}
            } else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
                setTime(value, 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 : TimeZones.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,
					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}
	 */
	@Override
	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(TimeZones.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}
	 */
	@Override
	public final String toString() {
		return super.toString() + 'T' +
				time.toString();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	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}
	 */
	@Override
	public int hashCode() {
		return super.hashCode();
	}

			this.templateFormat = dateFormat;
		}

		public DateFormat get() {
			DateFormat dateFormat = threadMap.get(Thread.currentThread());
			if (dateFormat == null) {
				dateFormat = (DateFormat) templateFormat.clone();
	private static class DateFormatCache {

		/**
		 * This map needs to keep weak references (to avoid memory leaks - see r1.37)
		 * and be thread-safe (since it may be concurrently modified in get() below).
		 */
		private final Map<Thread, DateFormat> threadMap = Collections.synchronizedMap(new WeakHashMap<Thread, DateFormat>());

		private final DateFormat templateFormat;

		private DateFormatCache(DateFormat dateFormat) {
			this.templateFormat = dateFormat;
		}

		public DateFormat get() {
			DateFormat dateFormat = threadMap.get(Thread.currentThread());
			if (dateFormat == null) {
				dateFormat = (DateFormat) templateFormat.clone();
				threadMap.put(Thread.currentThread(), dateFormat);
			}
			return dateFormat;
		}
	}
}

<MSG> Access to WeakHashMap must be synchronized

...if a thread might be changing the map.
<DFF> @@ -529,7 +529,7 @@ public class DateTime extends Date {
 			this.templateFormat = dateFormat;
 		}
 
-		public DateFormat get() {
+		public synchronized DateFormat get() {
 			DateFormat dateFormat = threadMap.get(Thread.currentThread());
 			if (dateFormat == null) {
 				dateFormat = (DateFormat) templateFormat.clone();

{'commit': 'diff --git a/src/main/java/net/fortuna/ical4j/model/DateTime.java b/src/main/java/net/fortuna/ical4j/model/DateTime.java\nindex 15d8c591b..be4d41aec 100644\n--- a/src/main/java/net/fortuna/ical4j/model/DateTime.java\n+++ b/src/main/java/net/fortuna/ical4j/model/DateTime.java\n@@ -529,7 +529,7 @@ public class DateTime extends Date {\n \t\t\tthis.templateFormat = dateFormat;\n \t\t}\n \n-\t\tpublic DateFormat get() {\n+\t\tpublic synchronized DateFormat get() {\n \t\t\tDateFormat dateFormat = threadMap.get(Thread.currentThread());\n \t\t\tif (dateFormat == null) {\n \t\t\t\tdateFormat = (DateFormat) templateFormat.clone();\n', 'commit_subject': 'Access to WeakHashMap must be synchronized', 'repo_name': 'ical4j/ical4j', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1470163779', 'tz_offset': '-420', 'date': {'seconds': '1470163779'}}, 'commit_tree': '467b99f85b171195e7b558595f99acbc6c3d3ab7', 'commit_message': 'Access to WeakHashMap must be synchronized\n\n...if a thread might be changing the map.', 'language_name': 'Shell', 'license': 'bsd-3-clause', 'hunks': '@@ -529,7 +529,7 @@ public class DateTime extends Date {\n \t\t\tthis.templateFormat = dateFormat;\n \t\t}\n \n-\t\tpublic DateFormat get() {\n+\t\tpublic synchronized DateFormat get() {\n \t\t\tDateFormat dateFormat = threadMap.get(Thread.currentThread());\n \t\t\tif (dateFormat == null) {\n \t\t\t\tdateFormat = (DateFormat) templateFormat.clone();\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/src/main/java/net/fortuna/ical4j/model/DateTime.java', 'tgt_file': 'b/src/main/java/net/fortuna/ical4j/model/DateTime.java', 'file_extension': '.java', 'before_file': '/**\n * Copyright (c) 2012, Ben Fortuna\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n *  o Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n *\n *  o Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n *  o Neither the name of Ben Fortuna nor the names of any other contributors\n * may be used to endorse or promote products derived from this software\n * without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\npackage net.fortuna.ical4j.model;\n\nimport net.fortuna.ical4j.util.CompatibilityHints;\nimport net.fortuna.ical4j.util.Dates;\nimport net.fortuna.ical4j.util.TimeZones;\nimport org.apache.commons.lang3.builder.EqualsBuilder;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.WeakHashMap;\n\n/**\n * A representation of the DATE-TIME object defined in RFC5445.\n * <p/>\n * <em>Extract from RFC5545:</em>\n * <p/>\n * <pre>\n * 3.3.5.  Date-Time\n *\n * Value Name:  DATE-TIME\n *\n * Purpose:  This value type is used to identify values that specify a\n * precise calendar date and time of day.\n *\n * Format Definition:  This value type is defined by the following\n * notation:\n *\n * date-time  = date "T" time ;As specified in the DATE and TIME\n * ;value definitions\n *\n * Description:  If the property permits, multiple "DATE-TIME" values\n * are specified as a COMMA-separated list of values.  No additional\n * content value encoding (i.e., BACKSLASH character encoding, see\n * Section 3.3.11) is defined for this value type.\n *\n * The "DATE-TIME" value type is used to identify values that contain\n * a precise calendar date and time of day.  The format is based on\n * the [ISO.8601.2004] complete representation, basic format for a\n * calendar date and time of day.  The text format is a concatenation\n * of the "date", followed by the LATIN CAPITAL LETTER T character,\n * the time designator, followed by the "time" format.\n *\n * The "DATE-TIME" value type expresses time values in three forms:\n *\n * The form of date and time with UTC offset MUST NOT be used.  For\n * example, the following is not valid for a DATE-TIME value:\n *\n * 19980119T230000-0800       ;Invalid time format\n *\n * FORM #1: DATE WITH LOCAL TIME\n *\n * The date with local time form is simply a DATE-TIME value that\n * does not contain the UTC designator nor does it reference a time\n * zone.  For example, the following represents January 18, 1998, at\n * 11 PM:\n *\n * 19980118T230000\n *\n * DATE-TIME values of this type are said to be "floating" and are\n * not bound to any time zone in particular.  They are used to\n * represent the same hour, minute, and second value regardless of\n * which time zone is currently being observed.  For example, an\n * event can be defined that indicates that an individual will be\n * busy from 11:00 AM to 1:00 PM every day, no matter which time zone\n * the person is in.  In these cases, a local time can be specified.\n * The recipient of an iCalendar object with a property value\n * consisting of a local time, without any relative time zone\n * information, SHOULD interpret the value as being fixed to whatever\n * time zone the "ATTENDEE" is in at any given moment.  This means\n * that two "Attendees", in different time zones, receiving the same\n * event definition as a floating time, may be participating in the\n * event at different actual times.  Floating time SHOULD only be\n * used where that is the reasonable behavior.\n *\n * In most cases, a fixed time is desired.  To properly communicate a\n * fixed time in a property value, either UTC time or local time with\n * time zone reference MUST be specified.\n *\n * The use of local time in a DATE-TIME value without the "TZID"\n * property parameter is to be interpreted as floating time,\n * regardless of the existence of "VTIMEZONE" calendar components in\n * the iCalendar object.\n *\n * FORM #2: DATE WITH UTC TIME\n *\n * The date with UTC time, or absolute time, is identified by a LATIN\n * CAPITAL LETTER Z suffix character, the UTC designator, appended to\n * the time value.  For example, the following represents January 19,\n * 1998, at 0700 UTC:\n *\n * 19980119T070000Z\n *\n * The "TZID" property parameter MUST NOT be applied to DATE-TIME\n * properties whose time values are specified in UTC.\n *\n * FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE\n *\n * The date and local time with reference to time zone information is\n * identified by the use the "TZID" property parameter to reference\n * the appropriate time zone definition.  "TZID" is discussed in\n * detail in Section 3.2.19.  For example, the following represents\n * 2:00 A.M. in New York on January 19, 1998:\n *\n * TZID=America/New_York:19980119T020000\n *\n * If, based on the definition of the referenced time zone, the local\n * time described occurs more than once (when changing from daylight\n * to standard time), the DATE-TIME value refers to the first\n * occurrence of the referenced time.  Thus, TZID=America/\n * New_York:20071104T013000 indicates November 4, 2007 at 1:30 A.M.\n * EDT (UTC-04:00).  If the local time described does not occur (when\n * changing from standard to daylight time), the DATE-TIME value is\n * interpreted using the UTC offset before the gap in local times.\n * Thus, TZID=America/New_York:20070311T023000 indicates March 11,\n * 2007 at 3:30 A.M. EDT (UTC-04:00), one hour after 1:30 A.M. EST\n * (UTC-05:00).\n *\n * A time value MUST only specify the second 60 when specifying a\n * positive leap second.  For example:\n *\n * 19970630T235960Z\n *\n * Implementations that do not support leap seconds SHOULD interpret\n * the second 60 as equivalent to the second 59.\n *\n * Example:  The following represents July 14, 1997, at 1:30 PM in New\n * York City in each of the three time formats, using the "DTSTART"\n * property.\n *\n * DTSTART:19970714T133000                   ; Local time\n * DTSTART:19970714T173000Z                  ; UTC time\n * DTSTART;TZID=America/New_York:19970714T133000\n * ; Local time and time\n * ; zone reference\n * </pre>\n * \n * @author Ben Fortuna\n * @version 2.0\n */\npublic class DateTime extends Date {\n\n\tprivate static final long serialVersionUID = -6407231357919440387L;\n\n\tprivate static final String DEFAULT_PATTERN = "yyyyMMdd\'T\'HHmmss";\n\n\tprivate static final String UTC_PATTERN = "yyyyMMdd\'T\'HHmmss\'Z\'";\n\t\n\tprivate static final String VCARD_PATTERN = "yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss\'Z\'";\n\n\tprivate static final String RELAXED_PATTERN = "yyyyMMdd";\n\n\t/**\n\t * Used for parsing times in a UTC date-time representation.\n\t */\n\tprivate static final DateFormatCache UTC_FORMAT;\n\tstatic {\n\t\tfinal DateFormat format = new SimpleDateFormat(UTC_PATTERN);\n\t\tformat.setTimeZone(TimeZones.getUtcTimeZone());\n\t\tformat.setLenient(false);\n\n\t\tUTC_FORMAT = new DateFormatCache(format);\n\t}\n\n\t/**\n\t * Used for parsing times in a local date-time representation.\n\t */\n\tprivate static final DateFormatCache DEFAULT_FORMAT;\n\tstatic {\n\t\tfinal DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN);\n\t\tformat.setLenient(false);\n\t\tDEFAULT_FORMAT = new DateFormatCache(format);\n\t}\n\n\tprivate static final DateFormatCache LENIENT_DEFAULT_FORMAT;\n\tstatic {\n\t\tfinal DateFormat format = new SimpleDateFormat(DEFAULT_PATTERN);\n\t\tLENIENT_DEFAULT_FORMAT = new DateFormatCache(format);\n\t}\n\n\tprivate static final DateFormatCache RELAXED_FORMAT;\n\tstatic {\n\t\tfinal DateFormat format = new SimpleDateFormat(RELAXED_PATTERN);\n        format.setLenient(true);\n\t\tRELAXED_FORMAT = new DateFormatCache(format);\n\t}\n\n\tprivate static final DateFormatCache VCARD_FORMAT;\n\tstatic {\n\t\tfinal DateFormat format = new SimpleDateFormat(VCARD_PATTERN);\n        VCARD_FORMAT = new DateFormatCache(format);\n\t}\n\n\tprivate Time time;\n\n\tprivate TimeZone timezone;\n\n\t/**\n\t * Default constructor.\n\t */\n\tpublic DateTime() {\n\t\tsuper(Dates.PRECISION_SECOND, java.util.TimeZone.getDefault());\n\t\tthis.time = new Time(getTime(), getFormat().getTimeZone());\n\t}\n\n\t/**\n\t * @param utc\n\t *            indicates if the date is in UTC time\n\t */\n\tpublic DateTime(final boolean utc) {\n\t\tthis();\n\t\tsetUtc(utc);\n\t}\n\n\t/**\n\t * @param time\n\t *            a date-time value in milliseconds\n\t */\n\tpublic DateTime(final long time) {\n\t\tsuper(time, Dates.PRECISION_SECOND, java.util.TimeZone.getDefault());\n\t\tthis.time = new Time(time, getFormat().getTimeZone());\n\t}\n\n\t/**\n\t * @param date\n\t *            a date-time value\n\t */\n\tpublic DateTime(final java.util.Date date) {\n\t\tsuper(date.getTime(), Dates.PRECISION_SECOND, java.util.TimeZone.getDefault());\n\t\tthis.time = new Time(date.getTime(), getFormat().getTimeZone());\n\t\t// copy timezone information if applicable..\n\t\tif (date instanceof DateTime) {\n\t\t\tfinal DateTime dateTime = (DateTime) date;\n\t\t\tif (dateTime.isUtc()) {\n\t\t\t\tsetUtc(true);\n\t\t\t} else {\n\t\t\t\tsetTimeZone(dateTime.getTimeZone());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic DateTime(final java.util.Date date, TimeZone timeZone) {\n\t\tthis(date);\n\t\tsetTimeZone(timeZone);\n\t}\n\n\t/**\n\t * Constructs a new DateTime instance from parsing the specified string\n\t * representation in the default (local) timezone.\n\t * \n\t * @param value\n\t *            a string representation of a date-time\n\t * @throws ParseException\n\t *             where the specified string is not a valid date-time\n\t */\n\tpublic DateTime(final String value) throws ParseException {\n\t\tthis(value, null);\n\t\t/*\n\t\t * long time = 0; try { synchronized (UTC_FORMAT) { time =\n\t\t * UTC_FORMAT.parse(value).getTime(); } setUtc(true); } catch\n\t\t * (ParseException pe) { synchronized (DEFAULT_FORMAT) {\n\t\t * DEFAULT_FORMAT.setTimeZone(getFormat().getTimeZone()); time =\n\t\t * DEFAULT_FORMAT.parse(value).getTime(); } this.time = new Time(time,\n\t\t * getFormat().getTimeZone()); } setTime(time);\n\t\t */\n\t}\n\n\t/**\n\t * Creates a new date-time instance from the specified value in the given\n\t * timezone. If a timezone is not specified, the default timezone (as\n\t * returned by {@link TimeZones#getDefault()}) is used.\n\t * \n\t * @param value\n\t *            a string representation of a date-time\n\t * @param timezone\n\t *            the timezone for the date-time instance\n\t * @throws ParseException\n\t *             where the specified string is not a valid date-time\n\t */\n\tpublic DateTime(final String value, final TimeZone timezone)\n\t\t\tthrows ParseException {\n\t\t// setting the time to 0 since we are going to reset it anyway\n\t\tsuper(0, Dates.PRECISION_SECOND, timezone != null ? timezone : TimeZones.getDefault());\n\t\tthis.time = new Time(getTime(), getFormat().getTimeZone());\n\n        try {\n            if (value.endsWith("Z")) {\n                setTime(value, UTC_FORMAT.get(), null);\n                setUtc(true);\n            } else {\n                if (timezone != null) {\n                    setTime(value, DEFAULT_FORMAT.get(), timezone);\n                } else {\n                    // Use lenient parsing for floating times. This is to\n                    // overcome\n                    // the problem of parsing VTimeZone dates that specify dates\n                    // that the strict parser does not accept.\n                    setTime(value, LENIENT_DEFAULT_FORMAT.get(),\n                            getFormat().getTimeZone());\n                }\n                setTimeZone(timezone);\n            }\n        } catch (ParseException pe) {\n            if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_VCARD_COMPATIBILITY)) {\n\n            \ttry {\n\t                setTime(value, VCARD_FORMAT.get(), timezone);\n\t                setTimeZone(timezone);\n            \t} catch (ParseException pe2) {\n                    if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {\n    \t                setTime(value, RELAXED_FORMAT.get(), timezone);\n    \t                setTimeZone(timezone);\n                    }            \t\t\n            \t}\n            } else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {\n                setTime(value, RELAXED_FORMAT.get(), timezone);\n                setTimeZone(timezone);\n            } else {\n                throw pe;\n            }\n        }\n\t}\n\n\t/**\n\t * @param value\n\t *            a string representation of a date-time\n\t * @param pattern\n\t *            a pattern to apply when parsing the date-time value\n\t * @param timezone\n\t *            the timezone for the date-time instance\n\t * @throws ParseException\n\t *             where the specified string is not a valid date-time\n\t */\n\tpublic DateTime(String value, String pattern, TimeZone timezone)\n\t\t\tthrows ParseException {\n\t\t// setting the time to 0 since we are going to reset it anyway\n\t\tsuper(0, Dates.PRECISION_SECOND, timezone != null ? timezone : TimeZones.getDefault());\n\t\tthis.time = new Time(getTime(), getFormat().getTimeZone());\n\n\t\tfinal DateFormat format = CalendarDateFormatFactory\n\t\t\t\t.getInstance(pattern);\n\t\tsetTime(value, format, timezone);\n\t}\n\n\t/**\n\t * @param value\n\t *            a string representation of a date-time\n\t * @param pattern\n\t *            a pattern to apply when parsing the date-time value\n\t * @param utc\n\t *            indicates whether the date-time is in UTC time\n\t * @throws ParseException\n\t *             where the specified string is not a valid date-time\n\t */\n\tpublic DateTime(String value, String pattern, boolean utc)\n\t\t\tthrows ParseException {\n                // setting the time to 0 since we are going to reset it anyway\n\t\tthis(0);\n\t\tfinal DateFormat format = CalendarDateFormatFactory\n\t\t\t\t.getInstance(pattern);\n\t\tif (utc) {\n\t\t\tsetTime(value, format,\n\t\t\t\t\tUTC_FORMAT.get().getTimeZone());\n\t\t} else {\n\t\t\tsetTime(value, format, null);\n\t\t}\n\t\tsetUtc(utc);\n\t}\n\n\t/**\n\t * Internal set of time by parsing value string.\n\t * \n\t * @param value\n\t * @param format\n\t *            a {@code DateFormat}, protected by the use of a ThreadLocal.\n\t * @param tz\n\t * @throws ParseException\n\t */\n\tprivate void setTime(final String value, final DateFormat format,\n\t\t\tfinal java.util.TimeZone tz) throws ParseException {\n\n\t\tif (tz != null) {\n\t\t\tformat.setTimeZone(tz);\n\t\t}\n\t\tsetTime(format.parse(value).getTime());\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic final void setTime(final long time) {\n\t\tsuper.setTime(time);\n\t\t// need to check for null time due to Android java.util.Date(long)\n\t\t// constructor\n\t\t// calling this method..\n\t\tif (this.time != null) {\n\t\t\tthis.time.setTime(time);\n\t\t}\n\t}\n\n\t/**\n\t * @return Returns the utc.\n\t */\n\tpublic final boolean isUtc() {\n\t\treturn time.isUtc();\n\t}\n\n\t/**\n\t * Updates this date-time to display in UTC time if the argument is true.\n\t * Otherwise, resets to the default timezone.\n\t * \n\t * @param utc\n\t *            The utc to set.\n\t */\n\tpublic final void setUtc(final boolean utc) {\n\t\t// reset the timezone associated with this instance..\n\t\tthis.timezone = null;\n\t\tif (utc) {\n\t\t\tgetFormat().setTimeZone(TimeZones.getUtcTimeZone());\n\t\t} else {\n\t\t\tresetTimeZone();\n\t\t}\n\t\ttime = new Time(time, getFormat().getTimeZone(), utc);\n\t}\n\n\t/**\n\t * Sets the timezone associated with this date-time instance. If the\n\t * specified timezone is null, it will reset to the default timezone. If the\n\t * date-time instance is utc, it will turn into either a floating (no\n\t * timezone) date-time, or a date-time with a timezone.\n\t * \n\t * @param timezone\n\t *            a timezone to apply to the instance\n\t */\n\tpublic final void setTimeZone(final TimeZone timezone) {\n\t\tthis.timezone = timezone;\n\t\tif (timezone != null) {\n\t\t\tgetFormat().setTimeZone(timezone);\n\t\t} else {\n\t\t\tresetTimeZone();\n\t\t}\n\t\ttime = new Time(time, getFormat().getTimeZone(), false);\n\t}\n\n\t/**\n\t * Reset the timezone to default.\n\t */\n\tprivate void resetTimeZone() {\n\t\t// use GMT timezone to avoid daylight savings rules affecting floating\n\t\t// time values..\n\t\tgetFormat().setTimeZone(TimeZones.getDefault());\n\t\t// getFormat().setTimeZone(TimeZone.getTimeZone(TimeZones.GMT_ID));\n\t}\n\n\t/**\n\t * Returns the current timezone associated with this date-time value.\n\t * \n\t * @return a Java timezone\n\t */\n\tpublic final TimeZone getTimeZone() {\n\t\treturn timezone;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic final String toString() {\n\t\treturn super.toString() + \'T\' +\n\t\t\t\ttime.toString();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean equals(final Object arg0) {\n\t\t// TODO: what about compareTo, before, after, etc.?\n\n\t\tif (arg0 instanceof DateTime) {\n\t\t\treturn new EqualsBuilder().append(time, ((DateTime) arg0).time)\n\t\t\t\t\t.isEquals();\n\t\t}\n\t\treturn super.equals(arg0);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\treturn super.hashCode();\n\t}\n\n\t\t\tthis.templateFormat = dateFormat;\n\t\t}\n\n\t\tpublic DateFormat get() {\n\t\t\tDateFormat dateFormat = threadMap.get(Thread.currentThread());\n\t\t\tif (dateFormat == null) {\n\t\t\t\tdateFormat = (DateFormat) templateFormat.clone();\n\tprivate static class DateFormatCache {\n\n\t\t/**\n\t\t * This map needs to keep weak references (to avoid memory leaks - see r1.37)\n\t\t * and be thread-safe (since it may be concurrently modified in get() below).\n\t\t */\n\t\tprivate final Map<Thread, DateFormat> threadMap = Collections.synchronizedMap(new WeakHashMap<Thread, DateFormat>());\n\n\t\tprivate final DateFormat templateFormat;\n\n\t\tprivate DateFormatCache(DateFormat dateFormat) {\n\t\t\tthis.templateFormat = dateFormat;\n\t\t}\n\n\t\tpublic DateFormat get() {\n\t\t\tDateFormat dateFormat = threadMap.get(Thread.currentThread());\n\t\t\tif (dateFormat == null) {\n\t\t\t\tdateFormat = (DateFormat) templateFormat.clone();\n\t\t\t\tthreadMap.put(Thread.currentThread(), dateFormat);\n\t\t\t}\n\t\t\treturn dateFormat;\n\t\t}\n\t}\n}\n', 'message': 'Access to WeakHashMap must be synchronized\n\n...if a thread might be changing the map.', 'author_name': 'GitHub', 'lang': 'java'}