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 }