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