1 /*
   2  * Copyright (c) 1996, 2014, 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
  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      */
 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      */
 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      */
 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      */
 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         int year = 0;
 175         int month = 0;
 176         int day = 0;
 177         int hour;
 178         int minute;
 179         int second;
 180         int a_nanos = 0;
 181         int firstDash;
 182         int secondDash;
 183         int dividingSpace;
 184         int firstColon;
 185         int secondColon;
 186         int period;
 187         String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]";
 188 
 189         if (s == null) throw new java.lang.IllegalArgumentException("null string");
 190 
 191         // Split the string into date and time components
 192         s = s.trim();
 193         dividingSpace = s.indexOf(' ');
 194         if (dividingSpace < 0) {
 195             throw new java.lang.IllegalArgumentException(formatError);
 196         }
 197 
 198         // Parse the date
 199         firstDash = s.indexOf('-');
 200         secondDash = s.indexOf('-', firstDash+1);
 201 
 202         // Parse the time
 203         firstColon = s.indexOf(':', dividingSpace + 1);
 204         secondColon = s.indexOf(':', firstColon + 1);
 205         period = s.indexOf('.', secondColon + 1);
 206 
 207         // Convert the date
 208         boolean parsedDate = false;
 209         if ((firstDash > 0) && (secondDash > 0) && (secondDash < dividingSpace - 1)) {
 210             if (firstDash == YEAR_LENGTH &&
 211                     (secondDash - firstDash >= 2 && secondDash - firstDash - 1 <= MONTH_LENGTH) &&
 212                     (dividingSpace - secondDash >= 2 && dividingSpace - secondDash - 1 <= DAY_LENGTH)) {
 213                  year = Integer.parseInt(s, 10, 0, firstDash);
 214                  month = Integer.parseInt(s, 10, firstDash + 1, secondDash);
 215                  day = Integer.parseInt(s, 10, secondDash + 1, dividingSpace);
 216 
 217                 if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
 218                     parsedDate = true;
 219                 }
 220             }
 221         }
 222         if (! parsedDate) {
 223             throw new java.lang.IllegalArgumentException(formatError);
 224         }
 225 
 226         // Convert the time; default missing nanos
 227         if (firstColon > 0 && secondColon > 0 && secondColon < s.length() - 1) {
 228             hour = Integer.parseInt(s, 10, dividingSpace + 1, firstColon);
 229             minute = Integer.parseInt(s, 10, firstColon + 1, secondColon);
 230             if (period > 0 && period < s.length() - 1) {
 231                 second = Integer.parseInt(s, 10, secondColon + 1, period);
 232                 int nanoPrecision = s.length() - (period + 1);
 233                 if (nanoPrecision > 9)
 234                     throw new java.lang.IllegalArgumentException(formatError);
 235                 if (!Character.isDigit(s.charAt(period + 1)))
 236                     throw new java.lang.IllegalArgumentException(formatError);
 237                 int tmpNanos = Integer.parseInt(s, 10, period + 1);
 238                 while (nanoPrecision < 9) {
 239                     tmpNanos *= 10;
 240                     nanoPrecision++;
 241                 }
 242                 a_nanos = tmpNanos;
 243             } else if (period > 0) {
 244                 throw new java.lang.IllegalArgumentException(formatError);
 245             } else {
 246                 second = Integer.parseInt(s, 10, secondColon + 1);
 247             }
 248         } else {
 249             throw new java.lang.IllegalArgumentException(formatError);
 250         }
 251 
 252         return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos);
 253     }
 254 
 255     /**
 256      * Formats a timestamp in JDBC timestamp escape format.
 257      *         <code>yyyy-mm-dd hh:mm:ss.fffffffff</code>,
 258      * where <code>ffffffffff</code> indicates nanoseconds.
 259      *
 260      * @return a <code>String</code> object in
 261      *           <code>yyyy-mm-dd hh:mm:ss.fffffffff</code> format
 262      */
 263     @SuppressWarnings("deprecation")
 264     public String toString () {
 265 
 266         int year = super.getYear() + 1900;
 267         int month = super.getMonth() + 1;
 268         int day = super.getDate();
 269         int hour = super.getHours();
 270         int minute = super.getMinutes();
 271         int second = super.getSeconds();
 272         String yearString;
 273         String monthString;
 274         String dayString;
 275         String hourString;
 276         String minuteString;
 277         String secondString;
 278         String nanosString;
 279         String zeros = "000000000";
 280         String yearZeros = "0000";
 281         StringBuffer timestampBuf;
 282 
 283         if (year < 1000) {
 284             // Add leading zeros
 285             yearString = "" + year;
 286             yearString = yearZeros.substring(0, (4-yearString.length())) +
 287                 yearString;
 288         } else {
 289             yearString = "" + year;
 290         }
 291         if (month < 10) {
 292             monthString = "0" + month;
 293         } else {
 294             monthString = Integer.toString(month);
 295         }
 296         if (day < 10) {
 297             dayString = "0" + day;
 298         } else {
 299             dayString = Integer.toString(day);
 300         }
 301         if (hour < 10) {
 302             hourString = "0" + hour;
 303         } else {
 304             hourString = Integer.toString(hour);
 305         }
 306         if (minute < 10) {
 307             minuteString = "0" + minute;
 308         } else {
 309             minuteString = Integer.toString(minute);
 310         }
 311         if (second < 10) {
 312             secondString = "0" + second;
 313         } else {
 314             secondString = Integer.toString(second);
 315         }
 316         if (nanos == 0) {
 317             nanosString = "0";
 318         } else {
 319             nanosString = Integer.toString(nanos);
 320 
 321             // Add leading zeros
 322             nanosString = zeros.substring(0, (9-nanosString.length())) +
 323                 nanosString;
 324 
 325             // Truncate trailing zeros
 326             char[] nanosChar = new char[nanosString.length()];
 327             nanosString.getChars(0, nanosString.length(), nanosChar, 0);
 328             int truncIndex = 8;
 329             while (nanosChar[truncIndex] == '0') {
 330                 truncIndex--;
 331             }
 332 
 333             nanosString = new String(nanosChar, 0, truncIndex + 1);
 334         }
 335 
 336         // do a string buffer here instead.
 337         timestampBuf = new StringBuffer(20+nanosString.length());
 338         timestampBuf.append(yearString);
 339         timestampBuf.append("-");
 340         timestampBuf.append(monthString);
 341         timestampBuf.append("-");
 342         timestampBuf.append(dayString);
 343         timestampBuf.append(" ");
 344         timestampBuf.append(hourString);
 345         timestampBuf.append(":");
 346         timestampBuf.append(minuteString);
 347         timestampBuf.append(":");
 348         timestampBuf.append(secondString);
 349         timestampBuf.append(".");
 350         timestampBuf.append(nanosString);
 351 
 352         return (timestampBuf.toString());
 353     }
 354 
 355     /**
 356      * Gets this <code>Timestamp</code> object's <code>nanos</code> value.
 357      *
 358      * @return this <code>Timestamp</code> object's fractional seconds component
 359      * @see #setNanos
 360      */
 361     public int getNanos() {
 362         return nanos;
 363     }
 364 
 365     /**
 366      * Sets this <code>Timestamp</code> object's <code>nanos</code> field
 367      * to the given value.
 368      *
 369      * @param n the new fractional seconds component
 370      * @exception java.lang.IllegalArgumentException if the given argument
 371      *            is greater than 999999999 or less than 0
 372      * @see #getNanos
 373      */
 374     public void setNanos(int n) {
 375         if (n > 999999999 || n < 0) {
 376             throw new IllegalArgumentException("nanos > 999999999 or < 0");
 377         }
 378         nanos = n;
 379     }
 380 
 381     /**
 382      * Tests to see if this <code>Timestamp</code> object is
 383      * equal to the given <code>Timestamp</code> object.
 384      *
 385      * @param ts the <code>Timestamp</code> value to compare with
 386      * @return <code>true</code> if the given <code>Timestamp</code>
 387      *         object is equal to this <code>Timestamp</code> object;
 388      *         <code>false</code> otherwise
 389      */
 390     public boolean equals(Timestamp ts) {
 391         if (super.equals(ts)) {
 392             if  (nanos == ts.nanos) {
 393                 return true;
 394             } else {
 395                 return false;
 396             }
 397         } else {
 398             return false;
 399         }
 400     }
 401 
 402     /**
 403      * Tests to see if this <code>Timestamp</code> object is
 404      * equal to the given object.
 405      *
 406      * This version of the method <code>equals</code> has been added
 407      * to fix the incorrect
 408      * signature of <code>Timestamp.equals(Timestamp)</code> and to preserve backward
 409      * compatibility with existing class files.
 410      *
 411      * Note: This method is not symmetric with respect to the
 412      * <code>equals(Object)</code> method in the base class.
 413      *
 414      * @param ts the <code>Object</code> value to compare with
 415      * @return <code>true</code> if the given <code>Object</code> is an instance
 416      *         of a <code>Timestamp</code> that
 417      *         is equal to this <code>Timestamp</code> object;
 418      *         <code>false</code> otherwise
 419      */
 420     public boolean equals(java.lang.Object ts) {
 421       if (ts instanceof Timestamp) {
 422         return this.equals((Timestamp)ts);
 423       } else {
 424         return false;
 425       }
 426     }
 427 
 428     /**
 429      * Indicates whether this <code>Timestamp</code> object is
 430      * earlier than the given <code>Timestamp</code> object.
 431      *
 432      * @param ts the <code>Timestamp</code> value to compare with
 433      * @return <code>true</code> if this <code>Timestamp</code> object is earlier;
 434      *        <code>false</code> otherwise
 435      */
 436     public boolean before(Timestamp ts) {
 437         return compareTo(ts) < 0;
 438     }
 439 
 440     /**
 441      * Indicates whether this <code>Timestamp</code> object is
 442      * later than the given <code>Timestamp</code> object.
 443      *
 444      * @param ts the <code>Timestamp</code> value to compare with
 445      * @return <code>true</code> if this <code>Timestamp</code> object is later;
 446      *        <code>false</code> otherwise
 447      */
 448     public boolean after(Timestamp ts) {
 449         return compareTo(ts) > 0;
 450     }
 451 
 452     /**
 453      * Compares this <code>Timestamp</code> object to the given
 454      * <code>Timestamp</code> object.
 455      *
 456      * @param   ts   the <code>Timestamp</code> object to be compared to
 457      *                this <code>Timestamp</code> object
 458      * @return  the value <code>0</code> if the two <code>Timestamp</code>
 459      *          objects are equal; a value less than <code>0</code> if this
 460      *          <code>Timestamp</code> object is before the given argument;
 461      *          and a value greater than <code>0</code> if this
 462      *          <code>Timestamp</code> object is after the given argument.
 463      * @since   1.4
 464      */
 465     public int compareTo(Timestamp ts) {
 466         long thisTime = this.getTime();
 467         long anotherTime = ts.getTime();
 468         int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1));
 469         if (i == 0) {
 470             if (nanos > ts.nanos) {
 471                     return 1;
 472             } else if (nanos < ts.nanos) {
 473                 return -1;
 474             }
 475         }
 476         return i;
 477     }
 478 
 479     /**
 480      * Compares this <code>Timestamp</code> object to the given
 481      * <code>Date</code> object.
 482      *
 483      * @param o the <code>Date</code> to be compared to
 484      *          this <code>Timestamp</code> object
 485      * @return  the value <code>0</code> if this <code>Timestamp</code> object
 486      *          and the given object are equal; a value less than <code>0</code>
 487      *          if this  <code>Timestamp</code> object is before the given argument;
 488      *          and a value greater than <code>0</code> if this
 489      *          <code>Timestamp</code> object is after the given argument.
 490      *
 491      * @since   1.5
 492      */
 493     public int compareTo(java.util.Date o) {
 494        if(o instanceof Timestamp) {
 495             // When Timestamp instance compare it with a Timestamp
 496             // Hence it is basically calling this.compareTo((Timestamp))o);
 497             // Note typecasting is safe because o is instance of Timestamp
 498            return compareTo((Timestamp)o);
 499       } else {
 500             // When Date doing a o.compareTo(this)
 501             // will give wrong results.
 502           Timestamp ts = new Timestamp(o.getTime());
 503           return this.compareTo(ts);
 504       }
 505     }
 506 
 507     /**
 508      * {@inheritDoc}
 509      *
 510      * The {@code hashCode} method uses the underlying {@code java.util.Date}
 511      * implementation and therefore does not include nanos in its computation.
 512      *
 513      */
 514     @Override
 515     public int hashCode() {
 516         return super.hashCode();
 517     }
 518 
 519     static final long serialVersionUID = 2745179027874758501L;
 520 
 521     private static final int MILLIS_PER_SECOND = 1000;
 522 
 523     /**
 524      * Obtains an instance of {@code Timestamp} from a {@code LocalDateTime}
 525      * object, with the same year, month, day of month, hours, minutes,
 526      * seconds and nanos date-time value as the provided {@code LocalDateTime}.
 527      * <p>
 528      * The provided {@code LocalDateTime} is interpreted as the local
 529      * date-time in the local time zone.
 530      *
 531      * @param dateTime a {@code LocalDateTime} to convert
 532      * @return a {@code Timestamp} object
 533      * @exception NullPointerException if {@code dateTime} is null.
 534      * @since 1.8
 535      */
 536     @SuppressWarnings("deprecation")
 537     public static Timestamp valueOf(LocalDateTime dateTime) {
 538         return new Timestamp(dateTime.getYear() - 1900,
 539                              dateTime.getMonthValue() - 1,
 540                              dateTime.getDayOfMonth(),
 541                              dateTime.getHour(),
 542                              dateTime.getMinute(),
 543                              dateTime.getSecond(),
 544                              dateTime.getNano());
 545     }
 546 
 547     /**
 548      * Converts this {@code Timestamp} object to a {@code LocalDateTime}.
 549      * <p>
 550      * The conversion creates a {@code LocalDateTime} that represents the
 551      * same year, month, day of month, hours, minutes, seconds and nanos
 552      * date-time value as this {@code Timestamp} in the local time zone.
 553      *
 554      * @return a {@code LocalDateTime} object representing the same date-time value
 555      * @since 1.8
 556      */
 557     @SuppressWarnings("deprecation")
 558     public LocalDateTime toLocalDateTime() {
 559         return LocalDateTime.of(getYear() + 1900,
 560                                 getMonth() + 1,
 561                                 getDate(),
 562                                 getHours(),
 563                                 getMinutes(),
 564                                 getSeconds(),
 565                                 getNanos());
 566     }
 567 
 568     /**
 569      * Obtains an instance of {@code Timestamp} from an {@link Instant} object.
 570      * <p>
 571      * {@code Instant} can store points on the time-line further in the future
 572      * and further in the past than {@code Date}. In this scenario, this method
 573      * will throw an exception.
 574      *
 575      * @param instant  the instant to convert
 576      * @return an {@code Timestamp} representing the same point on the time-line as
 577      *  the provided instant
 578      * @exception NullPointerException if {@code instant} is null.
 579      * @exception IllegalArgumentException if the instant is too large to
 580      *  represent as a {@code Timestamp}
 581      * @since 1.8
 582      */
 583     public static Timestamp from(Instant instant) {
 584         try {
 585             Timestamp stamp = new Timestamp(instant.getEpochSecond() * MILLIS_PER_SECOND);
 586             stamp.nanos = instant.getNano();
 587             return stamp;
 588         } catch (ArithmeticException ex) {
 589             throw new IllegalArgumentException(ex);
 590         }
 591     }
 592 
 593     /**
 594      * Converts this {@code Timestamp} object to an {@code Instant}.
 595      * <p>
 596      * The conversion creates an {@code Instant} that represents the same
 597      * point on the time-line as this {@code Timestamp}.
 598      *
 599      * @return an instant representing the same point on the time-line
 600      * @since 1.8
 601      */
 602     @Override
 603     public Instant toInstant() {
 604         return Instant.ofEpochSecond(super.getTime() / MILLIS_PER_SECOND, nanos);
 605     }
 606 }