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             int tzdIndex = sDate.indexOf("+",tIndex);
149             if (tzdIndex==-1) {
150                 tzdIndex = sDate.indexOf("-",tIndex);
151             }
152             if (tzdIndex>-1) {
153                 String pre = sDate.substring(0,tzdIndex);
154                 int secFraction = pre.indexOf(",");
155                 if (secFraction>-1) {
156                     pre = pre.substring(0,secFraction);
157                 }
158                 String post = sDate.substring(tzdIndex);
159                 sDate = pre + "GMT" + post;
160             }
161         }
162         return parseUsingMask(W3CDATETIME_MASKS,sDate);
163     }
164 
165     /***
166      * create a RFC822 representation of a date.
167      * <p/>
168      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
169      * <p/>
170      * @param date Date to parse
171      * @return the RFC822 represented by the given Date
172      *         It returns <b>null</b> if it was not possible to parse the date.
173      *
174      */
175     public static String formatRFC822(Date date) {
176         SimpleDateFormat dateFormater = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
177         dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
178         return dateFormater.format(date);
179     }
180 
181     /***
182      * create a W3C Date Time representation of a date.
183      * <p/>
184      * Refer to the java.text.SimpleDateFormat javadocs for details on the format of each element.
185      * <p/>
186      * @param date Date to parse
187      * @return the W3C Date Time represented by the given Date
188      *         It returns <b>null</b> if it was not possible to parse the date.
189      *
190      */
191     public static String formatW3CDateTime(Date date) {
192         SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
193         dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
194         return dateFormater.format(date);
195     }
196 
197 }