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