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 }