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