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 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 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 * 273 * @return a <code>String</code> object in 274 * <code>yyyy-mm-dd hh:mm:ss.fffffffff</code> format 275 */ 276 @SuppressWarnings("deprecation") 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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") 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") 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 */ 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 616 public Instant toInstant() { 617 return Instant.ofEpochSecond(super.getTime() / MILLIS_PER_SECOND, nanos); 618 } 619 }