Comparing java.sql.Date values

   Before we move onto how Microsoft SQL Server 2005 JDBC driver handles Calendar arguments, let's take a look at Date, Time and Timestamp objects to make sure we are on the same page. Date, Time and Timestamp are the three temporal classes contained in java.sql package. All three extend java.util.Date base class and hence store time as milliseconds since the epoch in UTC. Of these three, Timestamp is the most similar to java.util.Date class as it contains all the functionality of java.util.Date while adding a nanoseconds field on top. Date and Time, on the other hand, use only related components of the java.util.Date class. Let's take Date as an example. Suppose you issue the following code snippet.

  java.sql.Date dateA = java.sql.Date.valueOf("2008-06-18");
System.out.println("Date value : " + dateA); //prints out 2008-06-18
System.out.println("Converted to Timestamp : " + new Timestamp(dateA.getTime()));

You will receive a Date value of "2008-06-18". The Timestamp value will contain the same date value with the time portions set to midnight, "2008-06-18 00:00:00.0". What happens if the conversion proceeds in the other direction? Take a look at the following.

  Timestamp ts = Timestamp.valueOf("2008-06-18 21:28:08.963");
java.sql.Date dateB = new java.sql.Date(ts.getTime());
System.out.println("Converted to Date : " + dateB); //prints out 2008-06-18
System.out.println("Equality with the first Date : " + dateA.equals(dateB)); //prints out false

Both dateA and dateB represent and print out the same dates. Why, then, do they compare as unequal? Well, the reason again lies in how time is stored in a Date object. When you construct the Date object you are giving it a milliseconds since the epoch value. These milliseconds are internally stored without being normalized. Since we did not normalize the time components of this object either, i.e. subtract enough millis so that the time field is set to midnight, the resulting Date object ends up storing time information as well. When it comes to testing for equality, the millis stored by both objects are compared and dateB is found to contain more millis than dateA since it also stores the time information. You might wonder why Date constructor does not normalize the time components. I did. If this object was truly intended to carry only Date information, why would you let it store time information as well? If you also need the time information, why would you not operate on Timestamp instead? Well, this is a very simplistic approach to this issue and things get much more complicated when it comes to converting date values between timezones. Then, you realize you need the time information as well.

How would one compare two date values then? It depends on how you interpret the Date object. If you would like to ignore the time components as intended by Java, then you can use a Calendar object to retrieve the date components and compare them individually as in the following example.

     Calendar calA = Calendar.getInstance();
calA.setTimeInMillis(dateA.getTime());
Calendar calB = Calendar.getInstance();
calB.setTimeInMillis(dateB.getTime());
System.out.println("Compare day fields : " + (calA.get(Calendar.DAY_OF_MONTH) == calB.get(Calendar.DAY_OF_MONTH))); //prints true

If you need the time components for your comparison you can still use the equals method and I would love to hear your reason:-)

Comments