1 /* 2 * Copyright (c) 1996, 2011, 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.util.StringTokenizer; 29 30 /** 31 * <P>A thin wrapper around <code>java.util.Date</code> that allows 32 * the JDBC API to identify this as an SQL <code>TIMESTAMP</code> value. 33 * It adds the ability 34 * to hold the SQL <code>TIMESTAMP</code> fractional seconds value, by allowing 35 * the specification of fractional seconds to a precision of nanoseconds. 36 * A Timestamp also provides formatting and 37 * parsing operations to support the JDBC escape syntax for timestamp values. 38 * 39 * <p>The precision of a Timestamp object is calculated to be either: 40 * <ul> 41 * <li><code>19 </code>, which is the number of characters in yyyy-mm-dd hh:mm:ss 42 * <li> <code> 20 + s </code>, which is the number 43 * of characters in the yyyy-mm-dd hh:mm:ss.[fff...] and <code>s</code> represents the scale of the given Timestamp, 44 * its fractional seconds precision. 45 *</ul> 46 * 47 * <P><B>Note:</B> This type is a composite of a <code>java.util.Date</code> and a 48 * separate nanoseconds value. Only integral seconds are stored in the 49 * <code>java.util.Date</code> component. The fractional seconds - the nanos - are 50 * separate. The <code>Timestamp.equals(Object)</code> method never returns 51 * <code>true</code> when passed an object 52 * that isn't an instance of <code>java.sql.Timestamp</code>, 53 * because the nanos component of a date is unknown. 54 * As a result, the <code>Timestamp.equals(Object)</code> 55 * method is not symmetric with respect to the 56 * <code>java.util.Date.equals(Object)</code> 57 * method. Also, the <code>hashCode</code> method uses the underlying 58 * <code>java.util.Date</code> 59 * implementation and therefore does not include nanos in its computation. 60 * <P> 61 * Due to the differences between the <code>Timestamp</code> class 62 * and the <code>java.util.Date</code> 63 * class mentioned above, it is recommended that code not view 64 * <code>Timestamp</code> values generically as an instance of 65 * <code>java.util.Date</code>. The 66 * inheritance relationship between <code>Timestamp</code> 67 * and <code>java.util.Date</code> really 68 * denotes implementation inheritance, and not type inheritance. 69 */ 70 public class Timestamp extends java.util.Date { 71 72 /** 73 * Constructs a <code>Timestamp</code> object initialized 74 * with the given values. 75 * 76 * @param year the year minus 1900 77 * @param month 0 to 11 78 * @param date 1 to 31 79 * @param hour 0 to 23 80 * @param minute 0 to 59 81 * @param second 0 to 59 82 * @param nano 0 to 999,999,999 83 * @deprecated instead use the constructor <code>Timestamp(long millis)</code> 84 * @exception IllegalArgumentException if the nano argument is out of bounds 85 */ 86 @Deprecated Timestamp(int year, int month, int date, int hour, int minute, int second, int nano)87 public Timestamp(int year, int month, int date, 88 int hour, int minute, int second, int nano) { 89 super(year, month, date, hour, minute, second); 90 if (nano > 999999999 || nano < 0) { 91 throw new IllegalArgumentException("nanos > 999999999 or < 0"); 92 } 93 nanos = nano; 94 } 95 96 /** 97 * Constructs a <code>Timestamp</code> object 98 * using a milliseconds time value. The 99 * integral seconds are stored in the underlying date value; the 100 * fractional seconds are stored in the <code>nanos</code> field of 101 * the <code>Timestamp</code> object. 102 * 103 * @param time milliseconds since January 1, 1970, 00:00:00 GMT. 104 * A negative number is the number of milliseconds before 105 * January 1, 1970, 00:00:00 GMT. 106 * @see java.util.Calendar 107 */ Timestamp(long time)108 public Timestamp(long time) { 109 super((time/1000)*1000); 110 nanos = (int)((time%1000) * 1000000); 111 if (nanos < 0) { 112 nanos = 1000000000 + nanos; 113 super.setTime(((time/1000)-1)*1000); 114 } 115 } 116 117 /** 118 * Sets this <code>Timestamp</code> object to represent a point in time that is 119 * <tt>time</tt> milliseconds after January 1, 1970 00:00:00 GMT. 120 * 121 * @param time the number of milliseconds. 122 * @see #getTime 123 * @see #Timestamp(long time) 124 * @see java.util.Calendar 125 */ setTime(long time)126 public void setTime(long time) { 127 super.setTime((time/1000)*1000); 128 nanos = (int)((time%1000) * 1000000); 129 if (nanos < 0) { 130 nanos = 1000000000 + nanos; 131 super.setTime(((time/1000)-1)*1000); 132 } 133 } 134 135 /** 136 * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 137 * represented by this <code>Timestamp</code> object. 138 * 139 * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT 140 * represented by this date. 141 * @see #setTime 142 */ getTime()143 public long getTime() { 144 long time = super.getTime(); 145 return (time + (nanos / 1000000)); 146 } 147 148 149 /** 150 * @serial 151 */ 152 private int nanos; 153 154 /** 155 * Converts a <code>String</code> object in JDBC timestamp escape format to a 156 * <code>Timestamp</code> value. 157 * 158 * @param s timestamp in format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code>. The 159 * fractional seconds may be omitted. The leading zero for <code>mm</code> 160 * and <code>dd</code> may also be omitted. 161 * 162 * @return corresponding <code>Timestamp</code> value 163 * @exception java.lang.IllegalArgumentException if the given argument 164 * does not have the format <code>yyyy-[m]m-[d]d hh:mm:ss[.f...]</code> 165 */ valueOf(String s)166 public static Timestamp valueOf(String s) { 167 final int YEAR_LENGTH = 4; 168 final int MONTH_LENGTH = 2; 169 final int DAY_LENGTH = 2; 170 final int MAX_MONTH = 12; 171 final int MAX_DAY = 31; 172 String date_s; 173 String time_s; 174 String nanos_s; 175 int year = 0; 176 int month = 0; 177 int day = 0; 178 int hour; 179 int minute; 180 int second; 181 int a_nanos = 0; 182 int firstDash; 183 int secondDash; 184 int dividingSpace; 185 int firstColon = 0; 186 int secondColon = 0; 187 int period = 0; 188 String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]"; 189 String zeros = "000000000"; 190 String delimiterDate = "-"; 191 String delimiterTime = ":"; 192 193 if (s == null) throw new java.lang.IllegalArgumentException("null string"); 194 195 // Split the string into date and time components 196 s = s.trim(); 197 dividingSpace = s.indexOf(' '); 198 if (dividingSpace > 0) { 199 date_s = s.substring(0,dividingSpace); 200 time_s = s.substring(dividingSpace+1); 201 } else { 202 throw new java.lang.IllegalArgumentException(formatError); 203 } 204 205 // Parse the date 206 firstDash = date_s.indexOf('-'); 207 secondDash = date_s.indexOf('-', firstDash+1); 208 209 // Parse the time 210 if (time_s == null) 211 throw new java.lang.IllegalArgumentException(formatError); 212 firstColon = time_s.indexOf(':'); 213 secondColon = time_s.indexOf(':', firstColon+1); 214 period = time_s.indexOf('.', secondColon+1); 215 216 // Convert the date 217 boolean parsedDate = false; 218 if ((firstDash > 0) && (secondDash > 0) && (secondDash < date_s.length() - 1)) { 219 String yyyy = date_s.substring(0, firstDash); 220 String mm = date_s.substring(firstDash + 1, secondDash); 221 String dd = date_s.substring(secondDash + 1); 222 if (yyyy.length() == YEAR_LENGTH && 223 (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) && 224 (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { 225 year = Integer.parseInt(yyyy); 226 month = Integer.parseInt(mm); 227 day = Integer.parseInt(dd); 228 229 if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { 230 parsedDate = true; 231 } 232 } 233 } 234 if (! parsedDate) { 235 throw new java.lang.IllegalArgumentException(formatError); 236 } 237 238 // Convert the time; default missing nanos 239 if ((firstColon > 0) & (secondColon > 0) & 240 (secondColon < time_s.length()-1)) { 241 hour = Integer.parseInt(time_s.substring(0, firstColon)); 242 minute = 243 Integer.parseInt(time_s.substring(firstColon+1, secondColon)); 244 if ((period > 0) & (period < time_s.length()-1)) { 245 second = 246 Integer.parseInt(time_s.substring(secondColon+1, period)); 247 nanos_s = time_s.substring(period+1); 248 if (nanos_s.length() > 9) 249 throw new java.lang.IllegalArgumentException(formatError); 250 if (!Character.isDigit(nanos_s.charAt(0))) 251 throw new java.lang.IllegalArgumentException(formatError); 252 nanos_s = nanos_s + zeros.substring(0,9-nanos_s.length()); 253 a_nanos = Integer.parseInt(nanos_s); 254 } else if (period > 0) { 255 throw new java.lang.IllegalArgumentException(formatError); 256 } else { 257 second = Integer.parseInt(time_s.substring(secondColon+1)); 258 } 259 } else { 260 throw new java.lang.IllegalArgumentException(formatError); 261 } 262 263 return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos); 264 } 265 266 /** 267 * Formats a timestamp in JDBC timestamp escape format. 268 * <code>yyyy-mm-dd hh:mm:ss.fffffffff</code>, 269 * where <code>ffffffffff</code> indicates nanoseconds. 270 * <P> 271 * @return a <code>String</code> object in 272 * <code>yyyy-mm-dd hh:mm:ss.fffffffff</code> format 273 */ toString()274 public String toString () { 275 276 int year = super.getYear() + 1900; 277 int month = super.getMonth() + 1; 278 int day = super.getDate(); 279 int hour = super.getHours(); 280 int minute = super.getMinutes(); 281 int second = super.getSeconds(); 282 String yearString; 283 String monthString; 284 String dayString; 285 String hourString; 286 String minuteString; 287 String secondString; 288 String nanosString; 289 String zeros = "000000000"; 290 String yearZeros = "0000"; 291 StringBuffer timestampBuf; 292 293 if (year < 1000) { 294 // Add leading zeros 295 yearString = "" + year; 296 yearString = yearZeros.substring(0, (4-yearString.length())) + 297 yearString; 298 } else { 299 yearString = "" + year; 300 } 301 if (month < 10) { 302 monthString = "0" + month; 303 } else { 304 monthString = Integer.toString(month); 305 } 306 if (day < 10) { 307 dayString = "0" + day; 308 } else { 309 dayString = Integer.toString(day); 310 } 311 if (hour < 10) { 312 hourString = "0" + hour; 313 } else { 314 hourString = Integer.toString(hour); 315 } 316 if (minute < 10) { 317 minuteString = "0" + minute; 318 } else { 319 minuteString = Integer.toString(minute); 320 } 321 if (second < 10) { 322 secondString = "0" + second; 323 } else { 324 secondString = Integer.toString(second); 325 } 326 if (nanos == 0) { 327 nanosString = "0"; 328 } else { 329 nanosString = Integer.toString(nanos); 330 331 // Add leading zeros 332 nanosString = zeros.substring(0, (9-nanosString.length())) + 333 nanosString; 334 335 // Truncate trailing zeros 336 char[] nanosChar = new char[nanosString.length()]; 337 nanosString.getChars(0, nanosString.length(), nanosChar, 0); 338 int truncIndex = 8; 339 while (nanosChar[truncIndex] == '0') { 340 truncIndex--; 341 } 342 343 nanosString = new String(nanosChar, 0, truncIndex + 1); 344 } 345 346 // do a string buffer here instead. 347 timestampBuf = new StringBuffer(20+nanosString.length()); 348 timestampBuf.append(yearString); 349 timestampBuf.append("-"); 350 timestampBuf.append(monthString); 351 timestampBuf.append("-"); 352 timestampBuf.append(dayString); 353 timestampBuf.append(" "); 354 timestampBuf.append(hourString); 355 timestampBuf.append(":"); 356 timestampBuf.append(minuteString); 357 timestampBuf.append(":"); 358 timestampBuf.append(secondString); 359 timestampBuf.append("."); 360 timestampBuf.append(nanosString); 361 362 return (timestampBuf.toString()); 363 } 364 365 /** 366 * Gets this <code>Timestamp</code> object's <code>nanos</code> value. 367 * 368 * @return this <code>Timestamp</code> object's fractional seconds component 369 * @see #setNanos 370 */ getNanos()371 public int getNanos() { 372 return nanos; 373 } 374 375 /** 376 * Sets this <code>Timestamp</code> object's <code>nanos</code> field 377 * to the given value. 378 * 379 * @param n the new fractional seconds component 380 * @exception java.lang.IllegalArgumentException if the given argument 381 * is greater than 999999999 or less than 0 382 * @see #getNanos 383 */ setNanos(int n)384 public void setNanos(int n) { 385 if (n > 999999999 || n < 0) { 386 throw new IllegalArgumentException("nanos > 999999999 or < 0"); 387 } 388 nanos = n; 389 } 390 391 /** 392 * Tests to see if this <code>Timestamp</code> object is 393 * equal to the given <code>Timestamp</code> object. 394 * 395 * @param ts the <code>Timestamp</code> value to compare with 396 * @return <code>true</code> if the given <code>Timestamp</code> 397 * object is equal to this <code>Timestamp</code> object; 398 * <code>false</code> otherwise 399 */ equals(Timestamp ts)400 public boolean equals(Timestamp ts) { 401 if (super.equals(ts)) { 402 if (nanos == ts.nanos) { 403 return true; 404 } else { 405 return false; 406 } 407 } else { 408 return false; 409 } 410 } 411 412 /** 413 * Tests to see if this <code>Timestamp</code> object is 414 * equal to the given object. 415 * 416 * This version of the method <code>equals</code> has been added 417 * to fix the incorrect 418 * signature of <code>Timestamp.equals(Timestamp)</code> and to preserve backward 419 * compatibility with existing class files. 420 * 421 * Note: This method is not symmetric with respect to the 422 * <code>equals(Object)</code> method in the base class. 423 * 424 * @param ts the <code>Object</code> value to compare with 425 * @return <code>true</code> if the given <code>Object</code> is an instance 426 * of a <code>Timestamp</code> that 427 * is equal to this <code>Timestamp</code> object; 428 * <code>false</code> otherwise 429 */ equals(java.lang.Object ts)430 public boolean equals(java.lang.Object ts) { 431 if (ts instanceof Timestamp) { 432 return this.equals((Timestamp)ts); 433 } else { 434 return false; 435 } 436 } 437 438 /** 439 * Indicates whether this <code>Timestamp</code> object is 440 * earlier than the given <code>Timestamp</code> object. 441 * 442 * @param ts the <code>Timestamp</code> value to compare with 443 * @return <code>true</code> if this <code>Timestamp</code> object is earlier; 444 * <code>false</code> otherwise 445 */ before(Timestamp ts)446 public boolean before(Timestamp ts) { 447 return compareTo(ts) < 0; 448 } 449 450 /** 451 * Indicates whether this <code>Timestamp</code> object is 452 * later than the given <code>Timestamp</code> object. 453 * 454 * @param ts the <code>Timestamp</code> value to compare with 455 * @return <code>true</code> if this <code>Timestamp</code> object is later; 456 * <code>false</code> otherwise 457 */ after(Timestamp ts)458 public boolean after(Timestamp ts) { 459 return compareTo(ts) > 0; 460 } 461 462 /** 463 * Compares this <code>Timestamp</code> object to the given 464 * <code>Timestamp</code> object. 465 * 466 * @param ts the <code>Timestamp</code> object to be compared to 467 * this <code>Timestamp</code> object 468 * @return the value <code>0</code> if the two <code>Timestamp</code> 469 * objects are equal; a value less than <code>0</code> if this 470 * <code>Timestamp</code> object is before the given argument; 471 * and a value greater than <code>0</code> if this 472 * <code>Timestamp</code> object is after the given argument. 473 * @since 1.4 474 */ compareTo(Timestamp ts)475 public int compareTo(Timestamp ts) { 476 long thisTime = this.getTime(); 477 long anotherTime = ts.getTime(); 478 int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1)); 479 if (i == 0) { 480 if (nanos > ts.nanos) { 481 return 1; 482 } else if (nanos < ts.nanos) { 483 return -1; 484 } 485 } 486 return i; 487 488 } 489 490 /** 491 * Compares this <code>Timestamp</code> object to the given 492 * <code>Date</code> object. 493 * 494 * @param o the <code>Date</code> to be compared to 495 * this <code>Timestamp</code> object 496 * @return the value <code>0</code> if this <code>Timestamp</code> object 497 * and the given object are equal; a value less than <code>0</code> 498 * if this <code>Timestamp</code> object is before the given argument; 499 * and a value greater than <code>0</code> if this 500 * <code>Timestamp</code> object is after the given argument. 501 * 502 * @since 1.5 503 */ compareTo(java.util.Date o)504 public int compareTo(java.util.Date o) { 505 if(o instanceof Timestamp) { 506 // When Timestamp instance compare it with a Timestamp 507 // Hence it is basically calling this.compareTo((Timestamp))o); 508 // Note typecasting is safe because o is instance of Timestamp 509 return compareTo((Timestamp)o); 510 } else { 511 // When Date doing a o.compareTo(this) 512 // will give wrong results. 513 Timestamp ts = new Timestamp(o.getTime()); 514 return this.compareTo(ts); 515 } 516 } 517 518 /** 519 * {@inheritDoc} 520 * 521 * The {@code hashCode} method uses the underlying {@code java.util.Date} 522 * implementation and therefore does not include nanos in its computation. 523 * 524 */ 525 @Override hashCode()526 public int hashCode() { 527 return super.hashCode(); 528 } 529 530 static final long serialVersionUID = 2745179027874758501L; 531 532 } 533