JDK-5103041 : Regression: binary compatibility problem with java.sql.Timestamp.compareTo
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.sql
  • Affected Version: 5.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,linux,windows_xp
  • CPU: generic,x86
  • Submitted: 2004-09-16
  • Updated: 2017-05-16
  • Resolved: 2006-03-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
5.0u7Fixed 6 b59Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
Just to make this concrete, here's a small program demonstrating the
problem.  Compile and run it, first against 1.4.2, then against
1.5/5.0:

import java.sql.Timestamp;
import java.util.Date;

public class Foo {
  public static void main(String[] args) {
    Date d = new Date();
    Timestamp ts = new Timestamp(d.getTime());
    System.out.println(ts.compareTo(d));
  }
}

Note that this a *binary compatibility problem*!!  The overload
resolution is the same in 1.4.2 and 1.5.0;  in either case, it's
compareTo(Date).  With a 1.4.2 runtime, it dispatches to the version
defined in java.util.Date, with a 1.5.0 runtime it dispatches to the
version defined in java.sql.Timestamp, which promptly casts its
argument to Timestamp (and blows up).  This really feels like a P2 bug
to me.

Comments
EVALUATION Fix integrated in jdk 5.0u7 as of Jan 3. This should be available in the builds of jdk5.0u7 after Jan 3.
17-01-2006

EVALUATION This is getting fixed in Mustang and jdk5 update release as well. I have got the approval and this fix should be in soon. All those requesting for this fix watch out for this fix and comeback with your comments.
13-10-2005

SUGGESTED FIX public int compareTo(java.util.Date d) { if (d instanceof Timestamp) return compareTo((Timestamp)d); else return - (d.compareTo(this)); } ###@###.### 2005-06-07 19:02:25 GMT
07-06-2005

EVALUATION A very interesting bug... here's what I think happened. In the fix for 4227983 (java.sql.Timestamp didn't overload compareTo, setTime and getTime, delta 1.35, 2000/02/02, integrated into 1.4.0), two compareTo methods were added to the Timestamp class, one taking a Timestamp and the other taking an Object. The compareTo(Object) method was specified as follows: * Compares this Timestamp to another Object. If the Object is * a Date, this function behaves like compareTo(Timestamp). * Otherwise, it throws aClassCastException (as Timestamps are * comparable only to other Timestamps) * ... The intent of this change was to allow Timestamps to be compared to Dates. A later fix, for 4631234 (java.sql.Timestamp.compareTo no longer accepts java.util.Dates in 1.4, delta 1.46 2002/04/01, integrated into 1.4.1), revised the specification of the compareTo(Object) method instead to say: * Compares this <code>Timestamp</code> object to the given * <code>Object</code>, which must be a <code>Timestamp</code> * object. If the argument is not a <code>Timestamp</code> object, * this method throws a <code>ClassCastException</code> object. * (<code>Timestamp</code> objects are * comparable only to other <code>Timestamp</code> objects.) In other words, the intent of this change was to require a ClassCastException to be thrown when comparing a Timestamp to a Date. Such behavior would cause the submitter's code to fail. The change for 4631234 did not modify any code. It appears to have been made under the assumption that the existing code already behaved according to the new specification. Due to the presence of a compareTo(Date) method in the Date superclass, however, such comparisons were actually allowed, and so the submitter's code actually worked... until recently. The generification changes for Tiger (4964490) added the compareTo(Date) method to Timestamp in order to preserve what was assumed (from the specification) to be the current behavior; unfortunately the fact that the current behavior was different from the specified behavior was not noticed at that time. That new method is what is now causing the submitter's code to receive the (specified) ClassCastException. A reasonable fix for this bug would be to revise the specification of the Timestamp.compareTo(Date) method to allow Timestamp to be comparable to Dates once again, and to redefine that method as public int compareTo(java.util.Date d) { return d.compareTo(this); } (Of course the real problem here is that java.sql.Timestamp should not have been made a subclass of java.util.Date in the first place, as explained in item 7 of Bloch's "Effective Java", but that's water under the bridge.) -- ###@###.### 2004/9/16 I agree with the above comprehensive assesment, and I'll work with the JCK team and 1.5.x team to get fix for this. A specific determination as to the most ideal delivery vehicle still has to be determined, but likely Mustang at this point. ###@###.### 2004-09-16 I am looking into fixing this bug. The following lines will be getting removed from javadocs for the method public int compareTo(java.util.Date o) * @exception ClassCastException if the argument is not a * <code>Timestamp</code> object * @since 1.5 */ // This forwarding method ensures that the compareTo(Date) method defined // in java.util.Date is not invoked on a Timestamp ###@###.### 2005-05-02 05:25:12 GMT 1. I will be removing the above lines from mustang i.e. jdk 6.0 and will not be doing the changes in jdk5.0uX versions. I hope that is fine. ###@###.### 2005-06-07 13:58:51 GMT 2. Also will change the method code to, in addition to above :- public int compareTo(java.util.Date d) { return - (d.compareTo(this)); } ###@###.### 2005-06-07 14:39:59 GMT Since this is a regression and a binary incompatibility, it is not unlikely that we will get requests for backports. So I think a backport to a Tiger update must be considered. The proposed solution is problematic: java.util.Date d1 = new java.sql.Timestamp(...); java.util.Date d2 = new java.sql.Timestamp(...); d1.compareTo(d2); // stack overflow See "Suggested Fix" section for an alternative solution. ###@###.### 2005-06-07 19:02:25 GMT I propose the following fix, other fixes propose either don't work with 1.4.x and 1.5.x or if they work don't produce the same results. Index: src/java/sql/Timestamp.java (cvs diff -wu Timestamp.java) =================================================================== RCS file: /m/jws/jdbc4.0/src/java/sql/Timestamp.java,v retrieving revision 1.5 diff -w -u -r1.5 Timestamp.java --- src/java/sql/Timestamp.java 18 Jul 2005 19:33:37 -0000 1.5 +++ src/java/sql/Timestamp.java 21 Jul 2005 11:13:21 -0000 @@ -462,14 +462,22 @@ * <code>Timestamp</code> object * @since 1.5 */ - // This forwarding method ensures that the compareTo(Date) method defined - // in java.util.Date is not invoked on a Timestamp public int compareTo(java.util.Date o) { - return compareTo((Timestamp)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); + } + } static final long serialVersionUID = 2745179027874758501L; The test cases against which I verified the fix are already as given here and as below. /////////////// Test Case 1 /////////////////////// import java.sql.Timestamp; import java.util.Date; public class Foo { public static void main(String[] args) { Date d = new Date(); Timestamp ts = new Timestamp(d.getTime()); System.out.println(ts.compareTo(d)); } } /////////////// Test Case 2 /////////////////////// import java.sql.Timestamp; import java.util.Date; public class Foo1 { public static void main(String[] args) { Date d1 = new Date(); java.util.Date d1 = new java.sql.Timestamp(d.getTime()); java.util.Date d2 = new java.sql.Timestamp(d.getTime()); System.out.println("Value is : "+d1.compareTo(d2)); } } Except the comments(//This forwarding) which I am not sure can be backported, I think this fix of the code can be backported without problems, if required. Please comment for the fix.
22-09-2004