1 /*
2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.sql;
27 
28 import java.time.Instant;
29 import java.time.LocalDateTime;
30 import java.util.StringTokenizer;
31 
32 /**
33  * <P>A thin wrapper around <code>java.util.Date</code> that allows
34  * the JDBC API to identify this as an SQL <code>TIMESTAMP</code> value.
35  * It adds the ability
36  * to hold the SQL <code>TIMESTAMP</code> fractional seconds value, by allowing
37  * the specification of fractional seconds to a precision of nanoseconds.
38  * A Timestamp also provides formatting and
39  * parsing operations to support the JDBC escape syntax for timestamp values.
40  *
41  * <p>The precision of a Timestamp object is calculated to be either:
42  * <ul>
43  * <li><code>19 </code>, which is the number of characters in yyyy-mm-dd hh:mm:ss
44  * <li> <code> 20 + s </code>, which is the number
45  * of characters in the yyyy-mm-dd hh:mm:ss.[fff...] and <code>s</code> represents  the scale of the given Timestamp,
46  * its fractional seconds precision.
47  *</ul>
48  *
49  * <P><B>Note:</B> This type is a composite of a <code>java.util.Date</code> and a
50  * separate nanoseconds value. Only integral seconds are stored in the
51  * <code>java.util.Date</code> component. The fractional seconds - the nanos - are
52  * separate.  The <code>Timestamp.equals(Object)</code> method never returns
53  * <code>true</code> when passed an object
54  * that isn't an instance of <code>java.sql.Timestamp</code>,
55  * because the nanos component of a date is unknown.
56  * As a result, the <code>Timestamp.equals(Object)</code>
57  * method is not symmetric with respect to the
58  * <code>java.util.Date.equals(Object)</code>
59  * method.  Also, the <code>hashCode</code> method uses the underlying
60  * <code>java.util.Date</code>
61  * implementation and therefore does not include nanos in its computation.
62  * <P>
63  * Due to the differences between the <code>Timestamp</code> class
64  * and the <code>java.util.Date</code>
65  * class mentioned above, it is recommended that code not view
66  * <code>Timestamp</code> values generically as an instance of
67  * <code>java.util.Date</code>.  The
68  * inheritance relationship between <code>Timestamp</code>
69  * and <code>java.util.Date</code> really
70  * denotes implementation inheritance, and not type inheritance.
71  */
72 public class Timestamp extends java.util.Date {
73 
74     /**
75      * Constructs a <code>Timestamp</code> object initialized
76      * with the given values.
77      *
78      * @param year the year minus 1900
79      * @param month 0 to 11
80      * @param date 1 to 31
81      * @param hour 0 to 23
82      * @param minute 0 to 59
83      * @param second 0 to 59
84      * @param nano 0 to 999,999,999
85      * @deprecated instead use the constructor <code>Timestamp(long millis)</code>
86      * @exception IllegalArgumentException if the nano argument is out of bounds
87      */
88     @Deprecated
Timestamp(int year, int month, int date, int hour, int minute, int second, int nano)89     public Timestamp(int year, int month, int date,
90                      int hour, int minute, int second, int nano) {
91         super(year, month, date, hour, minute, second);
92         if (nano > 999999999 || nano < 0) {
93             throw new IllegalArgumentException("nanos > 999999999 or < 0");
94         }
95         nanos = nano;
96     }
97 
98     /**
99      * Constructs a <code>Timestamp</code> object
100      * using a milliseconds time value. The
101      * integral seconds are stored in the underlying date value; the
102      * fractional seconds are stored in the <code>nanos</code> field of
103      * the <code>Timestamp</code> object.
104      *
105      * @param time milliseconds since January 1, 1970, 00:00:00 GMT.
106      *        A negative number is the number of milliseconds before
107      *         January 1, 1970, 00:00:00 GMT.
108      * @see java.util.Calendar
109      */
Timestamp(long time)110     public Timestamp(long time) {
111         super((time/1000)*1000);
112         nanos = (int)((time%1000) * 1000000);
113         if (nanos < 0) {
114             nanos = 1000000000 + nanos;
115             super.setTime(((time/1000)-1)*1000);
116         }
117     }
118 
119     /**
120      * Sets this <code>Timestamp</code> object to represent a point in time that is
121      * <tt>time</tt> milliseconds after January 1, 1970 00:00:00 GMT.
122      *
123      * @param time   the number of milliseconds.
124      * @see #getTime
125      * @see #Timestamp(long time)
126      * @see java.util.Calendar
127      */
setTime(long time)128     public void setTime(long time) {
129         super.setTime((time/1000)*1000);
130         nanos = (int)((time%1000) * 1000000);
131         if (nanos < 0) {
132             nanos = 1000000000 + nanos;
133             super.setTime(((time/1000)-1)*1000);
134         }
135     }
136 
137     /**
138      * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
139      * represented by this <code>Timestamp</code> object.
140      *
141      * @return  the number of milliseconds since January 1, 1970, 00:00:00 GMT
142      *          represented by this date.
143      * @see #setTime
144      */
getTime()145     public long getTime() {
146         long time = super.getTime();
147         return (time + (nanos / 1000000));
148     }
149 
150 
151     /**
152      * @serial
153      */
154     private int nanos;
155 
156     /**
157      * Converts a <code>String</code> object in JDBC timestamp escape format to a
158      * <code>Timestamp</code> value.
159      *
160      * @param s timestamp in format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code>.  The
161      * fractional seconds may be omitted. The leading zero for <code>mm</code>
162      * and <code>dd</code> may also be omitted.
163      *
164      * @return corresponding <code>Timestamp</code> value
165      * @exception java.lang.IllegalArgumentException if the given argument
166      * does not have the format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code>
167      */
valueOf(String s)168     public static Timestamp valueOf(String s) {
169         final int YEAR_LENGTH = 4;
170         final int MONTH_LENGTH = 2;
171         final int DAY_LENGTH = 2;
172         final int MAX_MONTH = 12;
173         final int MAX_DAY = 31;
174         String date_s;
175         String time_s;
176         String nanos_s;
177         int year = 0;
178         int month = 0;
179         int day = 0;
180         int hour;
181         int minute;
182         int second;
183         int a_nanos = 0;
184         int firstDash;
185         int secondDash;
186         int dividingSpace;
187         int firstColon = 0;
188         int secondColon = 0;
189         int period = 0;
190         String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]";
191         String zeros = "000000000";
192         String delimiterDate = "-";
193         String delimiterTime = ":";
194 
195         if (s == null) throw new java.lang.IllegalArgumentException("null string");
196 
197         // Split the string into date and time components
198         s = s.trim();
199         dividingSpace = s.indexOf(' ');
200         if (dividingSpace > 0) {
201             date_s = s.substring(0,dividingSpace);
202             time_s = s.substring(dividingSpace+1);
203         } else {
204             throw new java.lang.IllegalArgumentException(formatError);
205         }
206 
207         // Parse the date
208         firstDash = date_s.indexOf('-');
209         secondDash = date_s.indexOf('-', firstDash+1);
210 
211         // Parse the time
212         if (time_s == null)
213             throw new java.lang.IllegalArgumentException(formatError);
214         firstColon = time_s.indexOf(':');
215         secondColon = time_s.indexOf(':', firstColon+1);
216         period = time_s.indexOf('.', secondColon+1);
217 
218         // Convert the date
219         boolean parsedDate = false;
220         if ((firstDash > 0) && (secondDash > 0) && (secondDash < date_s.length() - 1)) {
221             String yyyy = date_s.substring(0, firstDash);
222             String mm = date_s.substring(firstDash + 1, secondDash);
223             String dd = date_s.substring(secondDash + 1);
224             if (yyyy.length() == YEAR_LENGTH &&
225                     (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) &&
226                     (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) {
227                  year = Integer.parseInt(yyyy);
228                  month = Integer.parseInt(mm);
229                  day = Integer.parseInt(dd);
230 
231                 if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
232                     parsedDate = true;
233                 }
234             }
235         }
236         if (! parsedDate) {
237             throw new java.lang.IllegalArgumentException(formatError);
238         }
239 
240         // Convert the time; default missing nanos
241         if ((firstColon > 0) & (secondColon > 0) &
242             (secondColon < time_s.length()-1)) {
243             hour = Integer.parseInt(time_s.substring(0, firstColon));
244             minute =
245                 Integer.parseInt(time_s.substring(firstColon+1, secondColon));
246             if ((period > 0) & (period < time_s.length()-1)) {
247                 second =
248                     Integer.parseInt(time_s.substring(secondColon+1, period));
249                 nanos_s = time_s.substring(period+1);
250                 if (nanos_s.length() > 9)
251                     throw new java.lang.IllegalArgumentException(formatError);
252                 if (!Character.isDigit(nanos_s.charAt(0)))
253                     throw new java.lang.IllegalArgumentException(formatError);
254                 nanos_s = nanos_s + zeros.substring(0,9-nanos_s.length());
255                 a_nanos = Integer.parseInt(nanos_s);
256             } else if (period > 0) {
257                 throw new java.lang.IllegalArgumentException(formatError);
258             } else {
259                 second = Integer.parseInt(time_s.substring(secondColon+1));
260             }
261         } else {
262             throw new java.lang.IllegalArgumentException(formatError);
263         }
264 
265         return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos);
266     }
267 
268     /**
269      * Formats a timestamp in JDBC timestamp escape format.
270      *         <code>yyyy-mm-dd hh:mm:ss.fffffffff</code>,
271      * where <code>ffffffffff</code> indicates nanoseconds.
272      * <P>
273      * @return a <code>String</code> object in
274      *           <code>yyyy-mm-dd hh:mm:ss.fffffffff</code> format
275      */
276     @SuppressWarnings("deprecation")
toString()277     public String toString () {
278 
279         int year = super.getYear() + 1900;
280         int month = super.getMonth() + 1;
281         int day = super.getDate();
282         int hour = super.getHours();
283         int minute = super.getMinutes();
284         int second = super.getSeconds();
285         String yearString;
286         String monthString;
287         String dayString;
288         String hourString;
289         String minuteString;
290         String secondString;
291         String nanosString;
292         String zeros = "000000000";
293         String yearZeros = "0000";
294         StringBuffer timestampBuf;
295 
296         if (year < 1000) {
297             // Add leading zeros
298             yearString = "" + year;
299             yearString = yearZeros.substring(0, (4-yearString.length())) +
300                 yearString;
301         } else {
302             yearString = "" + year;
303         }
304         if (month < 10) {
305             monthString = "0" + month;
306         } else {
307             monthString = Integer.toString(month);
308         }
309         if (day < 10) {
310             dayString = "0" + day;
311         } else {
312             dayString = Integer.toString(day);
313         }
314         if (hour < 10) {
315             hourString = "0" + hour;
316         } else {
317             hourString = Integer.toString(hour);
318         }
319         if (minute < 10) {
320             minuteString = "0" + minute;
321         } else {
322             minuteString = Integer.toString(minute);
323         }
324         if (second < 10) {
325             secondString = "0" + second;
326         } else {
327             secondString = Integer.toString(second);
328         }
329         if (nanos == 0) {
330             nanosString = "0";
331         } else {
332             nanosString = Integer.toString(nanos);
333 
334             // Add leading zeros
335             nanosString = zeros.substring(0, (9-nanosString.length())) +
336                 nanosString;
337 
338             // Truncate trailing zeros
339             char[] nanosChar = new char[nanosString.length()];
340             nanosString.getChars(0, nanosString.length(), nanosChar, 0);
341             int truncIndex = 8;
342             while (nanosChar[truncIndex] == '0') {
343                 truncIndex--;
344             }
345 
346             nanosString = new String(nanosChar, 0, truncIndex + 1);
347         }
348 
349         // do a string buffer here instead.
350         timestampBuf = new StringBuffer(20+nanosString.length());
351         timestampBuf.append(yearString);
352         timestampBuf.append("-");
353         timestampBuf.append(monthString);
354         timestampBuf.append("-");
355         timestampBuf.append(dayString);
356         timestampBuf.append(" ");
357         timestampBuf.append(hourString);
358         timestampBuf.append(":");
359         timestampBuf.append(minuteString);
360         timestampBuf.append(":");
361         timestampBuf.append(secondString);
362         timestampBuf.append(".");
363         timestampBuf.append(nanosString);
364 
365         return (timestampBuf.toString());
366     }
367 
368     /**
369      * Gets this <code>Timestamp</code> object's <code>nanos</code> value.
370      *
371      * @return this <code>Timestamp</code> object's fractional seconds component
372      * @see #setNanos
373      */
getNanos()374     public int getNanos() {
375         return nanos;
376     }
377 
378     /**
379      * Sets this <code>Timestamp</code> object's <code>nanos</code> field
380      * to the given value.
381      *
382      * @param n the new fractional seconds component
383      * @exception java.lang.IllegalArgumentException if the given argument
384      *            is greater than 999999999 or less than 0
385      * @see #getNanos
386      */
setNanos(int n)387     public void setNanos(int n) {
388         if (n > 999999999 || n < 0) {
389             throw new IllegalArgumentException("nanos > 999999999 or < 0");
390         }
391         nanos = n;
392     }
393 
394     /**
395      * Tests to see if this <code>Timestamp</code> object is
396      * equal to the given <code>Timestamp</code> object.
397      *
398      * @param ts the <code>Timestamp</code> value to compare with
399      * @return <code>true</code> if the given <code>Timestamp</code>
400      *         object is equal to this <code>Timestamp</code> object;
401      *         <code>false</code> otherwise
402      */
equals(Timestamp ts)403     public boolean equals(Timestamp ts) {
404         if (super.equals(ts)) {
405             if  (nanos == ts.nanos) {
406                 return true;
407             } else {
408                 return false;
409             }
410         } else {
411             return false;
412         }
413     }
414 
415     /**
416      * Tests to see if this <code>Timestamp</code> object is
417      * equal to the given object.
418      *
419      * This version of the method <code>equals</code> has been added
420      * to fix the incorrect
421      * signature of <code>Timestamp.equals(Timestamp)</code> and to preserve backward
422      * compatibility with existing class files.
423      *
424      * Note: This method is not symmetric with respect to the
425      * <code>equals(Object)</code> method in the base class.
426      *
427      * @param ts the <code>Object</code> value to compare with
428      * @return <code>true</code> if the given <code>Object</code> is an instance
429      *         of a <code>Timestamp</code> that
430      *         is equal to this <code>Timestamp</code> object;
431      *         <code>false</code> otherwise
432      */
equals(java.lang.Object ts)433     public boolean equals(java.lang.Object ts) {
434       if (ts instanceof Timestamp) {
435         return this.equals((Timestamp)ts);
436       } else {
437         return false;
438       }
439     }
440 
441     /**
442      * Indicates whether this <code>Timestamp</code> object is
443      * earlier than the given <code>Timestamp</code> object.
444      *
445      * @param ts the <code>Timestamp</code> value to compare with
446      * @return <code>true</code> if this <code>Timestamp</code> object is earlier;
447      *        <code>false</code> otherwise
448      */
before(Timestamp ts)449     public boolean before(Timestamp ts) {
450         return compareTo(ts) < 0;
451     }
452 
453     /**
454      * Indicates whether this <code>Timestamp</code> object is
455      * later than the given <code>Timestamp</code> object.
456      *
457      * @param ts the <code>Timestamp</code> value to compare with
458      * @return <code>true</code> if this <code>Timestamp</code> object is later;
459      *        <code>false</code> otherwise
460      */
after(Timestamp ts)461     public boolean after(Timestamp ts) {
462         return compareTo(ts) > 0;
463     }
464 
465     /**
466      * Compares this <code>Timestamp</code> object to the given
467      * <code>Timestamp</code> object.
468      *
469      * @param   ts   the <code>Timestamp</code> object to be compared to
470      *                this <code>Timestamp</code> object
471      * @return  the value <code>0</code> if the two <code>Timestamp</code>
472      *          objects are equal; a value less than <code>0</code> if this
473      *          <code>Timestamp</code> object is before the given argument;
474      *          and a value greater than <code>0</code> if this
475      *          <code>Timestamp</code> object is after the given argument.
476      * @since   1.4
477      */
compareTo(Timestamp ts)478     public int compareTo(Timestamp ts) {
479         long thisTime = this.getTime();
480         long anotherTime = ts.getTime();
481         int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1));
482         if (i == 0) {
483             if (nanos > ts.nanos) {
484                     return 1;
485             } else if (nanos < ts.nanos) {
486                 return -1;
487             }
488         }
489         return i;
490     }
491 
492     /**
493      * Compares this <code>Timestamp</code> object to the given
494      * <code>Date</code> object.
495      *
496      * @param o the <code>Date</code> to be compared to
497      *          this <code>Timestamp</code> object
498      * @return  the value <code>0</code> if this <code>Timestamp</code> object
499      *          and the given object are equal; a value less than <code>0</code>
500      *          if this  <code>Timestamp</code> object is before the given argument;
501      *          and a value greater than <code>0</code> if this
502      *          <code>Timestamp</code> object is after the given argument.
503      *
504      * @since   1.5
505      */
compareTo(java.util.Date o)506     public int compareTo(java.util.Date o) {
507        if(o instanceof Timestamp) {
508             // When Timestamp instance compare it with a Timestamp
509             // Hence it is basically calling this.compareTo((Timestamp))o);
510             // Note typecasting is safe because o is instance of Timestamp
511            return compareTo((Timestamp)o);
512       } else {
513             // When Date doing a o.compareTo(this)
514             // will give wrong results.
515           Timestamp ts = new Timestamp(o.getTime());
516           return this.compareTo(ts);
517       }
518     }
519 
520     /**
521      * {@inheritDoc}
522      *
523      * The {@code hashCode} method uses the underlying {@code java.util.Date}
524      * implementation and therefore does not include nanos in its computation.
525      *
526      */
527     @Override
hashCode()528     public int hashCode() {
529         return super.hashCode();
530     }
531 
532     static final long serialVersionUID = 2745179027874758501L;
533 
534     private static final int MILLIS_PER_SECOND = 1000;
535 
536     /**
537      * Obtains an instance of {@code Timestamp} from a {@code LocalDateTime}
538      * object, with the same year, month, day of month, hours, minutes,
539      * seconds and nanos date-time value as the provided {@code LocalDateTime}.
540      * <p>
541      * The provided {@code LocalDateTime} is interpreted as the local
542      * date-time in the local time zone.
543      *
544      * @param dateTime a {@code LocalDateTime} to convert
545      * @return a {@code Timestamp} object
546      * @exception NullPointerException if {@code dateTime} is null.
547      * @since 1.8
548      */
549     @SuppressWarnings("deprecation")
valueOf(LocalDateTime dateTime)550     public static Timestamp valueOf(LocalDateTime dateTime) {
551         return new Timestamp(dateTime.getYear() - 1900,
552                              dateTime.getMonthValue() - 1,
553                              dateTime.getDayOfMonth(),
554                              dateTime.getHour(),
555                              dateTime.getMinute(),
556                              dateTime.getSecond(),
557                              dateTime.getNano());
558     }
559 
560     /**
561      * Converts this {@code Timestamp} object to a {@code LocalDateTime}.
562      * <p>
563      * The conversion creates a {@code LocalDateTime} that represents the
564      * same year, month, day of month, hours, minutes, seconds and nanos
565      * date-time value as this {@code Timestamp} in the local time zone.
566      *
567      * @return a {@code LocalDateTime} object representing the same date-time value
568      * @since 1.8
569      */
570     @SuppressWarnings("deprecation")
toLocalDateTime()571     public LocalDateTime toLocalDateTime() {
572         return LocalDateTime.of(getYear() + 1900,
573                                 getMonth() + 1,
574                                 getDate(),
575                                 getHours(),
576                                 getMinutes(),
577                                 getSeconds(),
578                                 getNanos());
579     }
580 
581     /**
582      * Obtains an instance of {@code Timestamp} from an {@link Instant} object.
583      * <p>
584      * {@code Instant} can store points on the time-line further in the future
585      * and further in the past than {@code Date}. In this scenario, this method
586      * will throw an exception.
587      *
588      * @param instant  the instant to convert
589      * @return an {@code Timestamp} representing the same point on the time-line as
590      *  the provided instant
591      * @exception NullPointerException if {@code instant} is null.
592      * @exception IllegalArgumentException if the instant is too large to
593      *  represent as a {@code Timesamp}
594      * @since 1.8
595      */
from(Instant instant)596     public static Timestamp from(Instant instant) {
597         try {
598             Timestamp stamp = new Timestamp(instant.getEpochSecond() * MILLIS_PER_SECOND);
599             stamp.nanos = instant.getNano();
600             return stamp;
601         } catch (ArithmeticException ex) {
602             throw new IllegalArgumentException(ex);
603         }
604     }
605 
606     /**
607      * Converts this {@code Timestamp} object to an {@code Instant}.
608      * <p>
609      * The conversion creates an {@code Instant} that represents the same
610      * point on the time-line as this {@code Timestamp}.
611      *
612      * @return an instant representing the same point on the time-line
613      * @since 1.8
614      */
615     @Override
toInstant()616     public Instant toInstant() {
617         return Instant.ofEpochSecond(super.getTime() / MILLIS_PER_SECOND, nanos);
618     }
619 }
620