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     public String toString () {
 275 
 276         int year = super.getYear() + 1900;
 277         int month = super.getMonth() + 1;
 278         int day = super.getDate();
 279         int hour = super.getHours();
 280         int minute = super.getMinutes();
 281         int second = super.getSeconds();
 282         String yearString;
 283         String monthString;
 284         String dayString;
 285         String hourString;
 286         String minuteString;
 287         String secondString;
 288         String nanosString;
 289         String zeros = "000000000";
 290         String yearZeros = "0000";
 291         StringBuffer timestampBuf;
 292 
 293         if (year < 1000) {
 294             // Add leading zeros
 295             yearString = "" + year;
 296             yearString = yearZeros.substring(0, (4-yearString.length())) +
 297                 yearString;
 298         } else {
 299             yearString = "" + year;
 300         }
 301         if (month < 10) {
 302             monthString = "0" + month;
 303         } else {
 304             monthString = Integer.toString(month);
 305         }
 306         if (day < 10) {
 307             dayString = "0" + day;
 308         } else {
 309             dayString = Integer.toString(day);
 310         }
 311         if (hour < 10) {
 312             hourString = "0" + hour;
 313         } else {
 314             hourString = Integer.toString(hour);
 315         }
 316         if (minute < 10) {
 317             minuteString = "0" + minute;
 318         } else {
 319             minuteString = Integer.toString(minute);
 320         }
 321         if (second < 10) {
 322             secondString = "0" + second;
 323         } else {
 324             secondString = Integer.toString(second);
 325         }
 326         if (nanos == 0) {
 327             nanosString = "0";
 328         } else {
 329             nanosString = Integer.toString(nanos);
 330 
 331             // Add leading zeros
 332             nanosString = zeros.substring(0, (9-nanosString.length())) +
 333                 nanosString;
 334 
 335             // Truncate trailing zeros
 336             char[] nanosChar = new char[nanosString.length()];
 337             nanosString.getChars(0, nanosString.length(), nanosChar, 0);
 338             int truncIndex = 8;
 339             while (nanosChar[truncIndex] == '0') {
 340                 truncIndex--;
 341             }
 342 
 343             nanosString = new String(nanosChar, 0, truncIndex + 1);
 344         }
 345 
 346         // do a string buffer here instead.
 347         timestampBuf = new StringBuffer(20+nanosString.length());
 348         timestampBuf.append(yearString);
 349         timestampBuf.append("-");
 350         timestampBuf.append(monthString);
 351         timestampBuf.append("-");
 352         timestampBuf.append(dayString);
 353         timestampBuf.append(" ");
 354         timestampBuf.append(hourString);
 355         timestampBuf.append(":");
 356         timestampBuf.append(minuteString);
 357         timestampBuf.append(":");
 358         timestampBuf.append(secondString);
 359         timestampBuf.append(".");
 360         timestampBuf.append(nanosString);
 361 
 362         return (timestampBuf.toString());
 363     }
 364 
 365     /**
 366      * Gets this <code>Timestamp</code> object's <code>nanos</code> value.
 367      *
 368      * @return this <code>Timestamp</code> object's fractional seconds component
 369      * @see #setNanos
 370      */
 371     public int getNanos() {
 372         return nanos;
 373     }
 374 
 375     /**
 376      * Sets this <code>Timestamp</code> object's <code>nanos</code> field
 377      * to the given value.
 378      *
 379      * @param n the new fractional seconds component
 380      * @exception java.lang.IllegalArgumentException if the given argument
 381      *            is greater than 999999999 or less than 0
 382      * @see #getNanos
 383      */
 384     public void setNanos(int n) {
 385         if (n > 999999999 || n < 0) {
 386             throw new IllegalArgumentException("nanos > 999999999 or < 0");
 387         }
 388         nanos = n;
 389     }
 390 
 391     /**
 392      * Tests to see if this <code>Timestamp</code> object is
 393      * equal to the given <code>Timestamp</code> object.
 394      *
 395      * @param ts the <code>Timestamp</code> value to compare with
 396      * @return <code>true</code> if the given <code>Timestamp</code>
 397      *         object is equal to this <code>Timestamp</code> object;
 398      *         <code>false</code> otherwise
 399      */
 400     public boolean equals(Timestamp ts) {
 401         if (super.equals(ts)) {
 402             if  (nanos == ts.nanos) {
 403                 return true;
 404             } else {
 405                 return false;
 406             }
 407         } else {
 408             return false;
 409         }
 410     }
 411 
 412     /**
 413      * Tests to see if this <code>Timestamp</code> object is
 414      * equal to the given object.
 415      *
 416      * This version of the method <code>equals</code> has been added
 417      * to fix the incorrect
 418      * signature of <code>Timestamp.equals(Timestamp)</code> and to preserve backward
 419      * compatibility with existing class files.
 420      *
 421      * Note: This method is not symmetric with respect to the
 422      * <code>equals(Object)</code> method in the base class.
 423      *
 424      * @param ts the <code>Object</code> value to compare with
 425      * @return <code>true</code> if the given <code>Object</code> is an instance
 426      *         of a <code>Timestamp</code> that
 427      *         is equal to this <code>Timestamp</code> object;
 428      *         <code>false</code> otherwise
 429      */
 430     public boolean equals(java.lang.Object ts) {
 431       if (ts instanceof Timestamp) {
 432         return this.equals((Timestamp)ts);
 433       } else {
 434         return false;
 435       }
 436     }
 437 
 438     /**
 439      * Indicates whether this <code>Timestamp</code> object is
 440      * earlier than the given <code>Timestamp</code> object.
 441      *
 442      * @param ts the <code>Timestamp</code> value to compare with
 443      * @return <code>true</code> if this <code>Timestamp</code> object is earlier;
 444      *        <code>false</code> otherwise
 445      */
 446     public boolean before(Timestamp ts) {
 447         return compareTo(ts) < 0;
 448     }
 449 
 450     /**
 451      * Indicates whether this <code>Timestamp</code> object is
 452      * later than the given <code>Timestamp</code> object.
 453      *
 454      * @param ts the <code>Timestamp</code> value to compare with
 455      * @return <code>true</code> if this <code>Timestamp</code> object is later;
 456      *        <code>false</code> otherwise
 457      */
 458     public boolean after(Timestamp ts) {
 459         return compareTo(ts) > 0;
 460     }
 461 
 462     /**
 463      * Compares this <code>Timestamp</code> object to the given
 464      * <code>Timestamp</code> object.
 465      *
 466      * @param   ts   the <code>Timestamp</code> object to be compared to
 467      *                this <code>Timestamp</code> object
 468      * @return  the value <code>0</code> if the two <code>Timestamp</code>
 469      *          objects are equal; a value less than <code>0</code> if this
 470      *          <code>Timestamp</code> object is before the given argument;
 471      *          and a value greater than <code>0</code> if this
 472      *          <code>Timestamp</code> object is after the given argument.
 473      * @since   1.4
 474      */
 475     public int compareTo(Timestamp ts) {
 476         long thisTime = this.getTime();
 477         long anotherTime = ts.getTime();
 478         int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1));
 479         if (i == 0) {
 480             if (nanos > ts.nanos) {
 481                     return 1;
 482             } else if (nanos < ts.nanos) {
 483                 return -1;
 484             }
 485         }
 486         return i;
 487 
 488     }
 489 
 490     /**
 491      * Compares this <code>Timestamp</code> object to the given
 492      * <code>Date</code> object.
 493      *
 494      * @param o the <code>Date</code> to be compared to
 495      *          this <code>Timestamp</code> object
 496      * @return  the value <code>0</code> if this <code>Timestamp</code> object
 497      *          and the given object are equal; a value less than <code>0</code>
 498      *          if this  <code>Timestamp</code> object is before the given argument;
 499      *          and a value greater than <code>0</code> if this
 500      *          <code>Timestamp</code> object is after the given argument.
 501      *
 502      * @since   1.5
 503      */
 504     public int compareTo(java.util.Date o) {
 505        if(o instanceof Timestamp) {
 506             // When Timestamp instance compare it with a Timestamp
 507             // Hence it is basically calling this.compareTo((Timestamp))o);
 508             // Note typecasting is safe because o is instance of Timestamp
 509            return compareTo((Timestamp)o);
 510       } else {
 511             // When Date doing a o.compareTo(this)
 512             // will give wrong results.
 513           Timestamp ts = new Timestamp(o.getTime());
 514           return this.compareTo(ts);
 515       }
 516     }
 517 
 518     /**
 519      * {@inheritDoc}
 520      *
 521      * The {@code hashCode} method uses the underlying {@code java.util.Date}
 522      * implementation and therefore does not include nanos in its computation.
 523      *
 524      */
 525     @Override
 526     public int hashCode() {
 527         return super.hashCode();
 528     }
 529 
 530     static final long serialVersionUID = 2745179027874758501L;
 531 
 532 }