001: /*
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2000 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: Date.java 6565 2006-12-22 16:05:26Z ekuns $
044: * Date Author Changes
045: * 10/02/2002 Arnaud Clean up
046: * 04/18/2002 Arnaud String constructor
047: * 05/23/2001 Arnaud Blandin Update to be compliant with XML Schema Recommendation
048: * 11/16/2000 Arnaud Blandin Constructor Date(java.util.Date)
049: * 11/01/2000 Arnaud Blandin Enhancements (constructor, methods access...)
050: * 10/23/2000 Arnaud Blandin Created
051: */
052: package org.exolab.castor.types;
053:
054: import java.text.ParseException;
055: import java.util.Calendar;
056: import java.util.GregorianCalendar;
057:
058: /**
059: * Describe an XML Schema Date.
060: * <p>
061: * The format is defined by W3C XML Schema Recommendation and ISO8601 i.e
062: * <tt>(-)CCYY-MM-DD(Z|(+|-)hh:mm)</tt>
063: *
064: * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
065: * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
066: * @version $Revision: 6565 $
067: */
068: public class Date extends DateTimeBase {
069: /** SerialVersionUID */
070: private static final long serialVersionUID = -1634875709019365137L;
071: /** Complaint string. */
072: private static final String BAD_DATE = "Bad Date format: ";
073:
074: /**
075: * No-arg constructor.
076: */
077: public Date() {
078: // Nothing to do
079: }
080:
081: /**
082: * Constructs a XML Schema Date instance given all the values of the
083: * different fields. By default a Date is not UTC and is local.
084: *
085: * @param values
086: * an array of shorts that represent the different fields of
087: * Time.
088: */
089: public Date(short[] values) {
090: setValues(values);
091: }
092:
093: /**
094: * This constructor is used to convert a long value representing a Date to a
095: * new org.exolab.castor.types.Date instance.
096: * <p>
097: * Note : all the information concerning the time part of the java.util.Date
098: * is lost since a W3C Schema Date only represents CCYY-MM-YY
099: *
100: * @param dateAsLong
101: * Date represented in from of a long value.
102: */
103: public Date(long dateAsLong) {
104: this (new java.util.Date(dateAsLong));
105: }
106:
107: /**
108: * This constructor is used to convert a java.util.Date into a new
109: * org.exolab.castor.types.Date.
110: * <p>
111: * Note : all the information concerning the time part of the java.util.Date
112: * is lost since a W3C Schema Date only represents CCYY-MM-YY.
113: *
114: * @param dateRef
115: * the java.util.Date to use to construct a new
116: * org.exolab.castor.types.Date
117: */
118: public Date(java.util.Date dateRef) {
119: GregorianCalendar tempCalendar = new GregorianCalendar();
120: tempCalendar.setTime(dateRef);
121: setCentury((short) (tempCalendar.get(Calendar.YEAR) / 100));
122: setYear((short) (tempCalendar.get(Calendar.YEAR) % 100));
123:
124: //we need to add 1 to the Month value returned by GregorianCalendar
125: //because 0<MONTH<11 (i.e January is 0)
126: setMonth((short) (tempCalendar.get(Calendar.MONTH) + 1));
127: setDay((short) tempCalendar.get(Calendar.DAY_OF_MONTH));
128: } //Date(java.util.Date)
129:
130: /**
131: * Constructs a date from a string.
132: * @param date the string representing the date
133: * @throws ParseException a parse exception is thrown if the string to parse
134: * does not follow the right format (see the description
135: * of this class)
136: */
137: public Date(String date) throws java.text.ParseException {
138: parseDateInternal(date, this );
139: }
140:
141: /**
142: * Sets all the fields by reading the values in an array.
143: * <p>
144: * If a Time Zone is specified, it has to be set by using
145: * {@link DateTimeBase#setZone(short, short) setZone}.
146: *
147: * @param values
148: * an array of shorts with the values the array is supposed to be
149: * of length 4 and ordered like the following:
150: * <ul>
151: * <li>century</li>
152: * <li>year</li>
153: * <li>month</li>
154: * <li>day</li>
155: * </ul>
156: */
157: public void setValues(short[] values) {
158: if (values.length != 4) {
159: throw new IllegalArgumentException(
160: "Date#setValues: not the right number of values");
161: }
162: this .setCentury(values[0]);
163: this .setYear(values[1]);
164: this .setMonth(values[2]);
165: this .setDay(values[3]);
166: }
167:
168: /**
169: * Returns an array of short with all the fields that describe this Date
170: * type.
171: * <p>
172: * Note:the time zone is not included.
173: *
174: * @return an array of short with all the fields that describe this Date
175: * type.
176: */
177: public short[] getValues() {
178: short[] result = new short[4];
179: result[0] = this .getCentury();
180: result[1] = this .getYear();
181: result[2] = this .getMonth();
182: result[3] = this .getDay();
183: return result;
184: } //getValues
185:
186: /**
187: * Converts this Date into a local java.util.Date.
188: * @return a local date representing this Date.
189: */
190: public java.util.Date toDate() {
191: Calendar calendar = new GregorianCalendar(getCentury() * 100
192: + getYear(), getMonth() - 1, getDay());
193: setDateFormatTimeZone(calendar);
194: return calendar.getTime();
195: } //toDate()
196:
197: /**
198: * Converts this date into a long value.
199: * @return This date instance as a long value.
200: */
201: public long toLong() {
202: return this .toDate().getTime();
203: }
204:
205: /**
206: * convert this Date to a string
207: * The format is defined by W3C XML Schema recommendation and ISO8601
208: * i.e (+|-)CCYY-MM-DD
209: * @return a string representing this Date
210: */
211: public String toString() {
212: StringBuffer result = new StringBuffer();
213:
214: appendDateString(result);
215: appendTimeZoneString(result);
216:
217: return result.toString();
218: } //toString
219:
220: /**
221: * parse a String and convert it into an java.lang.Object
222: * @param str the string to parse
223: * @return an Object represented by the string
224: * @throws ParseException a parse exception is thrown if the string to parse
225: * does not follow the right format (see the description
226: * of this class)
227: */
228: public static Object parse(String str) throws ParseException {
229: return parseDate(str);
230: }
231:
232: /**
233: * parse a String and convert it into a Date.
234: * @param str the string to parse
235: * @return the Date represented by the string
236: * @throws ParseException a parse exception is thrown if the string to parse
237: * does not follow the right format (see the description
238: * of this class)
239: */
240: public static Date parseDate(String str) throws ParseException {
241: Date result = new Date();
242: return parseDateInternal(str, result);
243: }
244:
245: private static Date parseDateInternal(String str, Date result)
246: throws ParseException {
247: if (str == null) {
248: throw new IllegalArgumentException(
249: "The string to be parsed must not be null.");
250: }
251:
252: if (result == null) {
253: result = new Date();
254: }
255:
256: char[] chars = str.toCharArray();
257:
258: // Year
259: int idx = 0;
260: if (chars[idx] == '-') {
261: idx++;
262: result.setNegative();
263: }
264:
265: if (!Character.isDigit(chars[idx])
266: || !Character.isDigit(chars[idx + 1])
267: || !Character.isDigit(chars[idx + 2])
268: || !Character.isDigit(chars[idx + 3])) {
269: throw new ParseException(BAD_DATE + "'" + str
270: + "'\nThe Year must be 4 digits long", idx);
271: }
272:
273: short value1;
274: short value2;
275:
276: value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
277: value2 = (short) ((chars[idx + 2] - '0') * 10 + (chars[idx + 3] - '0'));
278:
279: if (value1 == 0 && value2 == 0) {
280: throw new ParseException(BAD_DATE + str
281: + "\n'0000' is not allowed as a year.", idx);
282: }
283:
284: result.setCentury(value1);
285: result.setYear(value2);
286:
287: idx += 4;
288:
289: // Month
290: if (chars[idx] != '-') {
291: throw new ParseException(BAD_DATE + "'" + str + "'\n '-' "
292: + DateTimeBase.WRONGLY_PLACED, idx);
293: }
294:
295: idx++;
296:
297: if (!Character.isDigit(chars[idx])
298: || !Character.isDigit(chars[idx + 1])) {
299: throw new ParseException(BAD_DATE + "'" + str
300: + "'\nThe Month must be 2 digits long", idx);
301: }
302:
303: value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
304: result.setMonth(value1);
305:
306: idx += 2;
307:
308: // Day
309: if (chars[idx] != '-') {
310: throw new ParseException(BAD_DATE + "'" + str + "'\n '-' "
311: + DateTimeBase.WRONGLY_PLACED, idx);
312: }
313:
314: idx++;
315:
316: if (!Character.isDigit(chars[idx])
317: || !Character.isDigit(chars[idx + 1])) {
318: throw new ParseException(BAD_DATE + "'" + str
319: + "'\nThe Day must be 2 digits long", idx);
320: }
321:
322: value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
323: result.setDay(value1);
324:
325: idx += 2;
326:
327: parseTimeZone(str, result, chars, idx, BAD_DATE);
328:
329: return result;
330: } //parse
331:
332: /////////////////////////// DISALLOWED METHODS ///////////////////////////
333:
334: public boolean hasHour() {
335: return false;
336: }
337:
338: public short getHour() {
339: String err = "org.exolab.castor.types.Date does not have an Hour field.";
340: throw new OperationNotSupportedException(err);
341: }
342:
343: public void setHour(short hour) {
344: String err = "org.exolab.castor.types.Date does not have an Hour field.";
345: throw new OperationNotSupportedException(err);
346: }
347:
348: public boolean hasMinute() {
349: return false;
350: }
351:
352: public short getMinute() {
353: String err = "org.exolab.castor.types.Date does not have a Minute field.";
354: throw new OperationNotSupportedException(err);
355: }
356:
357: public void setMinute(short minute) {
358: String err = "org.exolab.castor.types.Date does not have a Minute field.";
359: throw new OperationNotSupportedException(err);
360: }
361:
362: public boolean hasSeconds() {
363: return false;
364: }
365:
366: public short getSeconds() {
367: String err = "org.exolab.castor.types.Date does not have a Seconds field.";
368: throw new OperationNotSupportedException(err);
369: }
370:
371: public void setSecond(short second) {
372: String err = "org.exolab.castor.types.Date does not have a Seconds field.";
373: throw new OperationNotSupportedException(err);
374: }
375:
376: public boolean hasMilli() {
377: return false;
378: }
379:
380: public short getMilli() {
381: String err = "org.exolab.castor.types.Date does not have a Milliseconds field.";
382: throw new OperationNotSupportedException(err);
383: }
384:
385: public void setMilliSecond(short millisecond) {
386: String err = "org.exolab.castor.types.Date does not have a Milliseconds field.";
387: throw new OperationNotSupportedException(err);
388: }
389:
390: }// Date
|