Name: gm110360 Date: 02/25/2004
FULL PRODUCT VERSION :
For JDK 1.3:
java version "1.3.1_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_07-b02)
Java HotSpot(TM) Server VM (build 1.3.1_07-b02, mixed mode)
For JDK 1.4:
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06)
Java HotSpot(TM) Server VM (build 1.4.1_02-b06, mixed mode)
FULL OS VERSION :
Linux dev1.yozons.com 2.2.14-VA.2.1 #1 Mon Jul 31 21:58:22 PDT 2000 i686 unknown
A DESCRIPTION OF THE PROBLEM :
I've since discovered that this issue is somehow related to the JDK 1.4's
java.sql.Timestamp object. Under JDK 1.3, it appears to work as advertised,
but this is not true under 1.4. This doesn't appear to be a JDBC driver issue.
For clarity, both JDKs were tested under RedHat Linux using Sun's JDK
1.3.1_07 and Sun's JDK 1.4.1_02.
Here is a quote from the Javadocs on java.sql.Timestamp, and it's the same
wording in the 1.3 and 1.4 javadocs:
"Only integral seconds are stored in the java.util.Date component. The
fractional seconds - the nanos - are separate. The getTime method will
return only integral seconds. If a time value that includes the fractional
seconds is desired, you must convert nanos to milliseconds (nanos/1000000)
and add this to the getTime value."
Why a Timestamp has this odd functionality when it's a subclass of
java.util.Date is beyond me, but they even caution us with the following:
"Due to the differences between the Timestamp class and the java.util.Date
class mentioned above, it is recommended that code not view Timestamp values
generically as an instance of java.util.Date. The inheritance relationship
between Timestamp and java.util.Date really denotes implementation
inheritance, and not type inheritance."
This implies the following code to convert a simple java.sql.Timestamp into
a java.util.Date:
// where 't' is a java.sql.Timestamp object
java.util.Date date = new java.util.Date( t.getTime() +
(t.getNanos()/1000000) );
But, when you read the 1.3 javadocs, there's no mention of the method
getTime() -- and it's only implied through it's inheritance from Date (again
making one wonder why the subclassing only not to allow a Timestamp to be a
Date!).
However, the 1.4 javadocs now have the method getTime() included, presumably
a specialization of the Date's version, and it's documentation reads just
like Date.getTime():
public long getTime()
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
represented by this Timestamp object.
So, what's happening now under JDK 1.4 is that date like April 23, 2003
10:45:58.688 AM PST (long value of 1051119957688) is being treated such that
getTime() returns the full long 1051119957688, and getNanos() returns
688,000,000, so when the addition occurs as before, I'll get a number
counting the milliseconds twice.
Under JDK 1.3 getTime() returns 1051119957000 and getNanos() returns
688,000,000, so the addition of those two values results in the correct time
including milliseconds.
Has anybody else seen this? Is this a Linux version issue or has the
meaning of getTime() actually changed in the two JDKs?
Thanks,
David
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Retrieve a java.sql.Timestamp field from a database using JDBC.
Convert the java.sql.Timestamp to a java.util.Date per the documentation:
new java.util.Date(tstamp.getTime() + (tstamp.getNanos()/1000000))
Under JDK 1.3, the getTime() call works because the millisecond resolution has been removed (the last three digits are set to all zeros) and the nanos contain those "missing" milliseconds as nanoseconds.
Under JDK 1.4, the getTime() calls includes the millisecond resolution that is also reported in the getNanos(), so adding the two parts results in a number that includes the milliseconds twice (so for all timestamps in which the milliseconds is > 500, the resulting time will not even have the same seconds value when displayed).
EXPECTED VERSUS ACTUAL BEHAVIOR :
Both JDK 1.3 and 1.4 should use the same algorithm for Timestamp.getTime(). It appeared to be poorly constructed from the get-go by using inheritance incorrectly. 1.4 should have always overridden getTime() (so it's odd that is' only overridden in 1.4 when its implementation is actually identical to java.util.Date!) so that it could suppress the milliseconds portion that are already accounted for in the nanos.
JDK 1.3:
The getTime() returns a millisecond value like 1051119957688 as 1051119957000 and getNanos() returns 688000000.
JDK 1.4:
The getTime() returns a millisecond value like 1051119957688 as 1051119957688 and getNanos() returns 688000000.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
assuming something like: "select test_time from test_table"
java.sql.Timestamp tstamp = resultSet.getTimestamp(1);
java.util.Date d = new java.util.Date( tstamp.getTime() + (tstamp.getNanos()/1000000) );
Note that under JDK 1.3 the 'tstamp.getTime()' will return the time in milliseconds, but not including the millisecond portion (they are set to all zeros). Under 1.4 the masking does not take place.
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None other than to have alternative code depending on which JDK a program is running under -- either add or don't add the nanos. But this is a high price to convert a Timestamp into a Date -- even though a Timestamp inherits from Date!
The workaround can be a real problem for most code that inspects the time into the subseconds since the values stored in the database don't match the values that come out after the conversion. This is particularly bad for digital signature code that relies on timestamps (which is pretty common).
(Incident Review ID: 184721)
======================================================================
FULL PRODUCT VERSION :
For JDK 1.3:
java version "1.3.1_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_07-b02)
Java HotSpot(TM) Server VM (build 1.3.1_07-b02, mixed mode)
For JDK 1.4:
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06)
Java HotSpot(TM) Server VM (build 1.4.1_02-b06, mixed mode)
FULL OS VERSION :
Linux dev1.yozons.com 2.2.14-VA.2.1 #1 Mon Jul 31 21:58:22 PDT 2000 i686 unknown
A DESCRIPTION OF THE PROBLEM :
I've since discovered that this issue is somehow related to the JDK 1.4's
java.sql.Timestamp object. Under JDK 1.3, it appears to work as advertised,
but this is not true under 1.4. This doesn't appear to be a JDBC driver issue.
For clarity, both JDKs were tested under RedHat Linux using Sun's JDK
1.3.1_07 and Sun's JDK 1.4.1_02.
Here is a quote from the Javadocs on java.sql.Timestamp, and it's the same
wording in the 1.3 and 1.4 javadocs:
"Only integral seconds are stored in the java.util.Date component. The
fractional seconds - the nanos - are separate. The getTime method will
return only integral seconds. If a time value that includes the fractional
seconds is desired, you must convert nanos to milliseconds (nanos/1000000)
and add this to the getTime value."
Why a Timestamp has this odd functionality when it's a subclass of
java.util.Date is beyond me, but they even caution us with the following:
"Due to the differences between the Timestamp class and the java.util.Date
class mentioned above, it is recommended that code not view Timestamp values
generically as an instance of java.util.Date. The inheritance relationship
between Timestamp and java.util.Date really denotes implementation
inheritance, and not type inheritance."
This implies the following code to convert a simple java.sql.Timestamp into
a java.util.Date:
// where 't' is a java.sql.Timestamp object
java.util.Date date = new java.util.Date( t.getTime() +
(t.getNanos()/1000000) );
But, when you read the 1.3 javadocs, there's no mention of the method
getTime() -- and it's only implied through it's inheritance from Date (again
making one wonder why the subclassing only not to allow a Timestamp to be a
Date!).
However, the 1.4 javadocs now have the method getTime() included, presumably
a specialization of the Date's version, and it's documentation reads just
like Date.getTime():
public long getTime()
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
represented by this Timestamp object.
So, what's happening now under JDK 1.4 is that date like April 23, 2003
10:45:58.688 AM PST (long value of 1051119957688) is being treated such that
getTime() returns the full long 1051119957688, and getNanos() returns
688,000,000, so when the addition occurs as before, I'll get a number
counting the milliseconds twice.
Under JDK 1.3 getTime() returns 1051119957000 and getNanos() returns
688,000,000, so the addition of those two values results in the correct time
including milliseconds.
Has anybody else seen this? Is this a Linux version issue or has the
meaning of getTime() actually changed in the two JDKs?
Thanks,
David
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Retrieve a java.sql.Timestamp field from a database using JDBC.
Convert the java.sql.Timestamp to a java.util.Date per the documentation:
new java.util.Date(tstamp.getTime() + (tstamp.getNanos()/1000000))
Under JDK 1.3, the getTime() call works because the millisecond resolution has been removed (the last three digits are set to all zeros) and the nanos contain those "missing" milliseconds as nanoseconds.
Under JDK 1.4, the getTime() calls includes the millisecond resolution that is also reported in the getNanos(), so adding the two parts results in a number that includes the milliseconds twice (so for all timestamps in which the milliseconds is > 500, the resulting time will not even have the same seconds value when displayed).
EXPECTED VERSUS ACTUAL BEHAVIOR :
Both JDK 1.3 and 1.4 should use the same algorithm for Timestamp.getTime(). It appeared to be poorly constructed from the get-go by using inheritance incorrectly. 1.4 should have always overridden getTime() (so it's odd that is' only overridden in 1.4 when its implementation is actually identical to java.util.Date!) so that it could suppress the milliseconds portion that are already accounted for in the nanos.
JDK 1.3:
The getTime() returns a millisecond value like 1051119957688 as 1051119957000 and getNanos() returns 688000000.
JDK 1.4:
The getTime() returns a millisecond value like 1051119957688 as 1051119957688 and getNanos() returns 688000000.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
assuming something like: "select test_time from test_table"
java.sql.Timestamp tstamp = resultSet.getTimestamp(1);
java.util.Date d = new java.util.Date( tstamp.getTime() + (tstamp.getNanos()/1000000) );
Note that under JDK 1.3 the 'tstamp.getTime()' will return the time in milliseconds, but not including the millisecond portion (they are set to all zeros). Under 1.4 the masking does not take place.
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
None other than to have alternative code depending on which JDK a program is running under -- either add or don't add the nanos. But this is a high price to convert a Timestamp into a Date -- even though a Timestamp inherits from Date!
The workaround can be a real problem for most code that inspects the time into the subseconds since the values stored in the database don't match the values that come out after the conversion. This is particularly bad for digital signature code that relies on timestamps (which is pretty common).
(Incident Review ID: 184721)
======================================================================
- relates to
-
JDK-5008227 java.sql.Timestamp.after() is not returning correct result
-
- Resolved
-