| /* | |
| * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package java.sql; | |
| import java.time.Instant; | |
| import java.time.LocalDateTime; | |
| /** | |
| * <P>A thin wrapper around {@code java.util.Date} that allows | |
| * the JDBC API to identify this as an SQL {@code TIMESTAMP} value. | |
| * It adds the ability | |
| * to hold the SQL {@code TIMESTAMP} fractional seconds value, by allowing | |
| * the specification of fractional seconds to a precision of nanoseconds. | |
| * A Timestamp also provides formatting and | |
| * parsing operations to support the JDBC escape syntax for timestamp values. | |
| * | |
| * <p>The precision of a Timestamp object is calculated to be either: | |
| * <ul> | |
| * <li>{@code 19 }, which is the number of characters in yyyy-mm-dd hh:mm:ss | |
| * <li> {@code 20 + s }, which is the number | |
| * of characters in the yyyy-mm-dd hh:mm:ss.[fff...] and {@code s} represents the scale of the given Timestamp, | |
| * its fractional seconds precision. | |
| *</ul> | |
| * | |
| * <P><B>Note:</B> This type is a composite of a {@code java.util.Date} and a | |
| * separate nanoseconds value. Only integral seconds are stored in the | |
| * {@code java.util.Date} component. The fractional seconds - the nanos - are | |
| * separate. The {@code Timestamp.equals(Object)} method never returns | |
| * {@code true} when passed an object | |
| * that isn't an instance of {@code java.sql.Timestamp}, | |
| * because the nanos component of a date is unknown. | |
| * As a result, the {@code Timestamp.equals(Object)} | |
| * method is not symmetric with respect to the | |
| * {@code java.util.Date.equals(Object)} | |
| * method. Also, the {@code hashCode} method uses the underlying | |
| * {@code java.util.Date} | |
| * implementation and therefore does not include nanos in its computation. | |
| * <P> | |
| * Due to the differences between the {@code Timestamp} class | |
| * and the {@code java.util.Date} | |
| * class mentioned above, it is recommended that code not view | |
| * {@code Timestamp} values generically as an instance of | |
| * {@code java.util.Date}. The | |
| * inheritance relationship between {@code Timestamp} | |
| * and {@code java.util.Date} really | |
| * denotes implementation inheritance, and not type inheritance. | |
| * | |
| * @since 1.1 | |
| */ | |
| public class Timestamp extends java.util.Date { | |
| /** | |
| * Constructs a {@code Timestamp} object initialized | |
| * with the given values. | |
| * | |
| * @param year the year minus 1900 | |
| * @param month 0 to 11 | |
| * @param date 1 to 31 | |
| * @param hour 0 to 23 | |
| * @param minute 0 to 59 | |
| * @param second 0 to 59 | |
| * @param nano 0 to 999,999,999 | |
| * @deprecated instead use the constructor {@code Timestamp(long millis)} | |
| * @throws IllegalArgumentException if the nano argument is out of bounds | |
| */ | |
| @Deprecated(since="1.2") | |
| public Timestamp(int year, int month, int date, | |
| int hour, int minute, int second, int nano) { | |
| super(year, month, date, hour, minute, second); | |
| if (nano > 999999999 || nano < 0) { | |
| throw new IllegalArgumentException("nanos > 999999999 or < 0"); | |
| } | |
| nanos = nano; | |
| } | |
| /** | |
| * Constructs a {@code Timestamp} object | |
| * using a milliseconds time value. The | |
| * integral seconds are stored in the underlying date value; the | |
| * fractional seconds are stored in the {@code nanos} field of | |
| * the {@code Timestamp} object. | |
| * | |
| * @param time milliseconds since January 1, 1970, 00:00:00 GMT. | |
| * A negative number is the number of milliseconds before | |
| * January 1, 1970, 00:00:00 GMT. | |
| * @see java.util.Calendar | |
| */ | |
| public Timestamp(long time) { | |
| super((time/1000)*1000); | |
| nanos = (int)((time%1000) * 1000000); | |
| if (nanos < 0) { | |
| nanos = 1000000000 + nanos; | |
| super.setTime(((time/1000)-1)*1000); | |
| } | |
| } | |
| /** | |
| * Sets this {@code Timestamp} object to represent a point in time that is | |
| * {@code time} milliseconds after January 1, 1970 00:00:00 GMT. | |
| * | |
| * @param time the number of milliseconds. | |
| * @see #getTime | |
| * @see #Timestamp(long time) | |
| * @see java.util.Calendar | |
| */ | |
| public void setTime(long time) { | |
| super.setTime((time/1000)*1000); | |
| nanos = (int)((time%1000) * 1000000); | |
| if (nanos < 0) { | |
| nanos = 1000000000 + nanos; | |
| super.setTime(((time/1000)-1)*1000); | |
| } | |
| } | |
| /** | |
| * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT | |
| * represented by this {@code Timestamp} object. | |
| * | |
| * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT | |
| * represented by this date. | |
| * @see #setTime | |
| */ | |
| public long getTime() { | |
| long time = super.getTime(); | |
| return (time + (nanos / 1000000)); | |
| } | |
| /** | |
| * @serial | |
| */ | |
| private int nanos; | |
| /** | |
| * Converts a {@code String} object in JDBC timestamp escape format to a | |
| * {@code Timestamp} value. | |
| * | |
| * @param s timestamp in format {@code yyyy-[m]m-[d]d hh:mm:ss[.f...]}. The | |
| * fractional seconds may be omitted. The leading zero for {@code mm} | |
| * and {@code dd} may also be omitted. | |
| * | |
| * @return corresponding {@code Timestamp} value | |
| * @throws java.lang.IllegalArgumentException if the given argument | |
| * does not have the format {@code yyyy-[m]m-[d]d hh:mm:ss[.f...]} | |
| */ | |
| public static Timestamp valueOf(String s) { | |
| final int YEAR_LENGTH = 4; | |
| final int MONTH_LENGTH = 2; | |
| final int DAY_LENGTH = 2; | |
| final int MAX_MONTH = 12; | |
| final int MAX_DAY = 31; | |
| int year = 0; | |
| int month = 0; | |
| int day = 0; | |
| int hour; | |
| int minute; | |
| int second; | |
| int a_nanos = 0; | |
| int firstDash; | |
| int secondDash; | |
| int dividingSpace; | |
| int firstColon; | |
| int secondColon; | |
| int period; | |
| String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]"; | |
| if (s == null) throw new java.lang.IllegalArgumentException("null string"); | |
| // Split the string into date and time components | |
| s = s.trim(); | |
| dividingSpace = s.indexOf(' '); | |
| if (dividingSpace < 0) { | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| } | |
| // Parse the date | |
| firstDash = s.indexOf('-'); | |
| secondDash = s.indexOf('-', firstDash+1); | |
| // Parse the time | |
| firstColon = s.indexOf(':', dividingSpace + 1); | |
| secondColon = s.indexOf(':', firstColon + 1); | |
| period = s.indexOf('.', secondColon + 1); | |
| // Convert the date | |
| boolean parsedDate = false; | |
| if (firstDash > 0 && secondDash > 0 && secondDash < dividingSpace - 1) { | |
| if (firstDash == YEAR_LENGTH && | |
| (secondDash - firstDash > 1 && secondDash - firstDash <= MONTH_LENGTH + 1) && | |
| (dividingSpace - secondDash > 1 && dividingSpace - secondDash <= DAY_LENGTH + 1)) { | |
| year = Integer.parseInt(s, 0, firstDash, 10); | |
| month = Integer.parseInt(s, firstDash + 1, secondDash, 10); | |
| day = Integer.parseInt(s, secondDash + 1, dividingSpace, 10); | |
| if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { | |
| parsedDate = true; | |
| } | |
| } | |
| } | |
| if (! parsedDate) { | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| } | |
| // Convert the time; default missing nanos | |
| int len = s.length(); | |
| if (firstColon > 0 && secondColon > 0 && secondColon < len - 1) { | |
| hour = Integer.parseInt(s, dividingSpace + 1, firstColon, 10); | |
| minute = Integer.parseInt(s, firstColon + 1, secondColon, 10); | |
| if (period > 0 && period < len - 1) { | |
| second = Integer.parseInt(s, secondColon + 1, period, 10); | |
| int nanoPrecision = len - (period + 1); | |
| if (nanoPrecision > 9) | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| if (!Character.isDigit(s.charAt(period + 1))) | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| int tmpNanos = Integer.parseInt(s, period + 1, len, 10); | |
| while (nanoPrecision < 9) { | |
| tmpNanos *= 10; | |
| nanoPrecision++; | |
| } | |
| a_nanos = tmpNanos; | |
| } else if (period > 0) { | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| } else { | |
| second = Integer.parseInt(s, secondColon + 1, len, 10); | |
| } | |
| } else { | |
| throw new java.lang.IllegalArgumentException(formatError); | |
| } | |
| return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos); | |
| } | |
| /** | |
| * Formats a timestamp in JDBC timestamp escape format. | |
| * {@code yyyy-mm-dd hh:mm:ss.fffffffff}, | |
| * where {@code fffffffff} indicates nanoseconds. | |
| * | |
| * @return a {@code String} object in | |
| * {@code yyyy-mm-dd hh:mm:ss.fffffffff} format | |
| */ | |
| @SuppressWarnings("deprecation") | |
| public String toString() { | |
| int year = super.getYear() + 1900; | |
| int month = super.getMonth() + 1; | |
| int day = super.getDate(); | |
| int hour = super.getHours(); | |
| int minute = super.getMinutes(); | |
| int second = super.getSeconds(); | |
| int trailingZeros = 0; | |
| int tmpNanos = nanos; | |
| if (tmpNanos == 0) { | |
| trailingZeros = 8; | |
| } else { | |
| while (tmpNanos % 10 == 0) { | |
| tmpNanos /= 10; | |
| trailingZeros++; | |
| } | |
| } | |
| // 8058429: To comply with current JCK tests, we need to deal with year | |
| // being any number between 0 and 292278995 | |
| int count = 10000; | |
| int yearSize = 4; | |
| do { | |
| if (year < count) { | |
| break; | |
| } | |
| yearSize++; | |
| count *= 10; | |
| } while (count < 1000000000); | |
| char[] buf = new char[25 + yearSize - trailingZeros]; | |
| Date.formatDecimalInt(year, buf, 0, yearSize); | |
| buf[yearSize] = '-'; | |
| Date.formatDecimalInt(month, buf, yearSize + 1, 2); | |
| buf[yearSize + 3] = '-'; | |
| Date.formatDecimalInt(day, buf, yearSize + 4, 2); | |
| buf[yearSize + 6] = ' '; | |
| Date.formatDecimalInt(hour, buf, yearSize + 7, 2); | |
| buf[yearSize + 9] = ':'; | |
| Date.formatDecimalInt(minute, buf, yearSize + 10, 2); | |
| buf[yearSize + 12] = ':'; | |
| Date.formatDecimalInt(second, buf, yearSize + 13, 2); | |
| buf[yearSize + 15] = '.'; | |
| Date.formatDecimalInt(tmpNanos, buf, yearSize + 16, 9 - trailingZeros); | |
| return new String(buf); | |
| } | |
| /** | |
| * Gets this {@code Timestamp} object's {@code nanos} value. | |
| * | |
| * @return this {@code Timestamp} object's fractional seconds component | |
| * @see #setNanos | |
| */ | |
| public int getNanos() { | |
| return nanos; | |
| } | |
| /** | |
| * Sets this {@code Timestamp} object's {@code nanos} field | |
| * to the given value. | |
| * | |
| * @param n the new fractional seconds component | |
| * @throws java.lang.IllegalArgumentException if the given argument | |
| * is greater than 999999999 or less than 0 | |
| * @see #getNanos | |
| */ | |
| public void setNanos(int n) { | |
| if (n > 999999999 || n < 0) { | |
| throw new IllegalArgumentException("nanos > 999999999 or < 0"); | |
| } | |
| nanos = n; | |
| } | |
| /** | |
| * Tests to see if this {@code Timestamp} object is | |
| * equal to the given {@code Timestamp} object. | |
| * | |
| * @param ts the {@code Timestamp} value to compare with | |
| * @return {@code true} if the given {@code Timestamp} | |
| * object is equal to this {@code Timestamp} object; | |
| * {@code false} otherwise | |
| */ | |
| public boolean equals(Timestamp ts) { | |
| if (super.equals(ts)) { | |
| if (nanos == ts.nanos) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } else { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Tests to see if this {@code Timestamp} object is | |
| * equal to the given object. | |
| * | |
| * This version of the method {@code equals} has been added | |
| * to fix the incorrect | |
| * signature of {@code Timestamp.equals(Timestamp)} and to preserve backward | |
| * compatibility with existing class files. | |
| * | |
| * Note: This method is not symmetric with respect to the | |
| * {@code equals(Object)} method in the base class. | |
| * | |
| * @param ts the {@code Object} value to compare with | |
| * @return {@code true} if the given {@code Object} is an instance | |
| * of a {@code Timestamp} that | |
| * is equal to this {@code Timestamp} object; | |
| * {@code false} otherwise | |
| */ | |
| public boolean equals(java.lang.Object ts) { | |
| if (ts instanceof Timestamp) { | |
| return this.equals((Timestamp)ts); | |
| } else { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Indicates whether this {@code Timestamp} object is | |
| * earlier than the given {@code Timestamp} object. | |
| * | |
| * @param ts the {@code Timestamp} value to compare with | |
| * @return {@code true} if this {@code Timestamp} object is earlier; | |
| * {@code false} otherwise | |
| */ | |
| public boolean before(Timestamp ts) { | |
| return compareTo(ts) < 0; | |
| } | |
| /** | |
| * Indicates whether this {@code Timestamp} object is | |
| * later than the given {@code Timestamp} object. | |
| * | |
| * @param ts the {@code Timestamp} value to compare with | |
| * @return {@code true} if this {@code Timestamp} object is later; | |
| * {@code false} otherwise | |
| */ | |
| public boolean after(Timestamp ts) { | |
| return compareTo(ts) > 0; | |
| } | |
| /** | |
| * Compares this {@code Timestamp} object to the given | |
| * {@code Timestamp} object. | |
| * | |
| * @param ts the {@code Timestamp} object to be compared to | |
| * this {@code Timestamp} object | |
| * @return the value {@code 0} if the two {@code Timestamp} | |
| * objects are equal; a value less than {@code 0} if this | |
| * {@code Timestamp} object is before the given argument; | |
| * and a value greater than {@code 0} if this | |
| * {@code Timestamp} object is after the given argument. | |
| * @since 1.4 | |
| */ | |
| public int compareTo(Timestamp ts) { | |
| long thisTime = this.getTime(); | |
| long anotherTime = ts.getTime(); | |
| int i = (thisTime<anotherTime ? -1 :(thisTime==anotherTime?0 :1)); | |
| if (i == 0) { | |
| if (nanos > ts.nanos) { | |
| return 1; | |
| } else if (nanos < ts.nanos) { | |
| return -1; | |
| } | |
| } | |
| return i; | |
| } | |
| /** | |
| * Compares this {@code Timestamp} object to the given | |
| * {@code Date} object. | |
| * | |
| * @param o the {@code Date} to be compared to | |
| * this {@code Timestamp} object | |
| * @return the value {@code 0} if this {@code Timestamp} object | |
| * and the given object are equal; a value less than {@code 0} | |
| * if this {@code Timestamp} object is before the given argument; | |
| * and a value greater than {@code 0} if this | |
| * {@code Timestamp} object is after the given argument. | |
| * | |
| * @since 1.5 | |
| */ | |
| public int compareTo(java.util.Date o) { | |
| if(o instanceof Timestamp) { | |
| // When Timestamp instance compare it with a Timestamp | |
| // Hence it is basically calling this.compareTo((Timestamp))o); | |
| // Note typecasting is safe because o is instance of Timestamp | |
| return compareTo((Timestamp)o); | |
| } else { | |
| // When Date doing a o.compareTo(this) | |
| // will give wrong results. | |
| Timestamp ts = new Timestamp(o.getTime()); | |
| return this.compareTo(ts); | |
| } | |
| } | |
| /** | |
| * {@inheritDoc} | |
| * | |
| * The {@code hashCode} method uses the underlying {@code java.util.Date} | |
| * implementation and therefore does not include nanos in its computation. | |
| * | |
| */ | |
| @Override | |
| public int hashCode() { | |
| return super.hashCode(); | |
| } | |
| static final long serialVersionUID = 2745179027874758501L; | |
| private static final int MILLIS_PER_SECOND = 1000; | |
| /** | |
| * Obtains an instance of {@code Timestamp} from a {@code LocalDateTime} | |
| * object, with the same year, month, day of month, hours, minutes, | |
| * seconds and nanos date-time value as the provided {@code LocalDateTime}. | |
| * <p> | |
| * The provided {@code LocalDateTime} is interpreted as the local | |
| * date-time in the local time zone. | |
| * | |
| * @param dateTime a {@code LocalDateTime} to convert | |
| * @return a {@code Timestamp} object | |
| * @throws NullPointerException if {@code dateTime} is null. | |
| * @since 1.8 | |
| */ | |
| public static Timestamp valueOf(LocalDateTime dateTime) { | |
| return new Timestamp(dateTime.getYear() - 1900, | |
| dateTime.getMonthValue() - 1, | |
| dateTime.getDayOfMonth(), | |
| dateTime.getHour(), | |
| dateTime.getMinute(), | |
| dateTime.getSecond(), | |
| dateTime.getNano()); | |
| } | |
| /** | |
| * Converts this {@code Timestamp} object to a {@code LocalDateTime}. | |
| * <p> | |
| * The conversion creates a {@code LocalDateTime} that represents the | |
| * same year, month, day of month, hours, minutes, seconds and nanos | |
| * date-time value as this {@code Timestamp} in the local time zone. | |
| * | |
| * @return a {@code LocalDateTime} object representing the same date-time value | |
| * @since 1.8 | |
| */ | |
| @SuppressWarnings("deprecation") | |
| public LocalDateTime toLocalDateTime() { | |
| return LocalDateTime.of(getYear() + 1900, | |
| getMonth() + 1, | |
| getDate(), | |
| getHours(), | |
| getMinutes(), | |
| getSeconds(), | |
| getNanos()); | |
| } | |
| /** | |
| * Obtains an instance of {@code Timestamp} from an {@link Instant} object. | |
| * <p> | |
| * {@code Instant} can store points on the time-line further in the future | |
| * and further in the past than {@code Date}. In this scenario, this method | |
| * will throw an exception. | |
| * | |
| * @param instant the instant to convert | |
| * @return an {@code Timestamp} representing the same point on the time-line as | |
| * the provided instant | |
| * @throws NullPointerException if {@code instant} is null. | |
| * @throws IllegalArgumentException if the instant is too large to | |
| * represent as a {@code Timestamp} | |
| * @since 1.8 | |
| */ | |
| public static Timestamp from(Instant instant) { | |
| try { | |
| Timestamp stamp = new Timestamp(Math.multiplyExact(instant.getEpochSecond(), MILLIS_PER_SECOND)); | |
| stamp.nanos = instant.getNano(); | |
| return stamp; | |
| } catch (ArithmeticException ex) { | |
| throw new IllegalArgumentException(ex); | |
| } | |
| } | |
| /** | |
| * Converts this {@code Timestamp} object to an {@code Instant}. | |
| * <p> | |
| * The conversion creates an {@code Instant} that represents the same | |
| * point on the time-line as this {@code Timestamp}. | |
| * | |
| * @return an instant representing the same point on the time-line | |
| * @since 1.8 | |
| */ | |
| @Override | |
| public Instant toInstant() { | |
| return Instant.ofEpochSecond(super.getTime() / MILLIS_PER_SECOND, nanos); | |
| } | |
| } |