• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package java.sql;
19 
20 import java.text.ParsePosition;
21 import java.text.SimpleDateFormat;
22 import java.util.Date;
23 import java.util.regex.Pattern;
24 
25 /**
26  * A Java representation of the SQL {@code TIMESTAMP} type. It provides the
27  * capability of representing the SQL {@code TIMESTAMP} nanosecond value, in
28  * addition to the regular date/time value which has millisecond resolution.
29  * <p>
30  * The {@code Timestamp} class consists of a regular date/time value, where only
31  * the integral seconds value is stored, plus a nanoseconds value where the
32  * fractional seconds are stored.
33  * <p>
34  * The addition of the nanosecond value field to the {@code Timestamp} object
35  * makes it significantly different from the {@code java.util.Date} object which
36  * it extends. Users should be aware that {@code Timestamp} objects are not
37  * interchangable with {@code java.util.Date} objects when used outside the
38  * confines of the {@code java.sql} package.
39  *
40  * @see Date
41  * @see Time
42  * @see java.util.Date
43  */
44 public class Timestamp extends Date {
45 
46     private static final long serialVersionUID = 2745179027874758501L;
47 
48     // The nanoseconds time value of the Timestamp
49     private int nanos;
50 
51     // The regex pattern of yyyy-MM-dd HH:mm:ss
52     private static final String TIME_FORMAT_REGEX = "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.*";
53 
54     /**
55      * Returns a {@code Timestamp} corresponding to the time specified by the
56      * supplied values for <i>Year</i>, <i>Month</i>, <i>Date</i>, <i>Hour</i>,
57      * <i>Minutes</i>, <i>Seconds</i> and <i>Nanoseconds</i>.
58      *
59      * @deprecated Use the constructor {@link #Timestamp(long)}.
60      * @param theYear
61      *            specified as the year minus 1900.
62      * @param theMonth
63      *            specified as an integer in the range [0,11].
64      * @param theDate
65      *            specified as an integer in the range [1,31].
66      * @param theHour
67      *            specified as an integer in the range [0,23].
68      * @param theMinute
69      *            specified as an integer in the range [0,59].
70      * @param theSecond
71      *            specified as an integer in the range [0,59].
72      * @param theNano
73      *            which defines the nanosecond value of the timestamp specified
74      *            as an integer in the range [0,999'999'999]
75      * @throws IllegalArgumentException
76      *             if any of the parameters is out of range.
77      */
78     @SuppressWarnings("deprecation")
79     @Deprecated
Timestamp(int theYear, int theMonth, int theDate, int theHour, int theMinute, int theSecond, int theNano)80     public Timestamp(int theYear, int theMonth, int theDate, int theHour,
81             int theMinute, int theSecond, int theNano)
82             throws IllegalArgumentException {
83         super(theYear, theMonth, theDate, theHour, theMinute, theSecond);
84         if (theNano < 0 || theNano > 999999999) {
85             throw new IllegalArgumentException();
86         }
87         nanos = theNano;
88     }
89 
90     /**
91      * Returns a {@code Timestamp} object corresponding to the time represented
92      * by a supplied time value.
93      *
94      * @param theTime
95      *            a time value in the format of milliseconds since the Epoch
96      *            (January 1 1970 00:00:00.000 GMT).
97      */
Timestamp(long theTime)98     public Timestamp(long theTime) {
99         super(theTime);
100         /*
101          * Now set the time for this Timestamp object - which deals with the
102          * nanosecond value as well as the base time
103          */
104         setTimeImpl(theTime);
105     }
106 
107     /**
108      * Returns {@code true} if this timestamp object is later than the supplied
109      * timestamp, otherwise returns {@code false}.
110      *
111      * @param theTimestamp
112      *            the timestamp to compare with this timestamp object.
113      * @return {@code true} if this {@code Timestamp} object is later than the
114      *         supplied timestamp, {@code false} otherwise.
115      */
after(Timestamp theTimestamp)116     public boolean after(Timestamp theTimestamp) {
117         long thisTime = this.getTime();
118         long compareTime = theTimestamp.getTime();
119 
120         // If the time value is later, the timestamp is later
121         if (thisTime > compareTime) {
122             return true;
123         }
124         // If the time value is earlier, the timestamp is not later
125         else if (thisTime < compareTime) {
126             return false;
127         }
128         /*
129          * Otherwise the time values are equal in which case the nanoseconds
130          * value determines whether this timestamp is later...
131          */
132         else if (this.getNanos() > theTimestamp.getNanos()) {
133             return true;
134         } else {
135             return false;
136         }
137     }
138 
139     /**
140      * Returns {@code true} if this {@code Timestamp} object is earlier than the
141      * supplied timestamp, otherwise returns {@code false}.
142      *
143      * @param theTimestamp
144      *            the timestamp to compare with this {@code Timestamp} object.
145      * @return {@code true} if this {@code Timestamp} object is earlier than the
146      *         supplied timestamp, {@code false} otherwise.
147      */
before(Timestamp theTimestamp)148     public boolean before(Timestamp theTimestamp) {
149         long thisTime = this.getTime();
150         long compareTime = theTimestamp.getTime();
151 
152         // If the time value is later, the timestamp is later
153         if (thisTime < compareTime) {
154             return true;
155         }
156         // If the time value is earlier, the timestamp is not later
157         else if (thisTime > compareTime) {
158             return false;
159         }
160         /*
161          * Otherwise the time values are equal in which case the nanoseconds
162          * value determines whether this timestamp is later...
163          */
164         else if (this.getNanos() < theTimestamp.getNanos()) {
165             return true;
166         } else {
167             return false;
168         }
169     }
170 
171     /**
172      * Compares this {@code Timestamp} object with a supplied {@code Timestamp}
173      * object.
174      *
175      * @param theObject
176      *            the timestamp to compare with this {@code Timestamp} object,
177      *            passed as an {@code Object}.
178      * @return <dd>
179      *         <dl>
180      *         {@code 0} if the two {@code Timestamp} objects are equal in time
181      *         </dl>
182      *         <dl>
183      *         a value {@code < 0} if this {@code Timestamp} object is before
184      *         the supplied {@code Timestamp} and a value
185      *         </dl>
186      *         <dl>
187      *         {@code > 0} if this {@code Timestamp} object is after the
188      *         supplied {@code Timestamp}
189      *         </dl>
190      *         </dd>
191      * @throws ClassCastException
192      *             if the supplied object is not a {@code Timestamp} object.
193      */
194     @Override
compareTo(Date theObject)195     public int compareTo(Date theObject) throws ClassCastException {
196         return this.compareTo((Timestamp) theObject);
197     }
198 
199     /**
200      * Compares this {@code Timestamp} object with a supplied {@code Timestamp}
201      * object.
202      *
203      * @param theTimestamp
204      *            the timestamp to compare with this {@code Timestamp} object,
205      *            passed in as a {@code Timestamp}.
206      * @return one of the following:
207      *         <ul>
208      *         <li>{@code 0}, if the two {@code Timestamp} objects are
209      *         equal in time</li>
210      *         <li>{@code < 0}, if this {@code Timestamp} object is before the
211      *         supplied {@code Timestamp}</li>
212      *         <li> {@code > 0}, if this {@code Timestamp} object is after the
213      *         supplied {@code Timestamp}</li>
214      *         </ul>
215      */
compareTo(Timestamp theTimestamp)216     public int compareTo(Timestamp theTimestamp) {
217         int result = super.compareTo(theTimestamp);
218         if (result == 0) {
219             int thisNano = this.getNanos();
220             int thatNano = theTimestamp.getNanos();
221             if (thisNano > thatNano) {
222                 return 1;
223             } else if (thisNano == thatNano) {
224                 return 0;
225             } else {
226                 return -1;
227             }
228         }
229         return result;
230     }
231 
232     /**
233      * Tests to see if this timestamp is equal to a supplied object.
234      *
235      * @param theObject
236      *            the object to which this timestamp is compared.
237      * @return {@code true} if this {@code Timestamp} object is equal to the
238      *         supplied {@code Timestamp} object<br>{@code false} if the object
239      *         is not a {@code Timestamp} object or if the object is a {@code
240      *         Timestamp} but represents a different instant in time.
241      */
242     @Override
equals(Object theObject)243     public boolean equals(Object theObject) {
244         if (theObject instanceof Timestamp) {
245             return equals((Timestamp) theObject);
246         }
247         return false;
248     }
249 
250     /**
251      * Tests to see if this timestamp is equal to a supplied timestamp.
252      *
253      * @param theTimestamp
254      *            the timestamp to compare with this {@code Timestamp} object,
255      *            passed as an {@code Object}.
256      * @return {@code true} if this {@code Timestamp} object is equal to the
257      *         supplied {@code Timestamp} object, {@code false} otherwise.
258      */
equals(Timestamp theTimestamp)259     public boolean equals(Timestamp theTimestamp) {
260         if (theTimestamp == null) {
261             return false;
262         }
263         return (this.getTime() == theTimestamp.getTime())
264                 && (this.getNanos() == theTimestamp.getNanos());
265     }
266 
267     /**
268      * Gets this {@code Timestamp}'s nanosecond value
269      *
270      * @return The timestamp's nanosecond value, an integer between 0 and
271      *         999,999,999.
272      */
getNanos()273     public int getNanos() {
274         return nanos;
275     }
276 
277     /**
278      * Returns the time represented by this {@code Timestamp} object, as a long
279      * value containing the number of milliseconds since the Epoch (January 1
280      * 1970, 00:00:00.000 GMT).
281      *
282      * @return the number of milliseconds that have passed since January 1 1970,
283      *         00:00:00.000 GMT.
284      */
285     @Override
getTime()286     public long getTime() {
287         long theTime = super.getTime();
288         theTime = theTime + (nanos / 1000000);
289         return theTime;
290     }
291 
292     /**
293      * Sets the nanosecond value for this {@code Timestamp}.
294      *
295      * @param n
296      *            number of nanoseconds.
297      * @throws IllegalArgumentException
298      *             if number of nanoseconds smaller than 0 or greater than
299      *             999,999,999.
300      */
setNanos(int n)301     public void setNanos(int n) throws IllegalArgumentException {
302         if ((n < 0) || (n > 999999999)) {
303             throw new IllegalArgumentException("Value out of range");
304         }
305         nanos = n;
306     }
307 
308     /**
309      * Sets the time represented by this {@code Timestamp} object to the
310      * supplied time, defined as the number of milliseconds since the Epoch
311      * (January 1 1970, 00:00:00.000 GMT).
312      *
313      * @param theTime
314      *            number of milliseconds since the Epoch (January 1 1970,
315      *            00:00:00.000 GMT).
316      */
317     @Override
setTime(long theTime)318     public void setTime(long theTime) {
319         setTimeImpl(theTime);
320     }
321 
setTimeImpl(long theTime)322     private void setTimeImpl(long theTime) {
323         /*
324          * Deal with the nanoseconds value. The supplied time is in milliseconds -
325          * so we must extract the milliseconds value and multiply by 1000000 to
326          * get nanoseconds. Things are more complex if theTime value is
327          * negative, since then the time value is the time before the Epoch but
328          * the nanoseconds value of the Timestamp must be positive - so we must
329          * take the "raw" milliseconds value and subtract it from 1000 to get to
330          * the true nanoseconds value Simultaneously, recalculate the time value
331          * to the exact nearest second and reset the Date time value
332          */
333         int milliseconds = (int) (theTime % 1000);
334         theTime = theTime - milliseconds;
335         if (milliseconds < 0) {
336             theTime = theTime - 1000;
337             milliseconds = 1000 + milliseconds;
338         }
339         super.setTime(theTime);
340         setNanos(milliseconds * 1000000);
341     }
342 
343     /**
344      * Returns the timestamp formatted as a String in the JDBC Timestamp Escape
345      * format, which is {@code "yyyy-MM-dd HH:mm:ss.nnnnnnnnn"}.
346      *
347      * @return A string representing the instant defined by the {@code
348      *         Timestamp}, in JDBC Timestamp escape format.
349      */
350     @SuppressWarnings("deprecation")
351     @Override
toString()352     public String toString() {
353         StringBuilder sb = new StringBuilder(29);
354 
355         format((getYear() + 1900), 4, sb);
356         sb.append('-');
357         format((getMonth() + 1), 2, sb);
358         sb.append('-');
359         format(getDate(), 2, sb);
360         sb.append(' ');
361         format(getHours(), 2, sb);
362         sb.append(':');
363         format(getMinutes(), 2, sb);
364         sb.append(':');
365         format(getSeconds(), 2, sb);
366         sb.append('.');
367         if (nanos == 0) {
368             sb.append('0');
369         } else {
370             format(nanos, 9, sb);
371             while (sb.charAt(sb.length() - 1) == '0') {
372                 sb.setLength(sb.length() - 1);
373             }
374         }
375 
376         return sb.toString();
377     }
378 
379     private static final String PADDING = "000000000";
380 
381     /*
382     * Private method to format the time
383     */
format(int date, int digits, StringBuilder sb)384     private void format(int date, int digits, StringBuilder sb) {
385         String str = String.valueOf(date);
386         if (digits - str.length() > 0) {
387             sb.append(PADDING.substring(0, digits - str.length()));
388         }
389         sb.append(str);
390     }
391 
392     /**
393      * Creates a {@code Timestamp} object with a time value equal to the time
394      * specified by a supplied String holding the time in JDBC timestamp escape
395      * format, which is {@code "yyyy-MM-dd HH:mm:ss.nnnnnnnnn}"
396      *
397      * @param s
398      *            the {@code String} containing a time in JDBC timestamp escape
399      *            format.
400      * @return A {@code Timestamp} object with time value as defined by the
401      *         supplied {@code String}.
402      * @throws IllegalArgumentException
403      *             if the provided string is {@code null}.
404      */
valueOf(String s)405     public static Timestamp valueOf(String s) throws IllegalArgumentException {
406         if (s == null) {
407             throw new IllegalArgumentException("Argument cannot be null");
408         }
409 
410         // omit trailing whitespace
411         s = s.trim();
412         if (!Pattern.matches(TIME_FORMAT_REGEX, s)) {
413             throw badTimestampString(s);
414         }
415 
416         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
417         ParsePosition pp = new ParsePosition(0);
418 
419         /*
420          * First parse out the yyyy-MM-dd HH:mm:ss component of the String into
421          * a Date object using the SimpleDateFormat. This should stop after the
422          * seconds value, according to the definition of SimpleDateFormat.parse,
423          * with the ParsePosition indicating the index of the "." which should
424          * precede the nanoseconds value
425          */
426         Date theDate;
427         try {
428             theDate = df.parse(s, pp);
429         } catch (Exception e) {
430             throw badTimestampString(s);
431         }
432 
433         if (theDate == null) {
434             throw badTimestampString(s);
435         }
436 
437         /*
438          * If we get here, the Date part of the string was OK - now for the
439          * nanoseconds value. Strictly, this requires the remaining part of the
440          * String to look like ".nnnnnnnnn". However, we accept anything with a
441          * '.' followed by 1 to 9 digits - we also accept nothing (no fractions
442          * of a second). Anything else is interpreted as incorrect format which
443          * will generate an IllegalArgumentException
444          */
445         int position = pp.getIndex();
446         int remaining = s.length() - position;
447         int theNanos;
448 
449         if (remaining == 0) {
450             // First, allow for the case where no fraction of a second is given:
451             theNanos = 0;
452         } else {
453             /*
454              * Case where fraction of a second is specified: Require 1 character
455              * plus the "." in the remaining part of the string...
456              */
457             if ((s.length() - position) < ".n".length()) {
458                 throw badTimestampString(s);
459             }
460 
461             /*
462              * If we're strict, we should not allow any EXTRA characters after
463              * the 9 digits
464              */
465             if ((s.length() - position) > ".nnnnnnnnn".length()) {
466                 throw badTimestampString(s);
467             }
468 
469             // Require the next character to be a "."
470             if (s.charAt(position) != '.') {
471                 throw new NumberFormatException("Bad input string format: expected '.' not '" +
472                         s.charAt(position) + "' in \"" + s + "\"");
473             }
474             // Get the length of the number string - need to account for the '.'
475             int nanoLength = s.length() - position - 1;
476 
477             // Get the 9 characters following the "." as an integer
478             String theNanoString = s.substring(position + 1, position + 1
479                     + nanoLength);
480             /*
481              * We must adjust for the cases where the nanos String was not 9
482              * characters long by padding out with zeros
483              */
484             theNanoString = theNanoString + "000000000";
485             theNanoString = theNanoString.substring(0, 9);
486 
487             try {
488                 theNanos = Integer.parseInt(theNanoString);
489             } catch (Exception e) {
490                 // If we get here, the string was not a number
491                 throw badTimestampString(s);
492             }
493         }
494 
495         if (theNanos < 0 || theNanos > 999999999) {
496             throw badTimestampString(s);
497         }
498 
499         Timestamp theTimestamp = new Timestamp(theDate.getTime());
500         theTimestamp.setNanos(theNanos);
501 
502         return theTimestamp;
503     }
504 
badTimestampString(String s)505     private static IllegalArgumentException badTimestampString(String s) {
506         throw new IllegalArgumentException("Timestamp format must be " +
507                 "yyyy-MM-dd HH:mm:ss.fffffffff; was '" + s + "'");
508     }
509 }
510