1 /*
   2  * Copyright (c) 1996, 2011, 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.util.StringTokenizer;
  29 
  30 /**
  31  * <P>A thin wrapper around <code>java.util.Date</code> that allows
  32  * the JDBC API to identify this as an SQL <code>TIMESTAMP</code> value.
  33  * It adds the ability
  34  * to hold the SQL <code>TIMESTAMP</code> fractional seconds value, by allowing
  35  * the specification of fractional seconds to a precision of nanoseconds.
  36  * A Timestamp also provides formatting and
  37  * parsing operations to support the JDBC escape syntax for timestamp values.
  38  *
  39  * <p>The precision of a Timestamp object is calculated to be either:
  40  * <ul>
  41  * <li><code>19 </code>, which is the number of characters in yyyy-mm-dd hh:mm:ss
  42  * <li> <code> 20 + s </code>, which is the number
  43  * of characters in the yyyy-mm-dd hh:mm:ss.[fff...] and <code>s</code> represents  the scale of the given Timestamp,
  44  * its fractional seconds precision.
  45  *</ul>
  46  *
  47  * <P><B>Note:</B> This type is a composite of a <code>java.util.Date</code> and a
  48  * separate nanoseconds value. Only integral seconds are stored in the
  49  * <code>java.util.Date</code> component. The fractional seconds - the nanos - are
  50  * separate.  The <code>Timestamp.equals(Object)</code> method never returns
  51  * <code>true</code> when passed an object
  52  * that isn't an instance of <code>java.sql.Timestamp</code>,
  53  * because the nanos component of a date is unknown.
  54  * As a result, the <code>Timestamp.equals(Object)</code>
  55  * method is not symmetric with respect to the
  56  * <code>java.util.Date.equals(Object)</code>
  57  * method.  Also, the <code>hashCode</code> method uses the underlying
  58  * <code>java.util.Date</code>
  59  * implementation and therefore does not include nanos in its computation.
  60  * <P>
  61  * Due to the differences between the <code>Timestamp</code> class
  62  * and the <code>java.util.Date</code>
  63  * class mentioned above, it is recommended that code not view
  64  * <code>Timestamp</code> values generically as an instance of
  65  * <code>java.util.Date</code>.  The
  66  * inheritance relationship between <code>Timestamp</code>
  67  * and <code>java.util.Date</code> really
  68  * denotes implementation inheritance, and not type inheritance.
  69  */
  70 public class Timestamp extends java.util.Date {
  71 
  72     /**
  73      * Constructs a <code>Timestamp</code> object initialized
  74      * with the given values.
  75      *
  76      * @param year the year minus 1900
  77      * @param month 0 to 11
  78      * @param date 1 to 31
  79      * @param hour 0 to 23
  80      * @param minute 0 to 59
  81      * @param second 0 to 59
  82      * @param nano 0 to 999,999,999
  83      * @deprecated instead use the constructor <code>Timestamp(long millis)</code>
  84      * @exception IllegalArgumentException if the nano argument is out of bounds
  85      */
  86     @Deprecated
  87     public Timestamp(int year, int month, int date,
  88                      int hour, int minute, int second, int nano) {
  89         super(year, month, date, hour, minute, second);
  90         if (nano > 999999999 || nano < 0) {
  91             throw new IllegalArgumentException("nanos > 999999999 or < 0");
  92         }
  93         nanos = nano;
  94     }
  95 
  96     /**
  97      * Constructs a <code>Timestamp</code> object
  98      * using a milliseconds time value. The
  99      * integral seconds are stored in the underlying date value; the
 100      * fractional seconds are stored in the <code>nanos</code> field of
 101      * the <code>Timestamp</code> object.
 102      *
 103      * @param time milliseconds since January 1, 1970, 00:00:00 GMT.
 104      *        A negative number is the number of milliseconds before
 105      *         January 1, 1970, 00:00:00 GMT.
 106      * @see java.util.Calendar
 107      */
 108     public Timestamp(long time) {
 109         super((time/1000)*1000);
 110         nanos = (int)((time%1000) * 1000000);
 111         if (nanos < 0) {
 112             nanos = 1000000000 + nanos;
 113             super.setTime(((time/1000)-1)*1000);
 114         }
 115     }
 116 
 117     /**
 118      * Sets this <code>Timestamp</code> object to represent a point in time that is
 119      * <tt>time</tt> milliseconds after January 1, 1970 00:00:00 GMT.
 120      *
 121      * @param time   the number of milliseconds.
 122      * @see #getTime
 123      * @see #Timestamp(long time)
 124      * @see java.util.Calendar
 125      */
 126     public void setTime(long time) {
 127         super.setTime((time/1000)*1000);
 128         nanos = (int)((time%1000) * 1000000);
 129         if (nanos < 0) {
 130             nanos = 1000000000 + nanos;
 131             super.setTime(((time/1000)-1)*1000);
 132         }
 133     }
 134 
 135     /**
 136      * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
 137      * represented by this <code>Timestamp</code> object.
 138      *
 139      * @return  the number of milliseconds since January 1, 1970, 00:00:00 GMT
 140      *          represented by this date.
 141      * @see #setTime
 142      */
 143     public long getTime() {
 144         long time = super.getTime();
 145         return (time + (nanos / 1000000));
 146     }
 147 
 148 
 149     /**
 150      * @serial
 151      */
 152     private int nanos;
 153 
 154     /**
 155      * Converts a <code>String</code> object in JDBC timestamp escape format to a
 156      * <code>Timestamp</code> value.
 157      *
 158      * @param s timestamp in format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code>.  The
 159      * fractional seconds may be omitted. The leading zero for <code>mm</code>
 160      * and <code>dd</code> may also be omitted.
 161      *
 162      * @return corresponding <code>Timestamp</code> value
 163      * @exception java.lang.IllegalArgumentException if the given argument
 164      * does not have the format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code>
 165      */
 166     public static Timestamp valueOf(String s) {
 167         final int YEAR_LENGTH = 4;
 168         final int MONTH_LENGTH = 2;
 169         final int DAY_LENGTH = 2;
 170         final int MAX_MONTH = 12;
 171         final int MAX_DAY = 31;
 172         String date_s;
 173         String time_s;
 174         String nanos_s;
 175         int year = 0;
 176         int month = 0;
 177         int day = 0;
 178         int hour;
 179         int minute;
 180         int second;
 181         int a_nanos = 0;
 182         int firstDash;
 183         int secondDash;
 184         int dividingSpace;
 185         int firstColon = 0;
 186         int secondColon = 0;
 187         int period = 0;
 188         String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]";
 189         String zeros = "000000000";
 190         String delimiterDate = "-";
 191         String delimiterTime = ":";
 192 
 193         if (s == null) throw new java.lang.IllegalArgumentException("null string");
 194 
 195         // Split the string into date and time components
 196         s = s.trim();
 197         dividingSpace = s.indexOf(' ');
 198         if (dividingSpace > 0) {
 199             date_s = s.substring(0,dividingSpace);
 200             time_s = s.substring(dividingSpace+1);
 201         } else {
 202             throw new java.lang.IllegalArgumentException(formatError);
 203         }
 204 
 205         // Parse the date
 206         firstDash = date_s.indexOf('-');
 207         secondDash = date_s.indexOf('-', firstDash+1);
 208 
 209         // Parse the time
 210         if (time_s == null)
 211             throw new java.lang.IllegalArgumentException(formatError);
 212         firstColon = time_s.indexOf(':');
 213         secondColon = time_s.indexOf(':', firstColon+1);
 214         period = time_s.indexOf('.', secondColon+1);
 215 
 216         // Convert the date
 217         boolean parsedDate = false;
 218         if ((firstDash > 0) && (secondDash > 0) && (secondDash < date_s.length() - 1)) {
 219             String yyyy = date_s.substring(0, firstDash);
 220             String mm = date_s.substring(firstDash + 1, secondDash);
 221             String dd = date_s.substring(secondDash + 1);
 222             if (yyyy.length() == YEAR_LENGTH &&
 223                     (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) &&
 224                     (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) {
 225                  year = Integer.parseInt(yyyy);
 226                  month = Integer.parseInt(mm);
 227                  day = Integer.parseInt(dd);
 228 
 229                 if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
 230                     parsedDate = true;
 231                 }
 232             }
 233         }
 234         if (! parsedDate) {
 235             throw new java.lang.IllegalArgumentException(formatError);
 236         }
 237 
 238         // Convert the time; default missing nanos
 239         if ((firstColon > 0) & (secondColon > 0) &
 240             (secondColon < time_s.length()-1)) {
 241             hour = Integer.parseInt(time_s.substring(0, firstColon));
 242             minute =
 243                 Integer.parseInt(time_s.substring(firstColon+1, secondColon));
 244             if ((period > 0) & (period < time_s.length()-1)) {
 245                 second =
 246                     Integer.parseInt(time_s.substring(secondColon+1, period));
 247                 nanos_s = time_s.substring(period+1);
 248                 if (nanos_s.length() > 9)
 249                     throw new java.lang.IllegalArgumentException(formatError);
 250                 if (!Character.isDigit(nanos_s.charAt(0)))
 251                     throw new java.lang.IllegalArgumentException(formatError);
 252                 nanos_s = nanos_s + zeros.substring(0,9-nanos_s.length());
 253                 a_nanos = Integer.parseInt(nanos_s);
 254             } else if (period > 0) {
 255                 throw new java.lang.IllegalArgumentException(formatError);
 256             } else {
 257                 second = Integer.parseInt(time_s.substring(secondColon+1));
 258             }
 259         } else {
 260             throw new java.lang.IllegalArgumentException(formatError);
 261         }
 262 
 263         return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos);
 264     }
 265 
 266     /**
 267      * Formats a timestamp in JDBC timestamp escape format.
 268      *         <code>yyyy-mm-dd hh:mm:ss.fffffffff</code>,
 269      * where <code>ffffffffff</code> indicates nanoseconds.
 270      * <P>
 271      * @return a <code>String</code> object in
 272      *           <code>yyyy-mm-dd hh:mm:ss.fffffffff</code> format
 273      */
 274     @SuppressWarnings("deprecation")
 275     public String toString () {
 276 
 277         int year = super.getYear() + 1900;
 278         int month = super.getMonth() + 1;
 279         int day = super.getDate();
 280         int hour = super.getHours();
 281         int minute = super.getMinutes();
 282         int second = super.getSeconds();
 283         String yearString;
 284         String monthString;
 285         String dayString;
 286         String hourString;
 287         String minuteString;
 288         String secondString;
 289         String nanosString;
 290         String zeros = "000000000";
 291         String yearZeros = "0000";
 292         StringBuffer timestampBuf;
 293 
 294         if (year < 1000) {
 295             // Add leading zeros
 296             yearString = "" + year;
 297             yearString = yearZeros.substring(0, (4-yearString.length())) +
 298                 yearString;
 299         } else {
 300             yearString = "" + year;
 301         }
 302         if (month < 10) {
 303             monthString = "0" + month;
 304         } else {
 305             monthString = Integer.toString(month);
 306         }
 307         if (day < 10) {
 308             dayString = "0" + day;
 309         } else {
 310             dayString = Integer.toString(day);
 311         }
 312         if (hour < 10) {
 313             hourString = "0" + hour;
 314         } else {
 315             hourString = Integer.toString(hour);
 316         }
 317         if (minute < 10) {
 318             minuteString = "0" + minute;
 319         } else {
 320             minuteString = Integer.toString(minute);
 321         }
 322         if (second < 10) {
 323             secondString = "0" + second;
 324         } else {
 325             secondString = Integer.toString(second);
 326         }
 327         if (nanos == 0) {
 328             nanosString = "0";
 329         } else {
 330             nanosString = Integer.toString(nanos);
 331 
 332             // Add leading zeros
 333             nanosString = zeros.substring(0, (9-nanosString.length())) +
 334                 nanosString;
 335 
 336             // Truncate trailing zeros
 337             char[] nanosChar = new char[nanosString.length()];
 338             nanosString.getChars(0, nanosString.length(), nanosChar, 0);
 339             int truncIndex = 8;
 340             while (nanosChar[truncIndex] == '0') {
 341                 truncIndex--;
 342             }
 343 
 344             nanosString = new String(nanosChar, 0, truncIndex + 1);
 345         }
 346 
 347         // do a string buffer here instead.
 348         timestampBuf = new StringBuffer(20+nanosString.length());
 349         timestampBuf.append(yearString);
 350         timestampBuf.append("-");
 351         timestampBuf.append(monthString);
 352         timestampBuf.append("-");
 353         timestampBuf.append(dayString);
 354         timestampBuf.append(" ");
 355         timestampBuf.append(hourString);
 356         timestampBuf.append(":");
 357         timestampBuf.append(minuteString);
 358         timestampBuf.append(":");
 359         timestampBuf.append(secondString);
 360         timestampBuf.append(".");
 361         timestampBuf.append(nanosString);
 362 
 363         return (timestampBuf.toString());
 364     }
 365 
 366     /**
 367      * Gets this <code>Timestamp</code> object's <code>nanos</code> value.
 368      *
 369      * @return this <code>Timestamp</code> object's fractional seconds component
 370      * @see #setNanos
 371      */
 372     public int getNanos() {
 373         return nanos;
 374     }
 375 
 376     /**
 377      * Sets this <code>Timestamp</code> object's <code>nanos</code> field
 378      * to the given value.
 379      *
 380      * @param n the new fractional seconds component
 381      * @exception java.lang.IllegalArgumentException if the given argument
 382      *            is greater than 999999999 or less than 0
 383      * @see #getNanos
 384      */
 385     public void setNanos(int n) {
 386         if (n > 999999999 || n < 0) {
 387             throw new IllegalArgumentException("nanos > 999999999 or < 0");
 388         }
 389         nanos = n;
 390     }
 391 
 392     /**
 393      * Tests to see if this <code>Timestamp</code> object is
 394      * equal to the given <code>Timestamp</code> object.
 395      *
 396      * @param ts the <code>Timestamp</code> value to compare with
 397      * @return <code>true</code> if the given <code>Timestamp</code>
 398      *         object is equal to this <code>Timestamp</code> object;
 399      *         <code>false</code> otherwise
 400      */
 401     public boolean equals(Timestamp ts) {
 402         if (super.equals(ts)) {
 403             if  (nanos == ts.nanos) {
 404                 return true;
 405             } else {
 406                 return false;
 407             }
 408         } else {
 409             return false;
 410         }
 411     }
 412 
 413     /**
 414      * Tests to see if this <code>Timestamp</code> object is
 415      * equal to the given object.
 416      *
 417      * This version of the method <code>equals</code> has been added
 418      * to fix the incorrect
 419      * signature of <code>Timestamp.equals(Timestamp)</code> and to preserve backward
 420      * compatibility with existing class files.
 421      *
 422      * Note: This method is not symmetric with respect to the
 423      * <code>equals(Object)</code> method in the base class.
 424      *
 425      * @param ts the <code>Object</code> value to compare with
 426      * @return <code>true</code> if the given <code>Object</code> is an instance
 427      *         of a <code>Timestamp</code> that
 428      *         is equal to this <code>Timestamp</code> object;
 429      *         <code>false</code> otherwise
 430      */
 431     public boolean equals(java.lang.Object ts) {
 432       if (ts instanceof Timestamp) {
 433         return this.equals((Timestamp)ts);
 434       } else {
 435         return false;
 436       }
 437     }
 438 
 439     /**
 440      * Indicates whether this <code>Timestamp</code> object is
 441      * earlier than the given <code>Timestamp</code> object.
 442      *
 443      * @param ts the <code>Timestamp</code> value to compare with
 444      * @return <code>true</code> if this <code>Timestamp</code> object is earlier;
 445      *        <code>false</code> otherwise
 446      */
 447     public boolean before(Timestamp ts) {
 448         return compareTo(ts) < 0;
 449     }
 450 
 451     /**
 452      * Indicates whether this <code>Timestamp</code> object is
 453      * later than the given <code>Timestamp</code> object.
 454      *
 455      * @param ts the <code>Timestamp</code> value to compare with
 456      * @return <code>true</code> if this <code>Timestamp</code> object is later;
 457      *        <code>false</code> otherwise
 458      */
 459     public boolean after(Timestamp ts) {
 460         return compareTo(ts) > 0;
 461     }
 462 
 463     /**
 464      * Compares this <code>Timestamp</code> object to the given
 465      * <code>Timestamp</code> object.
 466      *
 467      * @param   ts   the <code>Timestamp</code> object to be compared to
 468      *                this <code>Timestamp</code> object
 469      * @return  the value <code>0</code> if the two <code>Timestamp</code>
 470      *          objects are equal; a value less than <code>0</code> if this
 471      *          <code>Timestamp</code> object is before the given argument;
 472      *          and a value greater than <code>0</code> if this
 473      *          <code>Timestamp</code> object is after the given argument.
 474      * @since   1.4
 475      */
 476     public int compareTo(Timestamp ts) {
 477         long thisTime = this.getTime();
 478         long anotherTime = ts.getTime();
 479         int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1));
 480         if (i == 0) {
 481             if (nanos > ts.nanos) {
 482                     return 1;
 483             } else if (nanos < ts.nanos) {
 484                 return -1;
 485             }
 486         }
 487         return i;
 488 
 489     }
 490 
 491     /**
 492      * Compares this <code>Timestamp</code> object to the given
 493      * <code>Date</code> object.
 494      *
 495      * @param o the <code>Date</code> to be compared to
 496      *          this <code>Timestamp</code> object
 497      * @return  the value <code>0</code> if this <code>Timestamp</code> object
 498      *          and the given object are equal; a value less than <code>0</code>
 499      *          if this  <code>Timestamp</code> object is before the given argument;
 500      *          and a value greater than <code>0</code> if this
 501      *          <code>Timestamp</code> object is after the given argument.
 502      *
 503      * @since   1.5
 504      */
 505     public int compareTo(java.util.Date o) {
 506        if(o instanceof Timestamp) {
 507             // When Timestamp instance compare it with a Timestamp
 508             // Hence it is basically calling this.compareTo((Timestamp))o);
 509             // Note typecasting is safe because o is instance of Timestamp
 510            return compareTo((Timestamp)o);
 511       } else {
 512             // When Date doing a o.compareTo(this)
 513             // will give wrong results.
 514           Timestamp ts = new Timestamp(o.getTime());
 515           return this.compareTo(ts);
 516       }
 517     }
 518 
 519     /**
 520      * {@inheritDoc}
 521      *
 522      * The {@code hashCode} method uses the underlying {@code java.util.Date}
 523      * implementation and therefore does not include nanos in its computation.
 524      *
 525      */
 526     @Override
 527     public int hashCode() {
 528         return super.hashCode();
 529     }
 530 
 531     static final long serialVersionUID = 2745179027874758501L;
 532 
 533 }