001: /*
002: * $Id: Timestamp.java,v 1.41 2007/09/18 11:21:04 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common.types.standard;
008:
009: import java.text.ParseException;
010: import java.text.SimpleDateFormat;
011:
012: import java.util.Calendar;
013:
014: import org.xins.common.MandatoryArgumentChecker;
015: import org.xins.common.types.Type;
016: import org.xins.common.types.TypeValueException;
017:
018: /**
019: * Standard type <em>_timestamp</em>. A value of this type represents a
020: * certain moment in time, with second-precision, without an indication of the
021: * time zone.
022: *
023: * <p>The textual representation of a timestamp is always 14 numeric
024: * characters, in the format:
025: *
026: * <blockquote><em>YYYYMMDDhhmmss</em></blockquote>
027: *
028: * where:
029: *
030: * <ul>
031: * <li><em>YYYY</em> is the year, including the century, between 1970 and
032: * 2999, for example <code>"2005"</code>.
033: * <li><em>MM</em> is the month of the year, 1-based, for example
034: * <code>"12"</code> for December.
035: * <li><em>DD</em> is the day of the month, 1-based, for example
036: * <code>"31"</code> for the last day of December.
037: * <li><em>hh</em> is the hour of the day, 0-based, for example
038: * <code>"23"</code> for the last hour of the day.
039: * <li><em>mm</em> is the minute within the hour, 0-based, for example
040: * <code>"59"</code> for the last minute within the hour.
041: * <li><em>ss</em> is the second within the minute, 0-based, for example
042: * <code>"59"</code> for the last second within the minute.
043: * </ul>
044: *
045: * <p>Note that all timestamps will be based on the current time zone (see
046: * {@link java.util.TimeZone#getDefault()}).
047: *
048: * <p>A number of milliseconds can be used to indicate a specific instant in
049: * time. This number of milliseconds is since the
050: * <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
051: *
052: * @version $Revision: 1.41 $ $Date: 2007/09/18 11:21:04 $
053: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
054: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
055: *
056: * @since XINS 1.0.0
057: */
058: public class Timestamp extends Type {
059:
060: /**
061: * The only instance of this class. This field is never <code>null</code>.
062: */
063: public static final Timestamp SINGLETON = new Timestamp();
064:
065: /**
066: * Formatter that converts a date to a string.
067: */
068: private static final SimpleDateFormat FORMATTER = new SimpleDateFormat(
069: "yyyyMMddHHmmss");
070:
071: /**
072: * Constructs a new <code>Timestamp</code> instance.
073: * This constructor is private, the field {@link #SINGLETON} should be
074: * used.
075: */
076: private Timestamp() {
077: super ("_timestamp", Value.class);
078: }
079:
080: /**
081: * Constructs a <code>Timestamp.Value</code> with the value of the current
082: * time.
083: *
084: * @return
085: * the {@link Value} initialized with the current time,
086: * never <code>null</code>.
087: */
088: public static Value now() {
089: return new Value(System.currentTimeMillis());
090: }
091:
092: /**
093: * Constructs a <code>Timestamp.Value</code> from the specified
094: * non-<code>null</code> string.
095: *
096: * @param string
097: * the string to convert, cannot be <code>null</code>.
098: *
099: * @return
100: * the {@link Value} object, never <code>null</code>.
101: *
102: * @throws IllegalArgumentException
103: * if <code>string == null</code>.
104: *
105: * @throws TypeValueException
106: * if the specified string does not represent a valid value for this
107: * type.
108: */
109: public static Value fromStringForRequired(String string)
110: throws IllegalArgumentException, TypeValueException {
111:
112: // Check preconditions
113: MandatoryArgumentChecker.check("string", string);
114:
115: return (Value) SINGLETON.fromString(string);
116: }
117:
118: /**
119: * Constructs a <code>Timestamp.Value</code> from the specified string.
120: *
121: * @param string
122: * the string to convert, can be <code>null</code>.
123: *
124: * @return
125: * the {@link Value}, or <code>null</code> if
126: * <code>string == null</code>.
127: *
128: * @throws TypeValueException
129: * if the specified string does not represent a valid value for this
130: * type.
131: */
132: public static Value fromStringForOptional(String string)
133: throws TypeValueException {
134: return (Value) SINGLETON.fromString(string);
135: }
136:
137: /**
138: * Converts the specified <code>Timestamp.Value</code> to a string.
139: *
140: * @param value
141: * the value to convert, can be <code>null</code>.
142: *
143: * @return
144: * the textual representation of the value;
145: * or <code>null</code> if and only if <code>value == null</code>.
146: */
147: public static String toString(Value value) {
148:
149: // Short-circuit if the argument is null
150: if (value == null) {
151: return null;
152: }
153:
154: return toString(value.getYear(), value.getMonthOfYear(), value
155: .getDayOfMonth(), value.getHourOfDay(), value
156: .getMinuteOfHour(), value.getSecondOfMinute());
157: }
158:
159: /**
160: * Converts the specified combination of a year, month, day, hour,
161: * minute and second to a string.
162: *
163: * @param year
164: * the year, must be >=1970 and <= 2999.
165: *
166: * @param month
167: * the month of the year, must be >= 1 and <= 12.
168: *
169: * @param day
170: * the day of the month, must be >= 1 and <= 31.
171: *
172: * @param hour
173: * the hour of the day, must be >= 0 and <= 23.
174: *
175: * @param minute
176: * the minute of the hour, must be >= 0 and <= 59.
177: *
178: * @param second
179: * the second of the minute, must be >= 0 and <= 59.
180: *
181: * @return
182: * the textual representation of the value in the format
183: * <em>YYYYMMDDhhmmss</em>, never <code>null</code>.
184: */
185: private static String toString(int year, int month, int day,
186: int hour, int minute, int second) {
187:
188: // Use a buffer to create the string
189: StringBuffer buffer = new StringBuffer(14);
190:
191: // Append the year
192: buffer.append(year);
193:
194: // Append the month
195: if (month < 10) {
196: buffer.append('0');
197: }
198: buffer.append(month);
199:
200: // Append the day
201: if (day < 10) {
202: buffer.append('0');
203: }
204: buffer.append(day);
205:
206: // Append the hour
207: if (hour < 10) {
208: buffer.append('0');
209: }
210: buffer.append(hour);
211:
212: // Append the minute
213: if (minute < 10) {
214: buffer.append('0');
215: }
216: buffer.append(minute);
217:
218: // Append the second
219: if (second < 10) {
220: buffer.append('0');
221: }
222: buffer.append(second);
223:
224: return buffer.toString();
225: }
226:
227: protected final boolean isValidValueImpl(String value) {
228:
229: // First check the length
230: if (value.length() != 14) {
231: return false;
232: }
233:
234: // Convert all 3 components of the string to integers
235: int y, m, d, h, mi, s;
236: try {
237: y = Integer.parseInt(value.substring(0, 4));
238: m = Integer.parseInt(value.substring(4, 6));
239: d = Integer.parseInt(value.substring(6, 8));
240: h = Integer.parseInt(value.substring(8, 10));
241: mi = Integer.parseInt(value.substring(10, 12));
242: s = Integer.parseInt(value.substring(12, 14));
243: } catch (NumberFormatException nfe) {
244: return false;
245: }
246:
247: // Check that the values are in the correct range
248: return (y >= 0) && (m >= 1) && (m <= 12) && (d >= 1)
249: && (d <= 31) && (h >= 0) && (h <= 23) && (mi >= 0)
250: && (mi <= 59) && (s >= 0) && (s <= 59);
251: }
252:
253: protected final Object fromStringImpl(String string)
254: throws TypeValueException {
255:
256: SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
257: java.util.Date date;
258: try {
259: date = format.parse(string);
260: } catch (ParseException exception) {
261: throw new TypeValueException(this , string); // XXX: Add detail?
262: }
263:
264: return new Value(date);
265: }
266:
267: public final String toString(Object value)
268: throws IllegalArgumentException, ClassCastException,
269: TypeValueException {
270:
271: // Check preconditions
272: MandatoryArgumentChecker.check("value", value);
273:
274: // Convert the Value object to a String
275: return toString((Value) value);
276: }
277:
278: public String getDescription() {
279: return "A timestamp. The format is YYYYMMDDhhmmss.";
280: }
281:
282: /**
283: * Value for the type <em>_timestamp</em>. Represents a specific moment in
284: * time, with second-precision.
285: *
286: * @version $Revision: 1.41 $
287: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
288: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
289: *
290: * @since XINS 1.0.0
291: */
292: public static final class Value implements Cloneable {
293:
294: /**
295: * Calendar representing the moment in time.
296: */
297: private Calendar _calendar;
298:
299: /**
300: * Constructs a new timestamp value. The values will not be checked.
301: *
302: * @param year
303: * the year, including century, e.g. <code>2005</code>.
304: *
305: * @param month
306: * the month of the year in the range 1-12, e.g. <code>11</code> for
307: * November.
308: *
309: * @param day
310: * the day of the month in the range 1-31, e.g. <code>1</code> for
311: * the first day of the month.
312: *
313: * @param hour
314: * the hour of the day in the range 0-23, e.g. <code>22</code> for 10
315: * o'clock at night.
316: *
317: * @param minute
318: * the minute of the hour in the range 0-59, e.g. <code>0</code> for
319: * first minute of the hour.
320: *
321: * @param second
322: * the second of the minute in the range 0-59, e.g. <code>0</code>
323: * for the first second of the minute.
324: */
325: public Value(int year, int month, int day, int hour,
326: int minute, int second) {
327:
328: // Construct the Calendar
329: _calendar = Calendar.getInstance();
330: _calendar.set(year, month - 1, day, hour, minute, second);
331: }
332:
333: /**
334: * Constructs a new timestamp value based on the specified
335: * <code>Calendar</code>.
336: *
337: * @param calendar
338: * the {@link java.util.Calendar} object to get the exact date from,
339: * cannot be <code>null</code>.
340: *
341: * @throws IllegalArgumentException
342: * if <code>calendar == null</code>.
343: *
344: * @since XINS 1.2.0
345: */
346: public Value(Calendar calendar) throws IllegalArgumentException {
347:
348: // Check preconditions
349: MandatoryArgumentChecker.check("calendar", calendar);
350:
351: // Initialize fields
352: _calendar = (Calendar) calendar.clone();
353: }
354:
355: /**
356: * Constructs a new timestamp value based on the specified
357: * <code>java.util.Date</code> object.
358: *
359: * @param date
360: * the {@link java.util.Date} object to get the exact date from,
361: * cannot be <code>null</code>.
362: *
363: * @throws IllegalArgumentException
364: * if <code>date == null</code>.
365: *
366: * @since XINS 1.2.0
367: */
368: public Value(java.util.Date date)
369: throws IllegalArgumentException {
370:
371: // Check preconditions
372: MandatoryArgumentChecker.check("date", date);
373:
374: // Construct the Calendar
375: _calendar = Calendar.getInstance();
376: _calendar.setTime(date);
377: }
378:
379: /**
380: * Constructs a new timestamp value based on the specified number of
381: * milliseconds since the UNIX Epoch.
382: *
383: * @param millis
384: * the number of milliseconds since the
385: * <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
386: *
387: * @throws IllegalArgumentException
388: * if <code>millis < 0L</code>.
389: *
390: * @see System#currentTimeMillis()
391: *
392: * @since XINS 1.2.0
393: */
394: public Value(long millis) throws IllegalArgumentException {
395:
396: // Check preconditions
397: if (millis < 0L) {
398: throw new IllegalArgumentException("millis (" + millis
399: + " < 0L");
400: }
401:
402: // Convert the number of milliseconds to a Date object
403: java.util.Date date = new java.util.Date(millis);
404:
405: // Construct the Calendar
406: _calendar = Calendar.getInstance();
407: _calendar.setTime(date);
408: }
409:
410: /**
411: * Creates and returns a copy of this object.
412: *
413: * @return
414: * a copy of this object, never <code>null</code>.
415: *
416: * @see Object#clone()
417: */
418: public Object clone() {
419: return new Value(_calendar);
420: }
421:
422: /**
423: * Returns the year.
424: *
425: * @return
426: * the year, between 1970 and 2999 (inclusive).
427: */
428: public int getYear() {
429: return _calendar.get(Calendar.YEAR);
430: }
431:
432: /**
433: * Returns the month of the year.
434: *
435: * @return
436: * the month of the year, between 1 and 12 (inclusive).
437: */
438: public int getMonthOfYear() {
439: return _calendar.get(Calendar.MONTH) + 1;
440: }
441:
442: /**
443: * Returns the day of the month.
444: *
445: * @return
446: * the day of the month, between 1 and 31 (inclusive).
447: */
448: public int getDayOfMonth() {
449: return _calendar.get(Calendar.DAY_OF_MONTH);
450: }
451:
452: /**
453: * Returns the hour of the day.
454: *
455: * @return
456: * the hour of the day, between 0 and 23 (inclusive).
457: */
458: public int getHourOfDay() {
459: return _calendar.get(Calendar.HOUR_OF_DAY);
460: }
461:
462: /**
463: * Returns the minute of the hour.
464: *
465: * @return
466: * the minute of the hour, between 0 and 59 (inclusive).
467: */
468: public int getMinuteOfHour() {
469: return _calendar.get(Calendar.MINUTE);
470: }
471:
472: /**
473: * Returns the second of the minute.
474: *
475: * @return
476: * the second of the minute, between 0 and 59 (inclusive).
477: */
478: public int getSecondOfMinute() {
479: return _calendar.get(Calendar.SECOND);
480: }
481:
482: public boolean equals(Object obj) {
483: if (!(obj instanceof Value)) {
484: return false;
485: }
486:
487: // Compare relevant values
488: Value that = (Value) obj;
489: return (getYear() == that.getYear())
490: && (getMonthOfYear() == that.getMonthOfYear())
491: && (getDayOfMonth() == that.getDayOfMonth())
492: && (getHourOfDay() == that.getHourOfDay())
493: && (getMinuteOfHour() == that.getMinuteOfHour())
494: && (getSecondOfMinute() == that.getSecondOfMinute());
495: }
496:
497: public int hashCode() {
498: return _calendar.hashCode();
499: }
500:
501: /**
502: * Converts to a <code>java.util.Date</code> object.
503: *
504: * @return
505: * the {@link java.util.Date} corresponding to this value.
506: *
507: * @since XINS 1.2.0
508: */
509: public java.util.Date toDate() {
510: return _calendar.getTime();
511: }
512:
513: /**
514: * Returns a textual representation of this object.
515: *
516: * @return
517: * the textual representation of this timestamp, never
518: * <code>null</code>.
519: */
520: public String toString() {
521: synchronized (FORMATTER) {
522: return FORMATTER.format(_calendar.getTime());
523: }
524: }
525: }
526: }
|