Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
28 package ch.boye.httpclientandroidlib.impl.cookie;
30 import java.lang.ref.SoftReference;
31 import java.text.ParseException;
32 import java.text.SimpleDateFormat;
33 import java.util.Calendar;
34 import java.util.Date;
35 import java.util.HashMap;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.TimeZone;
40 import ch.boye.httpclientandroidlib.annotation.Immutable;
42 /**
43 * A utility class for parsing and formatting HTTP dates as used in cookies and
44 * other headers. This class handles dates as defined by RFC 2616 section
45 * 3.3.1 as well as some other common non-standard formats.
46 *
47 *
48 * @since 4.0
49 */
50 @Immutable
51 public final class DateUtils {
53 /**
54 * Date format pattern used to parse HTTP date headers in RFC 1123 format.
55 */
56 public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
58 /**
59 * Date format pattern used to parse HTTP date headers in RFC 1036 format.
60 */
61 public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz";
63 /**
64 * Date format pattern used to parse HTTP date headers in ANSI C
65 * <code>asctime()</code> format.
66 */
67 public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
69 private static final String[] DEFAULT_PATTERNS = new String[] {
70 PATTERN_RFC1036,
71 PATTERN_RFC1123,
72 PATTERN_ASCTIME
73 };
75 private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
77 public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
79 static {
80 Calendar calendar = Calendar.getInstance();
81 calendar.setTimeZone(GMT);
82 calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
83 calendar.set(Calendar.MILLISECOND, 0);
84 DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
85 }
87 /**
88 * Parses a date value. The formats used for parsing the date value are retrieved from
89 * the default http params.
90 *
91 * @param dateValue the date value to parse
92 *
93 * @return the parsed date
94 *
95 * @throws DateParseException if the value could not be parsed using any of the
96 * supported date formats
97 */
98 public static Date parseDate(String dateValue) throws DateParseException {
99 return parseDate(dateValue, null, null);
100 }
102 /**
103 * Parses the date value using the given date formats.
104 *
105 * @param dateValue the date value to parse
106 * @param dateFormats the date formats to use
107 *
108 * @return the parsed date
109 *
110 * @throws DateParseException if none of the dataFormats could parse the dateValue
111 */
112 public static Date parseDate(final String dateValue, String[] dateFormats)
113 throws DateParseException {
114 return parseDate(dateValue, dateFormats, null);
115 }
117 /**
118 * Parses the date value using the given date formats.
119 *
120 * @param dateValue the date value to parse
121 * @param dateFormats the date formats to use
122 * @param startDate During parsing, two digit years will be placed in the range
123 * <code>startDate</code> to <code>startDate + 100 years</code>. This value may
124 * be <code>null</code>. When <code>null</code> is given as a parameter, year
125 * <code>2000</code> will be used.
126 *
127 * @return the parsed date
128 *
129 * @throws DateParseException if none of the dataFormats could parse the dateValue
130 */
131 public static Date parseDate(
132 String dateValue,
133 String[] dateFormats,
134 Date startDate
135 ) throws DateParseException {
137 if (dateValue == null) {
138 throw new IllegalArgumentException("dateValue is null");
139 }
140 if (dateFormats == null) {
141 dateFormats = DEFAULT_PATTERNS;
142 }
143 if (startDate == null) {
144 startDate = DEFAULT_TWO_DIGIT_YEAR_START;
145 }
146 // trim single quotes around date if present
147 // see issue #5279
148 if (dateValue.length() > 1
149 && dateValue.startsWith("'")
150 && dateValue.endsWith("'")
151 ) {
152 dateValue = dateValue.substring (1, dateValue.length() - 1);
153 }
155 for (String dateFormat : dateFormats) {
156 SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
157 dateParser.set2DigitYearStart(startDate);
159 try {
160 return dateParser.parse(dateValue);
161 } catch (ParseException pe) {
162 // ignore this exception, we will try the next format
163 }
164 }
166 // we were unable to parse the date
167 throw new DateParseException("Unable to parse the date " + dateValue);
168 }
170 /**
171 * Formats the given date according to the RFC 1123 pattern.
172 *
173 * @param date The date to format.
174 * @return An RFC 1123 formatted date string.
175 *
176 * @see #PATTERN_RFC1123
177 */
178 public static String formatDate(Date date) {
179 return formatDate(date, PATTERN_RFC1123);
180 }
182 /**
183 * Formats the given date according to the specified pattern. The pattern
184 * must conform to that used by the {@link SimpleDateFormat simple date
185 * format} class.
186 *
187 * @param date The date to format.
188 * @param pattern The pattern to use for formatting the date.
189 * @return A formatted date string.
190 *
191 * @throws IllegalArgumentException If the given date pattern is invalid.
192 *
193 * @see SimpleDateFormat
194 */
195 public static String formatDate(Date date, String pattern) {
196 if (date == null) throw new IllegalArgumentException("date is null");
197 if (pattern == null) throw new IllegalArgumentException("pattern is null");
199 SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
200 return formatter.format(date);
201 }
203 /** This class should not be instantiated. */
204 private DateUtils() {
205 }
207 /**
208 * A factory for {@link SimpleDateFormat}s. The instances are stored in a
209 * threadlocal way because SimpleDateFormat is not threadsafe as noted in
210 * {@link SimpleDateFormat its javadoc}.
211 *
212 */
213 final static class DateFormatHolder {
215 private static final ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>
216 THREADLOCAL_FORMATS = new ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>() {
218 @Override
219 protected SoftReference<Map<String, SimpleDateFormat>> initialValue() {
220 return new SoftReference<Map<String, SimpleDateFormat>>(
221 new HashMap<String, SimpleDateFormat>());
222 }
224 };
226 /**
227 * creates a {@link SimpleDateFormat} for the requested format string.
228 *
229 * @param pattern
230 * a non-<code>null</code> format String according to
231 * {@link SimpleDateFormat}. The format is not checked against
232 * <code>null</code> since all paths go through
233 * {@link DateUtils}.
234 * @return the requested format. This simple dateformat should not be used
235 * to {@link SimpleDateFormat#applyPattern(String) apply} to a
236 * different pattern.
237 */
238 public static SimpleDateFormat formatFor(String pattern) {
239 SoftReference<Map<String, SimpleDateFormat>> ref = THREADLOCAL_FORMATS.get();
240 Map<String, SimpleDateFormat> formats = ref.get();
241 if (formats == null) {
242 formats = new HashMap<String, SimpleDateFormat>();
243 THREADLOCAL_FORMATS.set(
244 new SoftReference<Map<String, SimpleDateFormat>>(formats));
245 }
247 SimpleDateFormat format = formats.get(pattern);
248 if (format == null) {
249 format = new SimpleDateFormat(pattern, Locale.US);
250 format.setTimeZone(TimeZone.getTimeZone("GMT"));
251 formats.put(pattern, format);
252 }
254 return format;
255 }
257 }
259 }