/*
* Copyright 2006 Edward Kuns
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: $
*/
package org.exolab.castor.types;
import java.text.ParseException;
import java.util.Calendar;
import java.util.GregorianCalendar;
/**
* Describe an XML schema DateTime.
* <p>The format is defined by W3C XML Schema Recommendation and ISO8601
* i.e <tt>(-)CCYY-MM-DD'T'HH:MM:SS(.SSSSS)(Z|(+|-)hh:mm)</tt>
* @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
* @version $Revision: 0000 $
*/
public class DateTime extends DateTimeBase {
/** SerialVersionUID. */
private static final long serialVersionUID = 6278590966410879734L;
/** Complaint string. */
private static final String BAD_DATE = "Bad DateTime format: ";
/**
* Default constructor.
*/
public DateTime() {
// Nothing for the default
}
/**
* Constructs a XML Schema DateTime instance given all the values of the
* different date and time (but not time zone) fields.
* <p>
* By default a DateTime is not UTC, and is local. To set a timezone, you
* need to separately call {@link #setZone(short, short)}.
*
* @param values
* an array of shorts that represent the different fields of
* Time.
* @see #setValues
*/
public DateTime(short[] values) {
setValues(values);
}
/**
* Creates a new XML Schema DateTime instance from a long that represents a
* Date. No time zone information is set.
* <p>
* By default a DateTime is not UTC, and is local. To set a timezone, you
* need to separately call {@link #setZone(short, short)}.
*
* @param dateAsLong
* java.util.Date represented as a long.
*/
public DateTime(long dateAsLong) {
this(new java.util.Date(dateAsLong));
}
/**
* Creates a new XML Schema DateTime instance from a java.util.Date. No time
* zone information is set.
* <p>
* By default a DateTime is not UTC, and is local. To set a timezone, you
* need to separately call {@link #setZone(short, short)}.
*
* @param dateRef
* a java.util.Date to convert.
*/
public DateTime(java.util.Date dateRef) {
GregorianCalendar tempCalendar = new GregorianCalendar();
tempCalendar.setTime(dateRef);
setCentury((short) (tempCalendar.get(Calendar.YEAR) / 100));
setYear((short) (tempCalendar.get(Calendar.YEAR) % 100));
// In GregorianCalendar, 0 <= Month <= 11; January == 0
setMonth((short) (tempCalendar.get(Calendar.MONTH) + 1));
setDay((short) tempCalendar.get(Calendar.DAY_OF_MONTH));
setHour((short) tempCalendar.get(Calendar.HOUR_OF_DAY));
setMinute((short) tempCalendar.get(Calendar.MINUTE));
setSecond((short) tempCalendar.get(Calendar.SECOND), (short) tempCalendar.get(Calendar.MILLISECOND));
}
/**
* Constructs a DateTime from a String. The String is expected to be in W3C
* Schema DateTime format.
*
* @param date
* the string representing the date
* @throws java.text.ParseException
* if we are passed an illegal value
*/
public DateTime(String date) throws java.text.ParseException {
parseDateTimeInternal(date, this);
}
/**
* Sets all the fields to the values provided in an Array. The Array must
* be at least eight entries long. Extra entries are ignored. The order of
* entries in the array is as follows:
* <ul>
* <li>century</li>
* <li>year</li>
* <li>month</li>
* <li>day</li>
* <li>hour</li>
* <li>minute</li>
* <li>second</li>
* <li>millisecond</li>
* </ul>
* If a Time Zone is to be specified, it has to be set separately by using
* {@link DateTimeBase#setZone(short, short) setZone}. A time zone
* previously set will not be cleared.
*
* @param values
* An array of shorts containing the values for the DateTime
*/
public void setValues(short[] values) {
if (values.length != 8) {
throw new IllegalArgumentException("DateTime#setValues: Array length "
+ values.length + " != 8");
}
this.setCentury(values[0]);
this.setYear(values[1]);
this.setMonth(values[2]);
this.setDay(values[3]);
this.setHour(values[4]);
this.setMinute(values[5]);
this.setSecond(values[6], values[7]);
}
/**
* Returns an array of shorts with all the fields that describe this
* DateTime type. The order of entries in the array is as follows:
* <ul>
* <li>century</li>
* <li>year</li>
* <li>month</li>
* <li>day</li>
* <li>hour</li>
* <li>minute</li>
* <li>second</li>
* <li>millisecond</li>
* </ul>
* Note:the time zone is not included.
*
* @return an array of short with all the fields that describe this Date
* type.
*/
public short[] getValues() {
short[] result = new short[8];
result[0] = this.getCentury();
result[1] = this.getYear();
result[2] = this.getMonth();
result[3] = this.getDay();
result[4] = this.getHour();
result[5] = this.getMinute();
result[6] = this.getSeconds();
result[7] = this.getMilli();
return result;
} //getValues
/**
* Converts this DateTime into a local java.util.Date.
* @return a local java.util.Date representing this DateTime.
*/
public java.util.Date toDate() {
Calendar calendar = new GregorianCalendar(getCentury()*100+getYear(), getMonth()-1, getDay(), getHour(), getMinute(), getSeconds());
calendar.set(Calendar.MILLISECOND, getMilli());
setDateFormatTimeZone(calendar);
return calendar.getTime();
} //toDate()
/**
* Converts this DateTime into a long value representing a java.util.Date.
* @return This DateTime instance as a long value representing a java.util.Date.
*/
public long toLong() {
return toDate().getTime();
}
/**
* Converts this DateTime to a string. The format is defined by W3C XML
* Schema recommendation and ISO8601: (+|-)CCYY-MM-DDTHH:MM:SS.SSS(+/-)HH:SS
*
* @return a string representing this Date
*/
public String toString() {
StringBuffer result = new StringBuffer();
appendDateString(result);
result.append('T');
appendTimeString(result);
appendTimeZoneString(result);
return result.toString();
} //toString
/**
* Parses a String into a new DateTime instance.
*
* @param str
* the string to parse
* @return a new DateTime instance with the value of the parsed string.
* @throws ParseException
* If the string to parse does not follow the right format
*/
public static DateTime parse(String str) throws ParseException {
return parseDateTime(str);
}
/**
* Parses a String into a new DateTime instance.
*
* @param str
* the string to parse
* @return a new DateTime instance with the value of the parsed string.
* @throws ParseException
* If the string to parse does not follow the right format
*/
public static DateTime parseDateTime(String str) throws ParseException {
return parseDateTimeInternal(str, new DateTime());
}
/**
* Parses a String into the provided DateTime instance (or a new DateTime
* instance if the one provided is null) and assigns that value to the
* DateTime instance given.
*
* @param str
* the string to parse
* @param result
* the DateTime instance to assign to the parsed value of the
* String. If null is passed, a new DateTime instance is created
* to be returned.
* @return the DateTime instance with the value of the parsed string.
* @throws ParseException
* If the string to parse does not follow the right format
*/
private static DateTime parseDateTimeInternal(String str, DateTime result) throws ParseException {
if (str == null) {
throw new IllegalArgumentException("The string to be parsed must not be null.");
}
if (result == null) {
result = new DateTime();
}
char[] chars = str.toCharArray();
if (chars.length < 19) {
throw new ParseException(BAD_DATE + str + "\nDateTime is not long enough", 0);
}
int idx = 0;
idx = parseYear(str, result, chars, idx, BAD_DATE);
idx = parseMonth(str, result, chars, idx, BAD_DATE);
idx = parseDay(str, result, chars, idx, BAD_DATE);
if (chars[idx] != 'T') {
throw new ParseException(BAD_DATE + str + "\n 'T' " + DateTimeBase.WRONGLY_PLACED, idx);
}
idx++;
idx = parseTime(str, result, chars, idx, BAD_DATE);
parseTimeZone(str, result, chars, idx, BAD_DATE);
return result;
} //parse
}