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.Locale; 24 import 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 */ 45 public 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)} instead. 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 Timestamp(int theYear, int theMonth, int theDate, int theHour, int theMinute, int theSecond, int theNano)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 */ Timestamp(long theTime)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 */ after(Timestamp theTimestamp)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 */ before(Timestamp theTimestamp)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 compareTo(Date theObject)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 */ compareTo(Timestamp theTimestamp)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 equals(Object theObject)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 */ equals(Timestamp theTimestamp)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 */ getNanos()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 getTime()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 */ setNanos(int n)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 setTime(long theTime)319 public void setTime(long theTime) { 320 setTimeImpl(theTime); 321 } 322 setTimeImpl(long theTime)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 toString()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 */ format(int date, int digits, StringBuilder sb)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 */ valueOf(String s)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 date; 428 try { 429 date = df.parse(s, pp); 430 } catch (Exception e) { 431 throw badTimestampString(s); 432 } 433 434 if (date == 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 nanos; 449 450 if (remaining == 0) { 451 // First, allow for the case where no fraction of a second is given: 452 nanos = 0; 453 } else { 454 // Validate the string is in the range ".0" to ".999999999" 455 if (remaining < 2 || remaining > 10 || s.charAt(position) != '.') { 456 throw badTimestampString(s); 457 } 458 try { 459 nanos = Integer.parsePositiveInt(s.substring(position + 1)); 460 } catch (NumberFormatException e) { 461 throw badTimestampString(s); 462 } 463 // We must adjust for the cases where the nanos String was not 9 464 // characters long (i.e. ".123" means 123000000 nanos) 465 if (nanos != 0) { 466 for (int i = remaining - 1; i < 9; i++) { 467 nanos *= 10; 468 } 469 } 470 } 471 472 Timestamp timestamp = new Timestamp(date.getTime()); 473 timestamp.setNanos(nanos); 474 475 return timestamp; 476 } 477 badTimestampString(String s)478 private static IllegalArgumentException badTimestampString(String s) { 479 return new IllegalArgumentException("Timestamp format must be " + 480 "yyyy-MM-dd HH:mm:ss.fffffffff; was '" + s + "'"); 481 } 482 } 483