001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.sql;
019:
020: import java.text.DecimalFormat;
021: import java.text.ParsePosition;
022: import java.text.SimpleDateFormat;
023: import java.util.Date;
024:
025: import org.apache.harmony.sql.internal.nls.Messages;
026:
027: /**
028: * A Java representation of the SQL TIMESTAMP type. It provides the capability
029: * to represent the SQL TIMESTAMP nanosecond value, in addition to the regular
030: * date/time value which has millisecond resolution.
031: * <p>
032: * The Timestamp class consists of a regular Date/Time value, where only the
033: * integral seconds value is stored, plus a nanoseconds value where the
034: * fractional seconds are stored.
035: * <p>
036: * The addition of the nanosecond value field to the Timestamp object makes it
037: * significantly different from the java.util.Date object which it extends.
038: * Users should be cautious in their use of Timestamp objects and should not
039: * assume that they are interchangeable with java.util.Date objects when used
040: * outside the confines of the java.sql package.
041: *
042: */
043: public class Timestamp extends Date {
044:
045: private static final long serialVersionUID = 2745179027874758501L;
046:
047: // The nanoseconds time value of the Timestamp
048: private int nanos;
049:
050: /**
051: * @deprecated Please use the constructor Timestamp( long ) Returns a
052: * Timestamp corresponding to the time specified by the supplied
053: * values for Year, Month, Date, Hour, Minutes, Seconds and
054: * Nanoseconds
055: * @param theYear
056: * specified as the year minus 1900
057: * @param theMonth
058: * specified as an integer in the range 0 - 11
059: * @param theDate
060: * specified as an integer in the range 1 - 31
061: * @param theHour
062: * specified as an integer in the range 0 - 23
063: * @param theMinute
064: * specified as an integer in the range 0 - 59
065: * @param theSecond
066: * specified as an integer in the range 0 - 59
067: * @param theNano
068: * which defines the nanosecond value of the timestamp specified
069: * as an integer in the range 0 - 999,999,999
070: * @throws IllegalArgumentException
071: * if any of the parameters is out of range
072: */
073: @SuppressWarnings("deprecation")
074: @Deprecated
075: public Timestamp(int theYear, int theMonth, int theDate,
076: int theHour, int theMinute, int theSecond, int theNano)
077: throws IllegalArgumentException {
078: super (theYear, theMonth, theDate, theHour, theMinute, theSecond);
079: if (theNano < 0 || theNano > 999999999) {
080: throw new IllegalArgumentException();
081: }
082: nanos = theNano;
083: }
084:
085: /**
086: * Returns a Timestamp object corresponding to the time represented by a
087: * supplied time value.
088: *
089: * @param theTime
090: * a time value in the format of milliseconds since the Epoch
091: * (January 1 1970 00:00:00.000 GMT)
092: */
093: public Timestamp(long theTime) {
094: super (theTime);
095: /*
096: * Now set the time for this Timestamp object - which deals with the
097: * nanosecond value as well as the base time
098: */
099: this .setTime(theTime);
100: }
101:
102: /**
103: * Returns true if this timestamp object is later than the supplied
104: * timestamp, otherwise returns false.
105: *
106: * @param theTimestamp
107: * the timestamp to compare with this timestamp object
108: * @return true if this timestamp object is later than the supplied
109: * timestamp, false otherwise
110: */
111: public boolean after(Timestamp theTimestamp) {
112: long this Time = this .getTime();
113: long compareTime = theTimestamp.getTime();
114:
115: // If the time value is later, the timestamp is later
116: if (this Time > compareTime) {
117: return true;
118: }
119: // If the time value is earlier, the timestamp is not later
120: else if (this Time < compareTime) {
121: return false;
122: }
123: /*
124: * Otherwise the time values are equal in which case the nanoseconds
125: * value determines whether this timestamp is later...
126: */
127: else if (this .getNanos() > theTimestamp.getNanos()) {
128: return true;
129: } else {
130: return false;
131: }
132: }
133:
134: /**
135: * Returns true if this timestamp object is earlier than the supplied
136: * timestamp, otherwise returns false.
137: *
138: * @param theTimestamp
139: * the timestamp to compare with this timestamp object
140: * @return true if this timestamp object is earlier than the supplied
141: * timestamp, false otherwise
142: */
143: public boolean before(Timestamp theTimestamp) {
144: long this Time = this .getTime();
145: long compareTime = theTimestamp.getTime();
146:
147: // If the time value is later, the timestamp is later
148: if (this Time < compareTime) {
149: return true;
150: }
151: // If the time value is earlier, the timestamp is not later
152: else if (this Time > compareTime) {
153: return false;
154: }
155: /*
156: * Otherwise the time values are equal in which case the nanoseconds
157: * value determines whether this timestamp is later...
158: */
159: else if (this .getNanos() < theTimestamp.getNanos()) {
160: return true;
161: } else {
162: return false;
163: }
164: }
165:
166: /**
167: * Compares this Timestamp object with a supplied Timestamp object
168: *
169: * @param theObject
170: * the timestamp to compare with this timestamp object, passed in
171: * as an Object
172: * @return 0 if the two Timestamp objects are equal in time, a value <0 if
173: * this Timestamp object is before the supplied Timestamp and a
174: * value >0 if this Timestamp object is after the supplied Timestamp
175: * @throws ClassCastException
176: * if the supplied object is not a Timestamp object
177: */
178: @Override
179: public int compareTo(Date theObject) throws ClassCastException {
180: return this .compareTo((Timestamp) theObject);
181: }
182:
183: /**
184: * Compares this Timestamp object with a supplied Timestamp object
185: *
186: * @param theTimestamp
187: * the timestamp to compare with this timestamp object, passed in
188: * as a Timestamp
189: * @return 0 if the two Timestamp objects are equal in time, a value <0 if
190: * this Timestamp object is before the supplied Timestamp and a
191: * value >0 if this Timestamp object is after the supplied Timestamp
192: */
193: public int compareTo(Timestamp theTimestamp) {
194: int result = super .compareTo(theTimestamp);
195: if (result == 0) {
196: int this Nano = this .getNanos();
197: int thatNano = theTimestamp.getNanos();
198: if (this Nano > thatNano) {
199: return 1;
200: } else if (this Nano == thatNano) {
201: return 0;
202: } else {
203: return -1;
204: }
205: }
206: return result;
207: }
208:
209: /**
210: * Tests to see if this timestamp is equal to a supplied object.
211: *
212: * @param theObject
213: * @return true if this Timestamp object is equal to the supplied Timestamp
214: * object false if the object is not a Timestamp object or if the
215: * object is a Timestamp but represents a different instant in time
216: */
217: @Override
218: public boolean equals(Object theObject) {
219: if (theObject instanceof Timestamp) {
220: return equals((Timestamp) theObject);
221: }
222: return false;
223: }
224:
225: /**
226: * Tests to see if this timestamp is equal to a supplied timestamp.
227: *
228: * @param theTimestamp
229: * the timestamp to compare with this timestamp object, passed in
230: * as an Object
231: * @return true if this Timestamp object is equal to the supplied Timestamp
232: * object
233: */
234: public boolean equals(Timestamp theTimestamp) {
235: if (theTimestamp == null) {
236: return false;
237: }
238: return (this .getTime() == theTimestamp.getTime())
239: && (this .getNanos() == theTimestamp.getNanos());
240: }
241:
242: /**
243: * Gets this Timestamp's nanosecond value
244: *
245: * @return The timestamp's nanosecond value, an integer between 0 and
246: * 999,999,999
247: */
248: public int getNanos() {
249: return nanos;
250: }
251:
252: /**
253: * Returns the time represented by this Timestamp object, as a long value
254: * containing the number of milliseconds since the Epoch (January 1 1970,
255: * 00:00:00.000 GMT)
256: */
257: @Override
258: public long getTime() {
259: long theTime = super .getTime();
260: theTime = theTime + (nanos / 1000000);
261: return theTime;
262: }
263:
264: /**
265: * Sets the nanosecond value for this timestamp
266: */
267: public void setNanos(int n) throws IllegalArgumentException {
268: if ((n < 0) || (n > 999999999)) {
269: // sql.0=Value out of range
270: throw new IllegalArgumentException(Messages
271: .getString("sql.0")); //$NON-NLS-1$
272: }
273: nanos = n;
274: }
275:
276: /**
277: * Sets the time represented by this Timestamp object to the supplied time,
278: * defined as the number of milliseconds since the Epoch (January 1 1970,
279: * 00:00:00.000 GMT)
280: */
281: @Override
282: public void setTime(long theTime) {
283: /*
284: * Deal with the nanoseconds value. The supplied time is in milliseconds -
285: * so we must extract the milliseconds value and multiply by 1000000 to
286: * get nanoseconds. Things are more complex if theTime value is
287: * negative, since then the time value is the time before the Epoch but
288: * the nanoseconds value of the Timestamp must be positive - so we must
289: * take the "raw" milliseconds value and subtract it from 1000 to get to
290: * the true nanoseconds value Simultaneously, recalculate the time value
291: * to the exact nearest second and reset the Date time value
292: */
293: int milliseconds = (int) (theTime % 1000);
294: theTime = theTime - milliseconds;
295: if (milliseconds < 0) {
296: theTime = theTime - 1000;
297: milliseconds = 1000 + milliseconds;
298: }
299: super .setTime(theTime);
300: setNanos(milliseconds * 1000000);
301: }
302:
303: /**
304: * Returns the timestamp formatted as a String in the JDBC Timestamp Escape
305: * format, which is of the form "yyyy-mm-dd hh:mm:ss.nnnnnnnnn"
306: *
307: * @return A string representing the instant defined by the Timestamp, in
308: * JDBC Timestamp escape format
309: */
310: @SuppressWarnings("deprecation")
311: @Override
312: public String toString() {
313: /*
314: * Use a DecimalFormat to lay out the nanosecond value as a simple
315: * string of 9 integers, with leading Zeros
316: */
317: DecimalFormat decimalFormat = new DecimalFormat("0"); //$NON-NLS-1$
318: decimalFormat.setMinimumIntegerDigits(9);
319: decimalFormat.setMaximumIntegerDigits(9);
320: String theNanos = decimalFormat.format(nanos);
321: theNanos = stripTrailingZeros(theNanos);
322:
323: String year = format((getYear() + 1900), 4);
324: String month = format((getMonth() + 1), 2);
325: String date = format(getDate(), 2);
326: String hours = format(getHours(), 2);
327: String minutes = format(getMinutes(), 2);
328: String seconds = format(getSeconds(), 2);
329:
330: return year + '-' + month + '-' + date + ' ' + hours + ':'
331: + minutes + ':' + seconds + '.' + theNanos;
332: }
333:
334: /*
335: * Private method to format the time
336: */
337: private String format(int date, int digits) {
338: StringBuilder dateStringBuffer = new StringBuilder(String
339: .valueOf(date));
340: while (dateStringBuffer.length() < digits) {
341: dateStringBuffer = dateStringBuffer.insert(0, '0');
342: }
343: return dateStringBuffer.toString();
344: }
345:
346: /*
347: * Private method to strip trailing '0' characters from a string. @param
348: * inputString the starting string @return a string with the trailing zeros
349: * stripped - will leave a single 0 at the beginning of the string
350: */
351: private String stripTrailingZeros(String inputString) {
352: String finalString;
353:
354: int i;
355: for (i = inputString.length(); i > 0; i--) {
356: if (inputString.charAt(i - 1) != '0') {
357: break;
358: }
359: /*
360: * If the string has a 0 as its first character, return a string
361: * with a single '0'
362: */
363: if (i == 1) {
364: return "0"; //$NON-NLS-1$
365: }
366: }
367:
368: finalString = inputString.substring(0, i);
369: return finalString;
370: }
371:
372: /**
373: * Creates a Timestamp object with a time value equal to the time specified
374: * by a supplied String holding the time in JDBC timestamp escape format,
375: * which is of the form "yyyy-mm-dd hh:mm:ss.nnnnnnnnn"
376: *
377: * @param s
378: * the String containing a time in JDBC timestamp escape format
379: * @return A timestamp object with time value as defined by the supplied
380: * String
381: */
382: public static Timestamp valueOf(String s)
383: throws IllegalArgumentException {
384: if (s == null) {
385: // sql.3=Argument cannot be null
386: throw new IllegalArgumentException(Messages
387: .getString("sql.3")); //$NON-NLS-1$
388: }
389:
390: // omit trailing whitespaces
391: s = s.trim();
392:
393: SimpleDateFormat df = new SimpleDateFormat(
394: "yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
395: ParsePosition pp = new ParsePosition(0);
396:
397: /*
398: * First parse out the yyyy-MM-dd HH:mm:ss component of the String into
399: * a Date object using the SimpleDateFormat. This should stop after the
400: * seconds value, according to the definition of SimpleDateFormat.parse,
401: * with the ParsePosition indicating the index of the "." which should
402: * precede the nanoseconds value
403: */
404: Date theDate;
405: try {
406: theDate = df.parse(s, pp);
407: } catch (Exception e) {
408: throw new IllegalArgumentException(Messages
409: .getString("sql.2")); //$NON-NLS-1$
410: }
411:
412: if (theDate == null) {
413: throw new IllegalArgumentException(Messages
414: .getString("sql.2")); //$NON-NLS-1$
415: }
416:
417: /*
418: * If we get here, the Date part of the string was OK - now for the
419: * nanoseconds value. Strictly, this requires the remaining part of the
420: * String to look like ".nnnnnnnnn". However, we accept anything with a
421: * '.' followed by 1 to 9 digits - we also accept nothing (no fractions
422: * of a second). Anything else is interpreted as incorrect format which
423: * will generate an IllegalArgumentException
424: */
425: int position = pp.getIndex();
426: int remaining = s.length() - position;
427: int theNanos;
428:
429: if (remaining == 0) {
430: // First, allow for the case where no fraction of a second is given:
431: theNanos = 0;
432: } else {
433: /*
434: * Case where fraction of a second is specified: Require 1 character
435: * plus the "." in the remaining part of the string...
436: */
437: if ((s.length() - position) < ".n".length()) { //$NON-NLS-1$
438: throw new IllegalArgumentException(Messages
439: .getString("sql.2")); //$NON-NLS-1$
440: }
441:
442: /*
443: * If we're strict, we should not allow any EXTRA characters after
444: * the 9 digits
445: */
446: if ((s.length() - position) > ".nnnnnnnnn".length()) { //$NON-NLS-1$
447: throw new IllegalArgumentException(Messages
448: .getString("sql.2")); //$NON-NLS-1$
449: }
450:
451: // Require the next character to be a "."
452: if (s.charAt(position) != '.') {
453: // sql.4=Bad input string format: expected '.' not {0}
454: throw new NumberFormatException(Messages.getString(
455: "sql.4", s.charAt(position))); //$NON-NLS-1$
456: }
457: // Get the length of the number string - need to account for the '.'
458: int nanoLength = s.length() - position - 1;
459:
460: // Get the 9 characters following the "." as an integer
461: String theNanoString = s.substring(position + 1, position
462: + 1 + nanoLength);
463: /*
464: * We must adjust for the cases where the nanos String was not 9
465: * characters long by padding out with zeros
466: */
467: theNanoString = theNanoString + "000000000"; //$NON-NLS-1$
468: theNanoString = theNanoString.substring(0, 9);
469:
470: try {
471: theNanos = Integer.parseInt(theNanoString);
472: } catch (Exception e) {
473: // If we get here, the string was not a number
474: throw new IllegalArgumentException(Messages
475: .getString("sql.2")); //$NON-NLS-1$
476: }
477: }
478:
479: if (theNanos < 0 || theNanos > 999999999) {
480: throw new IllegalArgumentException(Messages
481: .getString("sql.2")); //$NON-NLS-1$
482: }
483:
484: Timestamp theTimestamp = new Timestamp(theDate.getTime());
485: theTimestamp.setNanos(theNanos);
486:
487: return theTimestamp;
488: }
489: }
|