View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/util/DateUtil.java $
3    * $Revision$
4    * $Date$
5    *
6    * ====================================================================
7    *
8    *  Licensed to the Apache Software Foundation (ASF) under one or more
9    *  contributor license agreements.  See the NOTICE file distributed with
10   *  this work for additional information regarding copyright ownership.
11   *  The ASF licenses this file to You under the Apache License, Version 2.0
12   *  (the "License"); you may not use this file except in compliance with
13   *  the License.  You may obtain a copy of the License at
14   *
15   *      http://www.apache.org/licenses/LICENSE-2.0
16   *
17   *  Unless required by applicable law or agreed to in writing, software
18   *  distributed under the License is distributed on an "AS IS" BASIS,
19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   *  See the License for the specific language governing permissions and
21   *  limitations under the License.
22   * ====================================================================
23   *
24   * This software consists of voluntary contributions made by many
25   * individuals on behalf of the Apache Software Foundation.  For more
26   * information on the Apache Software Foundation, please see
27   * <http://www.apache.org/>.
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         // trim single quotes around date if present
142         // see issue #5279
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                 // ignore this exception, we will try the next format
166             }                
167         }
168         
169         // we were unable to parse the date
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 }