View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 2001 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * $Id$ Date Author Changes 10/02/2002 Arnaud Added support for 3 figure millisecond and clean up
34   * 04/18/2002 Arnaud String Constructor 05/22/2001 Arnaud Blandin Created
35   */
36  package org.exolab.castor.types;
37  
38  import java.text.SimpleDateFormat;
39  import java.text.ParseException;
40  import java.util.Date;
41  
42  /**
43   * Describes an XML schema Time.
44   * <p>
45   * The format is defined by W3C XML Schema Recommendation and ISO8601 i.e
46   * <tt>hh:mm:ss.sss(Z|(+|-)hh:mm)</tt>
47   * <p>
48   * Deep support of fractional seconds is not implemented. This implementation only supports
49   * fractional second resolution of milliseconds.
50   *
51   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
52   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
53   * @version $Revision$
54   * @see DateTimeBase
55   */
56  public class Time extends DateTimeBase {
57  
58    /** SerialVersionUID */
59    private static final long serialVersionUID = -8268707778437931489L;
60  
61    /** The Time Format used by the toDate() method */
62    private static final String TIME_FORMAT_MILLI = "HH:mm:ss.SSS";
63    private static final String TIME_FORMAT_NO_MILLI = "HH:mm:ss";
64  
65    private static final String BAD_TIME = "Bad Time format: ";
66  
67    /**
68     * No-arg constructor.
69     */
70    public Time() {
71      // Nothing to do
72    }
73  
74    /**
75     * Constructs a XML Schema Time instance given all the values of the different fields. By default
76     * a Time is not UTC and is local.
77     * 
78     * @param values an array of shorts that represent the different fields of Time.
79     */
80    public Time(short[] values) {
81      setValues(values);
82    }
83  
84    /**
85     * Constructs a XML Schema Time instance given a long representing the time in milliseconds. By
86     * default a Time is not UTC and is local.
87     * 
88     * @param l The long value that represents the time instance.
89     */
90    public Time(long l) {
91      this(l, false);
92    }
93  
94    /**
95     * Constructs a XML Schema Time instance given a long representing the time in milliseconds.
96     * 
97     * @param l The long value that represents the time instance.
98     * @param utc Does the long value represent a UTC time?
99     */
100   public Time(long l, boolean utc) {
101     if (l > 86400000L) {
102       throw new IllegalArgumentException("Bad Time: the long value can't represent more than 24h.");
103     }
104 
105     setHour((short) (l / 3600000));
106     l = l % 3600000;
107     setMinute((short) (l / 60000));
108     l = l % 60000;
109     setSecond((short) (l / 1000), (short) (l % 1000));
110 
111     if (utc) {
112       setZone((short) 0, (short) 0);
113     }
114   }
115 
116   /**
117    * Constructs a Time given a string representation.
118    * 
119    * @param time the string representation of the Time to instantiate
120    * @throws ParseException a parse exception is thrown if the string to parse does not follow the
121    *         rigth format (see the description of this class)
122    */
123   public Time(String time) throws ParseException {
124     parseTimeInternal(time, this);
125   }
126 
127   /**
128    * Sets all the fields by reading the values in an array.
129    * <p>
130    * if a Time Zone is specified it has to be set by using {@link DateTimeBase#setZone(short, short)
131    * setZone}.
132    *
133    * @param values an array of shorts with the values the array is supposed to be of length 4 and
134    *        ordered like that:
135    *        <ul>
136    *        <li>hour</li>
137    *        <li>minute</li>
138    *        <li>second</li>
139    *        <li>millisecond</li>
140    *        </ul>
141    */
142   public void setValues(short[] values) {
143     if (values.length != 4) {
144       throw new IllegalArgumentException("Time#setValues: not the right number of values");
145     }
146     this.setHour(values[0]);
147     this.setMinute(values[1]);
148     this.setSecond(values[2], values[3]);
149   }
150 
151   /**
152    * returns an array of short with all the fields that describe this time type.
153    * <p>
154    * Note:the time zone is not included.
155    * 
156    * @return an array of short with all the fields that describe this time type.
157    */
158   public short[] getValues() {
159     short[] result = new short[4];
160     result[0] = this.getHour();
161     result[1] = this.getMinute();
162     result[2] = this.getSeconds();
163     result[3] = this.getMilli();
164     return result;
165   } // getValues
166 
167   /**
168    * Converts this Time instance into a long value based on UTC time zone.
169    * 
170    * @return A long value representing this Time instance.
171    */
172   public long toLong() {
173     int sign = isZoneNegative() ? 1 : -1;
174     int hour = getHour() + sign * getZoneHour();
175     int minute = getMinute() + sign * getZoneMinute();
176     int second = getSeconds();
177     int milli = getMilli();
178 
179     if (minute < 0) {
180       minute = minute + 60;
181       hour = hour - 1;
182     } else if (minute > 59) {
183       minute = minute - 60;
184       hour = hour + 1;
185     }
186 
187     if (hour < 0) {
188       hour = hour + 24;
189     } else if (hour > 23) {
190       hour = hour - 24;
191     }
192 
193     return 3600000 * hour + 60000 * minute + 1000 * second + milli;
194   }
195 
196   /**
197    * converts this Time into a local java Date.
198    * 
199    * @return a local date representing this Time
200    */
201   public Date toDate() {
202     Date date = null;
203     SimpleDateFormat df = null;
204     String temp = this.toString();
205     if (temp.indexOf('.') > 0) {
206       df = new SimpleDateFormat(TIME_FORMAT_MILLI);
207     } else {
208       df = new SimpleDateFormat(TIME_FORMAT_NO_MILLI);
209     }
210 
211     setDateFormatTimeZone(df);
212 
213     try {
214       date = df.parse(temp);
215     } catch (ParseException e) {
216       // this can't happen since toString() should return the proper string format
217       e.printStackTrace();
218       return null;
219     }
220     return date;
221   } // toDate()
222 
223   /**
224    * convert this Time to a string The format is defined by W3C XML Schema Recommendation and
225    * ISO8601 i.e hh:mm:ss.sss(Z|(+|-)hh:mm)
226    * 
227    * @return a string representing this Time
228    */
229   public String toString() {
230     StringBuffer result = new StringBuffer();
231     appendTimeString(result);
232     appendTimeZoneString(result);
233     return result.toString();
234   } // toString
235 
236   /**
237    * parses a String and converts it into a java.lang.Object
238    * 
239    * @param str the string to parse
240    * @return the java.lang.Object represented by the string
241    * @throws ParseException a parse exception is thrown if the string to parse does not follow the
242    *         rigth format (see the description of this class)
243    */
244   public static Object parse(String str) throws ParseException {
245     return parseTime(str);
246   }
247 
248   /**
249    * parses a String and converts it into a Time.
250    * 
251    * @param str the string to parse
252    * @return the Time represented by the string
253    * @throws ParseException a parse exception is thrown if the string to parse does not follow the
254    *         rigth format (see the description of this class)
255    */
256   public static Time parseTime(String str) throws ParseException {
257     return parseTimeInternal(str, null);
258   }
259 
260   private static Time parseTimeInternal(String str, Time result) throws ParseException {
261     if (str == null) {
262       throw new IllegalArgumentException("The string to be parsed must not be null.");
263     }
264 
265     if (result == null) {
266       result = new Time();
267     }
268 
269     char[] chars = str.toCharArray();
270     int idx = parseTime(str, result, chars, 0, BAD_TIME);
271     parseTimeZone(str, result, chars, idx, BAD_TIME);
272 
273     return result;
274   } // parse
275 
276   /////////////////////////// DISALLOWED METHODS ///////////////////////////
277 
278   public boolean hasIsNegative() {
279     return false;
280   }
281 
282   public boolean isNegative() {
283     String err = "org.exolab.castor.types.Time does not have a 'negative' field.";
284     throw new UnsupportedOperationException(err);
285   }
286 
287   public void setNegative() {
288     String err = "org.exolab.castor.types.Time cannot be negative.";
289     throw new UnsupportedOperationException(err);
290   }
291 
292   public boolean hasCentury() {
293     return false;
294   }
295 
296   public short getCentury() {
297     String err = "org.exolab.castor.types.Time does not have a Century field.";
298     throw new UnsupportedOperationException(err);
299   }
300 
301   public void setCentury(short century) {
302     String err = "org.exolab.castor.types.Time does not have a Century field.";
303     throw new UnsupportedOperationException(err);
304   }
305 
306   public boolean hasYear() {
307     return false;
308   }
309 
310   public short getYear() {
311     String err = "org.exolab.castor.types.Time does not have a Year field.";
312     throw new UnsupportedOperationException(err);
313   }
314 
315   public void setYear(short year) {
316     String err = "org.exolab.castor.types.Time does not have a Year field.";
317     throw new UnsupportedOperationException(err);
318   }
319 
320   public boolean hasMonth() {
321     return false;
322   }
323 
324   public short getMonth() {
325     String err = "org.exolab.castor.types.Time does not have a Month field.";
326     throw new UnsupportedOperationException(err);
327   }
328 
329   public void setMonth(short month) {
330     String err = "org.exolab.castor.types.Time does not have a Month field.";
331     throw new UnsupportedOperationException(err);
332   }
333 
334   public boolean hasDay() {
335     return false;
336   }
337 
338   public short getDay() {
339     String err = "org.exolab.castor.types.Time does not have a Day field.";
340     throw new UnsupportedOperationException(err);
341   }
342 
343   public void setDay(short month) {
344     String err = "org.exolab.castor.types.Time does not have a Day field.";
345     throw new UnsupportedOperationException(err);
346   }
347 
348 } // Time