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