1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.sql; 19 20 import java.text.ParsePosition; 21 import java.text.SimpleDateFormat; 22 import java.util.Date; 23 import java.util.regex.Pattern; 24 25 /** 26 * A Java representation of the SQL {@code TIMESTAMP} type. It provides the 27 * capability of representing the SQL {@code TIMESTAMP} nanosecond value, in 28 * addition to the regular date/time value which has millisecond resolution. 29 * <p> 30 * The {@code Timestamp} class consists of a regular date/time value, where only 31 * the integral seconds value is stored, plus a nanoseconds value where the 32 * fractional seconds are stored. 33 * <p> 34 * The addition of the nanosecond value field to the {@code Timestamp} object 35 * makes it significantly different from the {@code java.util.Date} object which 36 * it extends. Users should be aware that {@code Timestamp} objects are not 37 * interchangable with {@code java.util.Date} objects when used outside the 38 * confines of the {@code java.sql} package. 39 * 40 * @see Date 41 * @see Time 42 * @see java.util.Date 43 */ 44 public class Timestamp extends Date { 45 46 private static final long serialVersionUID = 2745179027874758501L; 47 48 // The nanoseconds time value of the Timestamp 49 private int nanos; 50 51 // The regex pattern of yyyy-MM-dd HH:mm:ss 52 private static final String TIME_FORMAT_REGEX = "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.*"; 53 54 /** 55 * Returns a {@code Timestamp} corresponding to the time specified by the 56 * supplied values for <i>Year</i>, <i>Month</i>, <i>Date</i>, <i>Hour</i>, 57 * <i>Minutes</i>, <i>Seconds</i> and <i>Nanoseconds</i>. 58 * 59 * @deprecated Use the constructor {@link #Timestamp(long)}. 60 * @param theYear 61 * specified as the year minus 1900. 62 * @param theMonth 63 * specified as an integer in the range [0,11]. 64 * @param theDate 65 * specified as an integer in the range [1,31]. 66 * @param theHour 67 * specified as an integer in the range [0,23]. 68 * @param theMinute 69 * specified as an integer in the range [0,59]. 70 * @param theSecond 71 * specified as an integer in the range [0,59]. 72 * @param theNano 73 * which defines the nanosecond value of the timestamp specified 74 * as an integer in the range [0,999'999'999] 75 * @throws IllegalArgumentException 76 * if any of the parameters is out of range. 77 */ 78 @SuppressWarnings("deprecation") 79 @Deprecated Timestamp(int theYear, int theMonth, int theDate, int theHour, int theMinute, int theSecond, int theNano)80 public Timestamp(int theYear, int theMonth, int theDate, int theHour, 81 int theMinute, int theSecond, int theNano) 82 throws IllegalArgumentException { 83 super(theYear, theMonth, theDate, theHour, theMinute, theSecond); 84 if (theNano < 0 || theNano > 999999999) { 85 throw new IllegalArgumentException(); 86 } 87 nanos = theNano; 88 } 89 90 /** 91 * Returns a {@code Timestamp} object corresponding to the time represented 92 * by a supplied time value. 93 * 94 * @param theTime 95 * a time value in the format of milliseconds since the Epoch 96 * (January 1 1970 00:00:00.000 GMT). 97 */ Timestamp(long theTime)98 public Timestamp(long theTime) { 99 super(theTime); 100 /* 101 * Now set the time for this Timestamp object - which deals with the 102 * nanosecond value as well as the base time 103 */ 104 setTimeImpl(theTime); 105 } 106 107 /** 108 * Returns {@code true} if this timestamp object is later than the supplied 109 * timestamp, otherwise returns {@code false}. 110 * 111 * @param theTimestamp 112 * the timestamp to compare with this timestamp object. 113 * @return {@code true} if this {@code Timestamp} object is later than the 114 * supplied timestamp, {@code false} otherwise. 115 */ after(Timestamp theTimestamp)116 public boolean after(Timestamp theTimestamp) { 117 long thisTime = this.getTime(); 118 long compareTime = theTimestamp.getTime(); 119 120 // If the time value is later, the timestamp is later 121 if (thisTime > compareTime) { 122 return true; 123 } 124 // If the time value is earlier, the timestamp is not later 125 else if (thisTime < compareTime) { 126 return false; 127 } 128 /* 129 * Otherwise the time values are equal in which case the nanoseconds 130 * value determines whether this timestamp is later... 131 */ 132 else if (this.getNanos() > theTimestamp.getNanos()) { 133 return true; 134 } else { 135 return false; 136 } 137 } 138 139 /** 140 * Returns {@code true} if this {@code Timestamp} object is earlier than the 141 * supplied timestamp, otherwise returns {@code false}. 142 * 143 * @param theTimestamp 144 * the timestamp to compare with this {@code Timestamp} object. 145 * @return {@code true} if this {@code Timestamp} object is earlier than the 146 * supplied timestamp, {@code false} otherwise. 147 */ before(Timestamp theTimestamp)148 public boolean before(Timestamp theTimestamp) { 149 long thisTime = this.getTime(); 150 long compareTime = theTimestamp.getTime(); 151 152 // If the time value is later, the timestamp is later 153 if (thisTime < compareTime) { 154 return true; 155 } 156 // If the time value is earlier, the timestamp is not later 157 else if (thisTime > compareTime) { 158 return false; 159 } 160 /* 161 * Otherwise the time values are equal in which case the nanoseconds 162 * value determines whether this timestamp is later... 163 */ 164 else if (this.getNanos() < theTimestamp.getNanos()) { 165 return true; 166 } else { 167 return false; 168 } 169 } 170 171 /** 172 * Compares this {@code Timestamp} object with a supplied {@code Timestamp} 173 * object. 174 * 175 * @param theObject 176 * the timestamp to compare with this {@code Timestamp} object, 177 * passed as an {@code Object}. 178 * @return <dd> 179 * <dl> 180 * {@code 0} if the two {@code Timestamp} objects are equal in time 181 * </dl> 182 * <dl> 183 * a value {@code < 0} if this {@code Timestamp} object is before 184 * the supplied {@code Timestamp} and a value 185 * </dl> 186 * <dl> 187 * {@code > 0} if this {@code Timestamp} object is after the 188 * supplied {@code Timestamp} 189 * </dl> 190 * </dd> 191 * @throws ClassCastException 192 * if the supplied object is not a {@code Timestamp} object. 193 */ 194 @Override compareTo(Date theObject)195 public int compareTo(Date theObject) throws ClassCastException { 196 return this.compareTo((Timestamp) theObject); 197 } 198 199 /** 200 * Compares this {@code Timestamp} object with a supplied {@code Timestamp} 201 * object. 202 * 203 * @param theTimestamp 204 * the timestamp to compare with this {@code Timestamp} object, 205 * passed in as a {@code Timestamp}. 206 * @return one of the following: 207 * <ul> 208 * <li>{@code 0}, if the two {@code Timestamp} objects are 209 * equal in time</li> 210 * <li>{@code < 0}, if this {@code Timestamp} object is before the 211 * supplied {@code Timestamp}</li> 212 * <li> {@code > 0}, if this {@code Timestamp} object is after the 213 * supplied {@code Timestamp}</li> 214 * </ul> 215 */ compareTo(Timestamp theTimestamp)216 public int compareTo(Timestamp theTimestamp) { 217 int result = super.compareTo(theTimestamp); 218 if (result == 0) { 219 int thisNano = this.getNanos(); 220 int thatNano = theTimestamp.getNanos(); 221 if (thisNano > thatNano) { 222 return 1; 223 } else if (thisNano == thatNano) { 224 return 0; 225 } else { 226 return -1; 227 } 228 } 229 return result; 230 } 231 232 /** 233 * Tests to see if this timestamp is equal to a supplied object. 234 * 235 * @param theObject 236 * the object to which this timestamp is compared. 237 * @return {@code true} if this {@code Timestamp} object is equal to the 238 * supplied {@code Timestamp} object<br>{@code false} if the object 239 * is not a {@code Timestamp} object or if the object is a {@code 240 * Timestamp} but represents a different instant in time. 241 */ 242 @Override equals(Object theObject)243 public boolean equals(Object theObject) { 244 if (theObject instanceof Timestamp) { 245 return equals((Timestamp) theObject); 246 } 247 return false; 248 } 249 250 /** 251 * Tests to see if this timestamp is equal to a supplied timestamp. 252 * 253 * @param theTimestamp 254 * the timestamp to compare with this {@code Timestamp} object, 255 * passed as an {@code Object}. 256 * @return {@code true} if this {@code Timestamp} object is equal to the 257 * supplied {@code Timestamp} object, {@code false} otherwise. 258 */ equals(Timestamp theTimestamp)259 public boolean equals(Timestamp theTimestamp) { 260 if (theTimestamp == null) { 261 return false; 262 } 263 return (this.getTime() == theTimestamp.getTime()) 264 && (this.getNanos() == theTimestamp.getNanos()); 265 } 266 267 /** 268 * Gets this {@code Timestamp}'s nanosecond value 269 * 270 * @return The timestamp's nanosecond value, an integer between 0 and 271 * 999,999,999. 272 */ getNanos()273 public int getNanos() { 274 return nanos; 275 } 276 277 /** 278 * Returns the time represented by this {@code Timestamp} object, as a long 279 * value containing the number of milliseconds since the Epoch (January 1 280 * 1970, 00:00:00.000 GMT). 281 * 282 * @return the number of milliseconds that have passed since January 1 1970, 283 * 00:00:00.000 GMT. 284 */ 285 @Override getTime()286 public long getTime() { 287 long theTime = super.getTime(); 288 theTime = theTime + (nanos / 1000000); 289 return theTime; 290 } 291 292 /** 293 * Sets the nanosecond value for this {@code Timestamp}. 294 * 295 * @param n 296 * number of nanoseconds. 297 * @throws IllegalArgumentException 298 * if number of nanoseconds smaller than 0 or greater than 299 * 999,999,999. 300 */ setNanos(int n)301 public void setNanos(int n) throws IllegalArgumentException { 302 if ((n < 0) || (n > 999999999)) { 303 throw new IllegalArgumentException("Value out of range"); 304 } 305 nanos = n; 306 } 307 308 /** 309 * Sets the time represented by this {@code Timestamp} object to the 310 * supplied time, defined as the number of milliseconds since the Epoch 311 * (January 1 1970, 00:00:00.000 GMT). 312 * 313 * @param theTime 314 * number of milliseconds since the Epoch (January 1 1970, 315 * 00:00:00.000 GMT). 316 */ 317 @Override setTime(long theTime)318 public void setTime(long theTime) { 319 setTimeImpl(theTime); 320 } 321 setTimeImpl(long theTime)322 private void setTimeImpl(long theTime) { 323 /* 324 * Deal with the nanoseconds value. The supplied time is in milliseconds - 325 * so we must extract the milliseconds value and multiply by 1000000 to 326 * get nanoseconds. Things are more complex if theTime value is 327 * negative, since then the time value is the time before the Epoch but 328 * the nanoseconds value of the Timestamp must be positive - so we must 329 * take the "raw" milliseconds value and subtract it from 1000 to get to 330 * the true nanoseconds value Simultaneously, recalculate the time value 331 * to the exact nearest second and reset the Date time value 332 */ 333 int milliseconds = (int) (theTime % 1000); 334 theTime = theTime - milliseconds; 335 if (milliseconds < 0) { 336 theTime = theTime - 1000; 337 milliseconds = 1000 + milliseconds; 338 } 339 super.setTime(theTime); 340 setNanos(milliseconds * 1000000); 341 } 342 343 /** 344 * Returns the timestamp formatted as a String in the JDBC Timestamp Escape 345 * format, which is {@code "yyyy-MM-dd HH:mm:ss.nnnnnnnnn"}. 346 * 347 * @return A string representing the instant defined by the {@code 348 * Timestamp}, in JDBC Timestamp escape format. 349 */ 350 @SuppressWarnings("deprecation") 351 @Override toString()352 public String toString() { 353 StringBuilder sb = new StringBuilder(29); 354 355 format((getYear() + 1900), 4, sb); 356 sb.append('-'); 357 format((getMonth() + 1), 2, sb); 358 sb.append('-'); 359 format(getDate(), 2, sb); 360 sb.append(' '); 361 format(getHours(), 2, sb); 362 sb.append(':'); 363 format(getMinutes(), 2, sb); 364 sb.append(':'); 365 format(getSeconds(), 2, sb); 366 sb.append('.'); 367 if (nanos == 0) { 368 sb.append('0'); 369 } else { 370 format(nanos, 9, sb); 371 while (sb.charAt(sb.length() - 1) == '0') { 372 sb.setLength(sb.length() - 1); 373 } 374 } 375 376 return sb.toString(); 377 } 378 379 private static final String PADDING = "000000000"; 380 381 /* 382 * Private method to format the time 383 */ format(int date, int digits, StringBuilder sb)384 private void format(int date, int digits, StringBuilder sb) { 385 String str = String.valueOf(date); 386 if (digits - str.length() > 0) { 387 sb.append(PADDING.substring(0, digits - str.length())); 388 } 389 sb.append(str); 390 } 391 392 /** 393 * Creates a {@code Timestamp} object with a time value equal to the time 394 * specified by a supplied String holding the time in JDBC timestamp escape 395 * format, which is {@code "yyyy-MM-dd HH:mm:ss.nnnnnnnnn}" 396 * 397 * @param s 398 * the {@code String} containing a time in JDBC timestamp escape 399 * format. 400 * @return A {@code Timestamp} object with time value as defined by the 401 * supplied {@code String}. 402 * @throws IllegalArgumentException 403 * if the provided string is {@code null}. 404 */ valueOf(String s)405 public static Timestamp valueOf(String s) throws IllegalArgumentException { 406 if (s == null) { 407 throw new IllegalArgumentException("Argument cannot be null"); 408 } 409 410 // omit trailing whitespace 411 s = s.trim(); 412 if (!Pattern.matches(TIME_FORMAT_REGEX, s)) { 413 throw badTimestampString(s); 414 } 415 416 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 417 ParsePosition pp = new ParsePosition(0); 418 419 /* 420 * First parse out the yyyy-MM-dd HH:mm:ss component of the String into 421 * a Date object using the SimpleDateFormat. This should stop after the 422 * seconds value, according to the definition of SimpleDateFormat.parse, 423 * with the ParsePosition indicating the index of the "." which should 424 * precede the nanoseconds value 425 */ 426 Date theDate; 427 try { 428 theDate = df.parse(s, pp); 429 } catch (Exception e) { 430 throw badTimestampString(s); 431 } 432 433 if (theDate == null) { 434 throw badTimestampString(s); 435 } 436 437 /* 438 * If we get here, the Date part of the string was OK - now for the 439 * nanoseconds value. Strictly, this requires the remaining part of the 440 * String to look like ".nnnnnnnnn". However, we accept anything with a 441 * '.' followed by 1 to 9 digits - we also accept nothing (no fractions 442 * of a second). Anything else is interpreted as incorrect format which 443 * will generate an IllegalArgumentException 444 */ 445 int position = pp.getIndex(); 446 int remaining = s.length() - position; 447 int theNanos; 448 449 if (remaining == 0) { 450 // First, allow for the case where no fraction of a second is given: 451 theNanos = 0; 452 } else { 453 /* 454 * Case where fraction of a second is specified: Require 1 character 455 * plus the "." in the remaining part of the string... 456 */ 457 if ((s.length() - position) < ".n".length()) { 458 throw badTimestampString(s); 459 } 460 461 /* 462 * If we're strict, we should not allow any EXTRA characters after 463 * the 9 digits 464 */ 465 if ((s.length() - position) > ".nnnnnnnnn".length()) { 466 throw badTimestampString(s); 467 } 468 469 // Require the next character to be a "." 470 if (s.charAt(position) != '.') { 471 throw new NumberFormatException("Bad input string format: expected '.' not '" + 472 s.charAt(position) + "' in \"" + s + "\""); 473 } 474 // Get the length of the number string - need to account for the '.' 475 int nanoLength = s.length() - position - 1; 476 477 // Get the 9 characters following the "." as an integer 478 String theNanoString = s.substring(position + 1, position + 1 479 + nanoLength); 480 /* 481 * We must adjust for the cases where the nanos String was not 9 482 * characters long by padding out with zeros 483 */ 484 theNanoString = theNanoString + "000000000"; 485 theNanoString = theNanoString.substring(0, 9); 486 487 try { 488 theNanos = Integer.parseInt(theNanoString); 489 } catch (Exception e) { 490 // If we get here, the string was not a number 491 throw badTimestampString(s); 492 } 493 } 494 495 if (theNanos < 0 || theNanos > 999999999) { 496 throw badTimestampString(s); 497 } 498 499 Timestamp theTimestamp = new Timestamp(theDate.getTime()); 500 theTimestamp.setNanos(theNanos); 501 502 return theTimestamp; 503 } 504 badTimestampString(String s)505 private static IllegalArgumentException badTimestampString(String s) { 506 throw new IllegalArgumentException("Timestamp format must be " + 507 "yyyy-MM-dd HH:mm:ss.fffffffff; was '" + s + "'"); 508 } 509 } 510