1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package org.apache.commons.httpclient.util;
32
33 import java.text.ParseException;
34 import java.text.SimpleDateFormat;
35 import java.util.Arrays;
36 import java.util.Calendar;
37 import java.util.Collection;
38 import java.util.Date;
39 import java.util.Iterator;
40 import java.util.Locale;
41 import java.util.TimeZone;
42
43 /***
44 * A utility class for parsing and formatting HTTP dates as used in cookies and
45 * other headers. This class handles dates as defined by RFC 2616 section
46 * 3.3.1 as well as some other common non-standard formats.
47 *
48 * @author Christopher Brown
49 * @author Michael Becke
50 */
51 public class DateUtil {
52
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";
57
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";
62
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";
68
69 private static final Collection DEFAULT_PATTERNS = Arrays.asList(
70 new String[] { PATTERN_ASCTIME, PATTERN_RFC1036, PATTERN_RFC1123 } );
71
72 private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
73
74 static {
75 Calendar calendar = Calendar.getInstance();
76 calendar.set(2000, Calendar.JANUARY, 1, 0, 0);
77 DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
78 }
79
80 private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
81
82 /***
83 * Parses a date value. The formats used for parsing the date value are retrieved from
84 * the default http params.
85 *
86 * @param dateValue the date value to parse
87 *
88 * @return the parsed date
89 *
90 * @throws DateParseException if the value could not be parsed using any of the
91 * supported date formats
92 */
93 public static Date parseDate(String dateValue) throws DateParseException {
94 return parseDate(dateValue, null, null);
95 }
96
97 /***
98 * Parses the date value using the given date formats.
99 *
100 * @param dateValue the date value to parse
101 * @param dateFormats the date formats to use
102 *
103 * @return the parsed date
104 *
105 * @throws DateParseException if none of the dataFormats could parse the dateValue
106 */
107 public static Date parseDate(String dateValue, Collection dateFormats)
108 throws DateParseException {
109 return parseDate(dateValue, dateFormats, null);
110 }
111
112 /***
113 * Parses the date value using the given date formats.
114 *
115 * @param dateValue the date value to parse
116 * @param dateFormats the date formats to use
117 * @param startDate During parsing, two digit years will be placed in the range
118 * <code>startDate</code> to <code>startDate + 100 years</code>. This value may
119 * be <code>null</code>. When <code>null</code> is given as a parameter, year
120 * <code>2000</code> will be used.
121 *
122 * @return the parsed date
123 *
124 * @throws DateParseException if none of the dataFormats could parse the dateValue
125 */
126 public static Date parseDate(
127 String dateValue,
128 Collection dateFormats,
129 Date startDate
130 ) throws DateParseException {
131
132 if (dateValue == null) {
133 throw new IllegalArgumentException("dateValue is null");
134 }
135 if (dateFormats == null) {
136 dateFormats = DEFAULT_PATTERNS;
137 }
138 if (startDate == null) {
139 startDate = DEFAULT_TWO_DIGIT_YEAR_START;
140 }
141
142
143 if (dateValue.length() > 1
144 && dateValue.startsWith("'")
145 && dateValue.endsWith("'")
146 ) {
147 dateValue = dateValue.substring (1, dateValue.length() - 1);
148 }
149
150 SimpleDateFormat dateParser = null;
151 Iterator formatIter = dateFormats.iterator();
152
153 while (formatIter.hasNext()) {
154 String format = (String) formatIter.next();
155 if (dateParser == null) {
156 dateParser = new SimpleDateFormat(format, Locale.US);
157 dateParser.setTimeZone(TimeZone.getTimeZone("GMT"));
158 dateParser.set2DigitYearStart(startDate);
159 } else {
160 dateParser.applyPattern(format);
161 }
162 try {
163 return dateParser.parse(dateValue);
164 } catch (ParseException pe) {
165
166 }
167 }
168
169
170 throw new DateParseException("Unable to parse the date " + dateValue);
171 }
172
173 /***
174 * Formats the given date according to the RFC 1123 pattern.
175 *
176 * @param date The date to format.
177 * @return An RFC 1123 formatted date string.
178 *
179 * @see #PATTERN_RFC1123
180 */
181 public static String formatDate(Date date) {
182 return formatDate(date, PATTERN_RFC1123);
183 }
184
185 /***
186 * Formats the given date according to the specified pattern. The pattern
187 * must conform to that used by the {@link SimpleDateFormat simple date
188 * format} class.
189 *
190 * @param date The date to format.
191 * @param pattern The pattern to use for formatting the date.
192 * @return A formatted date string.
193 *
194 * @throws IllegalArgumentException If the given date pattern is invalid.
195 *
196 * @see SimpleDateFormat
197 */
198 public static String formatDate(Date date, String pattern) {
199 if (date == null) throw new IllegalArgumentException("date is null");
200 if (pattern == null) throw new IllegalArgumentException("pattern is null");
201
202 SimpleDateFormat formatter = new SimpleDateFormat(pattern, Locale.US);
203 formatter.setTimeZone(GMT);
204 return formatter.format(date);
205 }
206
207 /*** This class should not be instantiated. */
208 private DateUtil() { }
209
210 }