View Javadoc
1   /*
2    * Copyright 2006 Edward Kuns
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   *
14   * $Id: $
15   */
16  package org.exolab.castor.types;
17  
18  import java.text.ParseException;
19  import java.util.Calendar;
20  import java.util.GregorianCalendar;
21  
22  /**
23   * Describe an XML schema DateTime.
24   * <p>
25   * The format is defined by W3C XML Schema Recommendation and ISO8601 i.e
26   * <tt>(-)CCYY-MM-DD'T'HH:MM:SS(.SSSSS)(Z|(+|-)hh:mm)</tt>
27   * 
28   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
29   * @version $Revision: 0000 $
30   */
31  public class DateTime extends DateTimeBase {
32    /** SerialVersionUID. */
33    private static final long serialVersionUID = 6278590966410879734L;
34    /** Complaint string. */
35    private static final String BAD_DATE = "Bad DateTime format: ";
36  
37    /**
38     * Default constructor.
39     */
40    public DateTime() {
41      // Nothing for the default
42    }
43  
44    /**
45     * Constructs a XML Schema DateTime instance given all the values of the different date and time
46     * (but not time zone) fields.
47     * <p>
48     * By default a DateTime is not UTC, and is local. To set a timezone, you need to separately call
49     * {@link #setZone(short, short)}.
50     *
51     * @param values an array of shorts that represent the different fields of Time.
52     * @see #setValues
53     */
54    public DateTime(short[] values) {
55      setValues(values);
56    }
57  
58    /**
59     * Creates a new XML Schema DateTime instance from a long that represents a Date. No time zone
60     * information is set.
61     * <p>
62     * By default a DateTime is not UTC, and is local. To set a timezone, you need to separately call
63     * {@link #setZone(short, short)}.
64     *
65     * @param dateAsLong java.util.Date represented as a long.
66     */
67    public DateTime(long dateAsLong) {
68      this(new java.util.Date(dateAsLong));
69    }
70  
71    /**
72     * Creates a new XML Schema DateTime instance from a java.util.Date. No time zone information is
73     * set.
74     * <p>
75     * By default a DateTime is not UTC, and is local. To set a timezone, you need to separately call
76     * {@link #setZone(short, short)}.
77     *
78     * @param dateRef a java.util.Date to convert.
79     */
80    public DateTime(java.util.Date dateRef) {
81      GregorianCalendar tempCalendar = new GregorianCalendar();
82      tempCalendar.setTime(dateRef);
83  
84      setCentury((short) (tempCalendar.get(Calendar.YEAR) / 100));
85      setYear((short) (tempCalendar.get(Calendar.YEAR) % 100));
86      // In GregorianCalendar, 0 <= Month <= 11; January == 0
87      setMonth((short) (tempCalendar.get(Calendar.MONTH) + 1));
88      setDay((short) tempCalendar.get(Calendar.DAY_OF_MONTH));
89  
90      setHour((short) tempCalendar.get(Calendar.HOUR_OF_DAY));
91      setMinute((short) tempCalendar.get(Calendar.MINUTE));
92      setSecond((short) tempCalendar.get(Calendar.SECOND),
93          (short) tempCalendar.get(Calendar.MILLISECOND));
94    }
95  
96    /**
97     * Constructs a DateTime from a String. The String is expected to be in W3C Schema DateTime
98     * format.
99     *
100    * @param date the string representing the date
101    * @throws java.text.ParseException if we are passed an illegal value
102    */
103   public DateTime(String date) throws java.text.ParseException {
104     parseDateTimeInternal(date, this);
105   }
106 
107   /**
108    * Sets all the fields to the values provided in an Array. The Array must be at least eight
109    * entries long. Extra entries are ignored. The order of entries in the array is as follows:
110    * <ul>
111    * <li>century</li>
112    * <li>year</li>
113    * <li>month</li>
114    * <li>day</li>
115    * <li>hour</li>
116    * <li>minute</li>
117    * <li>second</li>
118    * <li>millisecond</li>
119    * </ul>
120    * If a Time Zone is to be specified, it has to be set separately by using
121    * {@link DateTimeBase#setZone(short, short) setZone}. A time zone previously set will not be
122    * cleared.
123    *
124    * @param values An array of shorts containing the values for the DateTime
125    */
126   public void setValues(short[] values) {
127     if (values.length != 8) {
128       throw new IllegalArgumentException(
129           "DateTime#setValues: Array length " + values.length + " != 8");
130     }
131     this.setCentury(values[0]);
132     this.setYear(values[1]);
133     this.setMonth(values[2]);
134     this.setDay(values[3]);
135     this.setHour(values[4]);
136     this.setMinute(values[5]);
137     this.setSecond(values[6], values[7]);
138   }
139 
140   /**
141    * Returns an array of shorts with all the fields that describe this DateTime type. The order of
142    * entries in the array is as follows:
143    * <ul>
144    * <li>century</li>
145    * <li>year</li>
146    * <li>month</li>
147    * <li>day</li>
148    * <li>hour</li>
149    * <li>minute</li>
150    * <li>second</li>
151    * <li>millisecond</li>
152    * </ul>
153    * Note:the time zone is not included.
154    *
155    * @return an array of short with all the fields that describe this Date type.
156    */
157   public short[] getValues() {
158     short[] result = new short[8];
159     result[0] = this.getCentury();
160     result[1] = this.getYear();
161     result[2] = this.getMonth();
162     result[3] = this.getDay();
163     result[4] = this.getHour();
164     result[5] = this.getMinute();
165     result[6] = this.getSeconds();
166     result[7] = this.getMilli();
167     return result;
168   } // getValues
169 
170   /**
171    * Converts this DateTime into a local java.util.Date.
172    * 
173    * @return a local java.util.Date representing this DateTime.
174    */
175   public java.util.Date toDate() {
176     Calendar calendar = new GregorianCalendar(getCentury() * 100 + getYear(), getMonth() - 1,
177         getDay(), getHour(), getMinute(), getSeconds());
178     calendar.set(Calendar.MILLISECOND, getMilli());
179     setDateFormatTimeZone(calendar);
180     return calendar.getTime();
181   } // toDate()
182 
183   /**
184    * Converts this DateTime into a long value representing a java.util.Date.
185    * 
186    * @return This DateTime instance as a long value representing a java.util.Date.
187    */
188   public long toLong() {
189     return toDate().getTime();
190   }
191 
192   /**
193    * Converts this DateTime to a string. The format is defined by W3C XML Schema recommendation and
194    * ISO8601: (+|-)CCYY-MM-DDTHH:MM:SS.SSS(+/-)HH:SS
195    *
196    * @return a string representing this Date
197    */
198   public String toString() {
199     StringBuffer result = new StringBuffer();
200 
201     appendDateString(result);
202     result.append('T');
203     appendTimeString(result);
204     appendTimeZoneString(result);
205 
206     return result.toString();
207   } // toString
208 
209   /**
210    * Parses a String into a new DateTime instance.
211    *
212    * @param str the string to parse
213    * @return a new DateTime instance with the value of the parsed string.
214    * @throws ParseException If the string to parse does not follow the right format
215    */
216   public static DateTime parse(String str) throws ParseException {
217     return parseDateTime(str);
218   }
219 
220   /**
221    * Parses a String into a new DateTime instance.
222    *
223    * @param str the string to parse
224    * @return a new DateTime instance with the value of the parsed string.
225    * @throws ParseException If the string to parse does not follow the right format
226    */
227   public static DateTime parseDateTime(String str) throws ParseException {
228     return parseDateTimeInternal(str, new DateTime());
229   }
230 
231   /**
232    * Parses a String into the provided DateTime instance (or a new DateTime instance if the one
233    * provided is null) and assigns that value to the DateTime instance given.
234    *
235    * @param str the string to parse
236    * @param result the DateTime instance to assign to the parsed value of the String. If null is
237    *        passed, a new DateTime instance is created to be returned.
238    * @return the DateTime instance with the value of the parsed string.
239    * @throws ParseException If the string to parse does not follow the right format
240    */
241   private static DateTime parseDateTimeInternal(String str, DateTime result) throws ParseException {
242     if (str == null) {
243       throw new IllegalArgumentException("The string to be parsed must not be null.");
244     }
245 
246     if (result == null) {
247       result = new DateTime();
248     }
249 
250     char[] chars = str.toCharArray();
251 
252     if (chars.length < 19) {
253       throw new ParseException(BAD_DATE + str + "\nDateTime is not long enough", 0);
254     }
255 
256     int idx = 0;
257     idx = parseYear(str, result, chars, idx, BAD_DATE);
258     idx = parseMonth(str, result, chars, idx, BAD_DATE);
259     idx = parseDay(str, result, chars, idx, BAD_DATE);
260 
261     if (chars[idx] != 'T') {
262       throw new ParseException(BAD_DATE + str + "\n 'T' " + DateTimeBase.WRONGLY_PLACED, idx);
263     }
264 
265     idx++;
266 
267     idx = parseTime(str, result, chars, idx, BAD_DATE);
268     parseTimeZone(str, result, chars, idx, BAD_DATE);
269 
270     return result;
271   } // parse
272 
273 }