View Javadoc

1   /*
2    * Copyright 2004 Sun Microsystems, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  package com.sun.syndication.io.impl;
18  
19  import java.text.DateFormat;
20  import java.text.SimpleDateFormat;
21  import java.util.Date;
22  import java.util.TimeZone;
23  
24  /***
25   * A helper class that parses Dates out of Strings with date time in RFC822 and W3CDateTime
26   * formats plus the variants Atom (0.3) and RSS (0.9, 0.91, 0.92, 0.93, 0.94, 1.0 and 2.0)
27   * specificators added to those formats.
28   * <p/>
29   * It uses the JDK java.text.SimpleDateFormat class attemtping the parse using a mask for
30   * each one of the possible formats.
31   * <p/>
32   *
33   * @author Alejandro Abdelnur
34   *
35   */
36  public class DateParser {
37  
38      // order is like this because the SimpleDateFormat.parse does not fail with exception
39      // if it can parse a valid date out of a substring of the full string given the mask
40      // so we have to check the most complete format first, then it fails with exception
41      private static final String[] RFC822_MASKS = {
42          "EEE, dd MMM yyyy HH:mm:ss z",
43          "EEE, dd MMM yyyy HH:mm z",
44          "EEE, dd MMM yy HH:mm:ss z",
45          "EEE, dd MMM yy HH:mm z",
46          "dd MMM yyyy HH:mm:ss z",
47          "dd MMM yyyy HH:mm z",
48          "dd MMM yy HH:mm:ss z",
49          "dd MMM yy HH:mm z"
50      };
51  
52      // order is like this because the SimpleDateFormat.parse does not fail with exception
53      // if it can parse a valid date out of a substring of the full string given the mask
54      // so we have to check the most complete format first, then it fails with exception
55      private static final String[] W3CDATETIME_MASKS = {
56          "yyyy-MM-dd'T'HH:mm:ssz",
57          "yyyy-MM-dd'T'HH:mmz",
58          "yyyy-MM-dd",
59          "yyyy-MM",
60          "yyyy"
61      };
62  
63      /***
64       * Private constructor to avoid DateParser instances creation.
65       */
66      private DateParser() {
67      }
68  
69      /***
70       * Parses a Date out of a string using an array of masks.
71       * <p/>
72       * It uses the masks in order until one of them succedes or all fail.
73       * <p/>
74       *
75       * @param masks array of masks to use for parsing the string
76       * @param sDate string to parse for a date.
77       * @return the Date represented by the given string using one of the given masks.
78       * It returns <b>null</b> if it was not possible to parse the the string with any of the masks.
79       *
80       */
81      private static Date parseUsingMask(String[] masks,String sDate) {
82          Date d = null;
83          for (int i=0;d==null && i<masks.length;i++) {
84              DateFormat df = new SimpleDateFormat(masks[i]);
85              df.setLenient(true);
86              try {
87                  d = df.parse(sDate);
88  //                System.out.println("s: "+sDate+" m: "+masks[i]+" d: "+d);
89              }
90              catch (Exception ex1) {
91  //                System.out.println("s: "+sDate+" m: "+masks[i]+" d: "+null);
92              }
93          }
94          return d;
95      }
96  
97      /***
98       * Parses a Date out of a String with a date in RFC822 format.
99       * <p/>
100      * It parsers the following formats:
101      * <ul>
102      *   <li>"EEE, dd MMM yyyy HH:mm:ss z"</li>
103      *   <li>"EEE, dd MMM yyyy HH:mm z"</li>
104      *   <li>"EEE, dd MMM yy HH:mm:ss z"</li>
105      *   <li>"EEE, dd MMM yy HH:mm z"</li>
106      *   <li>"dd MMM yyyy HH:mm:ss z"</li>
107      *   <li>"dd MMM yyyy HH:mm z"</li>
108      *   <li>"dd MMM yy HH:mm:ss z"</li>
109      *   <li>"dd MMM yy HH:mm z"</li>
110      * </ul>
111      * <p/>
112      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
113      * <p/>
114      * @param sDate string to parse for a date.
115      * @return the Date represented by the given RFC822 string.
116      *         It returns <b>null</b> if it was not possible to parse the given string into a Date.
117      *
118      */
119     public static Date parseRFC822(String sDate) {
120         return parseUsingMask(RFC822_MASKS,sDate);
121     }
122 
123 
124     /***
125      * Parses a Date out of a String with a date in W3C date-time format.
126      * <p/>
127      * It parsers the following formats:
128      * <ul>
129      *   <li>"yyyy-MM-dd'T'HH:mm:ssz"</li>
130      *   <li>"yyyy-MM-dd'T'HH:mmz"</li>
131      *   <li>"yyyy-MM-dd"</li>
132      *   <li>"yyyy-MM"</li>
133      *   <li>"yyyy"</li>
134      * </ul>
135      * <p/>
136      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
137      * <p/>
138      * @param sDate string to parse for a date.
139      * @return the Date represented by the given W3C date-time string.
140      *         It returns <b>null</b> if it was not possible to parse the given string into a Date.
141      *
142      */
143     public static Date parseW3CDateTime(String sDate) {
144         // if sDate has time on it, it injects 'GTM' before de TZ displacement to
145         // allow the SimpleDateFormat parser to parse it properly
146         int tIndex = sDate.indexOf("T");
147         if (tIndex>-1) {
148             if (sDate.endsWith("Z")) {
149                 sDate = sDate.substring(0,sDate.length()-1)+"+00:00";
150             }
151             int tzdIndex = sDate.indexOf("+",tIndex);
152             if (tzdIndex==-1) {
153                 tzdIndex = sDate.indexOf("-",tIndex);
154             }
155             if (tzdIndex>-1) {
156                 String pre = sDate.substring(0,tzdIndex);
157                 int secFraction = pre.indexOf(",");
158                 if (secFraction>-1) {
159                     pre = pre.substring(0,secFraction);
160                 }
161                 String post = sDate.substring(tzdIndex);
162                 sDate = pre + "GMT" + post;
163             }
164         }
165         return parseUsingMask(W3CDATETIME_MASKS,sDate);
166     }
167 
168     /***
169      * create a RFC822 representation of a date.
170      * <p/>
171      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
172      * <p/>
173      * @param date Date to parse
174      * @return the RFC822 represented by the given Date
175      *         It returns <b>null</b> if it was not possible to parse the date.
176      *
177      */
178     public static String formatRFC822(Date date) {
179         SimpleDateFormat dateFormater = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
180         dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
181         return dateFormater.format(date);
182     }
183 
184     /***
185      * create a W3C Date Time representation of a date.
186      * <p/>
187      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
188      * <p/>
189      * @param date Date to parse
190      * @return the W3C Date Time represented by the given Date
191      *         It returns <b>null</b> if it was not possible to parse the date.
192      *
193      */
194     public static String formatW3CDateTime(Date date) {
195         SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
196         dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
197         return dateFormater.format(date);
198     }
199 
200 }