Compare Date object with a TimeStamp in Java
Asked Answered
W

10

32

When I test this code:

java.util.Date date = new java.util.Date();
java.util.Date stamp = new java.sql.Timestamp(date.getTime());

assertTrue(date.equals(stamp));
assertTrue(date.compareTo(stamp) == 0);
assertTrue(stamp.compareTo(date) == 0);
assertTrue(stamp.equals(date));

I´ll be expecting a true, true, true, false. Because of this:

In the javadoc for java.sql.Timestamp, it states:

Note: This type is a composite of a java.util.Date and a separate nanoseconds value. Only integral seconds are stored in the java.util.Date component. The fractional seconds - the nanos - are separate. The Timestamp.equals(Object) method never returns true when passed a value of type java.util.Date because the nanos component of a date is unknown. As a result, the Timestamp.equals(Object) method is not symmetric with respect to the java.util.Date.equals(Object) method. Also, the hashcode method uses the underlying java.util.Date implementation and therefore does not include nanos in its computation.

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.

But instead I´ll get a true, false, true, false. Any ideas?

EDIT: This problem appear when a I was checking two Dates with the equals method, but one of the Date object come from a Hibernate class and debugging I see that the object contains a TimeStamp. So the equals method evaluate to false, then I found this: http://mattfleming.com/node/141

But when I try the code I get different results...if I can´t use neither equals and compareTo, what I should use to check if 2 Dates are the same?!?!

Wade answered 19/1, 2012 at 16:3 Comment(3)
Basically: if you want to compare two homogeneous objects (either both Date or Timestamp), there is no problem; do not compare two heterogeneous objects. We can try to address the problem at the source, meaning: why do you have to compare them?Bartolome
I´m comparing two Date objects, but the implicit object in one of them is a TimeStamp with is a Date type. If inheritance works in other object, why not here?Wade
Because the implementation of Timestamp is silly; you should treat those two classes as not being related, as was the intention of whoever wrote it.Bartolome
A
11

tl;dr

Use the modern java.time classes instead of those troublesome legacy date-time classes.

myPreparedStatement.setObject(
    … , 
    Instant.now()                // Capture the current moment in UTC.
)

Old Date-Time Classes Poorly Designed

Put more bluntly, the java.sql.Timestamp/.Date/.Time classes are a hack, a bad hack. Like java.util.Date/.Calendar, they are the result of poor design choices.

The java.sql types should be used as briefly as possible, used only for transfer of data in/out of the database. Do not use for business logic and further work.

java.time

The old date-time classes have been replaced by the java.time framework built into Java 8 and later. These new classes are defined by JSR 310, inspired by the highly successful Joda-Time library, and extended by the ThreeTen-Extra project.

Eventually we should see JDBC drivers updated to work directly with these java.time types. But until that day we need to convert to/from java.sql types. For such conversions, call new methods added to the old classes.

An Instant is a moment on the timeline in UTC to a resolution of nanoseconds.

Instant instant = myJavaSqlTimestamp.toInstant();

To go the other direction:

java.sql.Timestamp ts = java.sql.Timestamp.valueOf( instant );

Apply a time zone to get wall-clock time.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

The java.time classes have a clean wisely-chosen class design. So you can use the equals and compareTo as expected. Note that the classes with an offset-from-UTC or time zone also offer isEqual, isBefore, and isAfter methods. These methods compare by considering moments on the timeline, their chronological order. The equals and compareTo methods also consider the offset or time zone.

Minimizing the use of java.sql while maximizing the use of java.time renders the Question’s issues moot.

In Hibernate, use converters for java.time.

JDBC 4.2

As of JDBC 4.2 and later, you need not use the legacy classes at all. You can directly exchange java.time objects with your database via the getObject & setObject methods.

myPreparedStatement.setObject( … , instant ) ;

And retrieval.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

Note that many databases cannot store a moment with a resolution as fine as the nanoseconds used in java.time. You may want to truncate explicitly rather than letting your JDBC driver do so implicitly.

Instant instant = Instant.now().truncatedTo( ChronoUnit.MILLIS ) ; // Lop off any nanoseconds & microseconds, keeping only the milliseconds, to match limitations of database. 

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Amortize answered 29/2, 2016 at 16:16 Comment(0)
B
8

Nican explained the equals part, about compareTo:

  • Timestamp has a compareTo(Date) method that converts it to Timestamp internally
  • Date does the comparison by downcasting (since Timestamp is a subclass of it); but as the javadoc states: "The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance"

Which of course is an horrible idea, in my opinion.

Bartolome answered 19/1, 2012 at 16:39 Comment(0)
R
5

I had the same problem in a test where I wanted to compare java.util.Date and java.sql.Timestamp objects.

I converted them to LocalDate and that worked:

import org.apache.commons.lang.ObjectUtils;

// date1 and date2 can be java.util.Date or java.sql.Timestamp
boolean datesAreEqual = ObjectUtils.equals(toLocalDate(date1), toLocalDate(date2));

where toLocalDate is:

import org.joda.time.LocalDate;
import java.util.Date;

public static void LocalDate toLocalDate(Date date)
{
    return date != null ? LocalDate.fromDateFields(date) : null;
}
Reprove answered 1/5, 2013 at 13:27 Comment(0)
C
4
  1. date.equals(stamp) return true AND stamp equals(date) returns false. REASON :Date neglects the nanosecond part of timestamp and since the other parts happen to be equal so the result is equal. The fractional seconds - the nanos - are separate.The Timestamp.equals(Object) method never returns true when passed a value of type java.util.Date because the nanos component of a date is unknown. See here for more details

  2. date.compareTo(stamp) == 0 returns false AND stamp.compareTo(date) == 0 returns true. REASON: According to this bug compareTo function will behave as it does.

Connett answered 19/1, 2012 at 16:39 Comment(0)
O
3

Try recreate the object date using "Strings" like this

Date date = new Date();
Date stamp = Timestamp(date.getTime());

SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String date1String = ft.format(date);
String date2String = ft.format(stamp);

Date date1 = ft.parse(date1String);
Date date2 = ft.parse(date2String);


assertTrue(date1.equals(date2)); // true
assertTrue(date1.compareTo(date2) == 0); //true
assertTrue(date2.compareTo(date1) == 0); //true
assertTrue(date2.equals(date1)); // true
Ohmmeter answered 7/1, 2015 at 18:40 Comment(0)
D
2

Timestamp's nanos value is NOT the number of nanoseconds - it's a nanosecond-resolution number of millis (i.e. fractional seconds). As such, in the Timestamp constructor, it is setting the time on the super to be without milliseconds. Therefore, the Timestamp will always have a lower value for the member fastTime (used in Date's compareTo()) than the corresponding Date (unless, of course, it has no fractional seconds).

Check the source for Timestamp at line 110.

Drachma answered 20/1, 2012 at 18:49 Comment(0)
T
1

I've resolved converting both Date and TimeStamp into a Calendar object and then i've compared the single Calendar's properties:

Calendar date = Calendar.getInstance();
date.setTimeInMillis(dateObject.getTime());
Calendar timestamp = Calendar.getInstance();
timestamp.setTimeInMillis(timestampObject.getTime());
if (effettoAppendice.get(Calendar.DAY_OF_MONTH) == timestamp.get(Calendar.DAY_OF_MONTH) &&
    effettoAppendice.get(Calendar.MONTH) == timestamp.get(Calendar.MONTH) &&
    effettoAppendice.get(Calendar.YEAR) == timestamp.get(Calendar.YEAR)) {
    System.out.println("Date and Timestamp are the same");
} else {
    System.out.println("Date and Timestamp are NOT the same");
}

Hope this helps.

Tema answered 29/2, 2016 at 15:13 Comment(0)
I
0

Take a look at the source code compare method for Timestamp:

public boolean equals(java.lang.Object ts) {
  if (ts instanceof Timestamp) {
    return this.equals((Timestamp)ts);
  } else {
    return false;
  }
}

http://www.docjar.com/html/api/java/sql/Timestamp.java.html

It will only ever return true if the comparing object is a timestamp. Also, here is the Date source code: http://www.docjar.com/html/api/java/util/Date.java.html , and since Timestamp inherits Date, it can compare it.

Iconoscope answered 19/1, 2012 at 16:18 Comment(1)
The false assertion he's concerned with is compareTo(), not equals().Drachma
S
0

Sadly, the Timestamp class do overloads the equals(Object) method with equals(Timestamp) so comparisons on Timestamps are hard to do.

In the equals(Object) javadocs says:

Tests to see if this Timestamp object is equal to the given object. This version of the method equals has been added to fix the incorrect signature of Timestamp.equals(Timestamp) and to preserve backward compatibility with existing class files. Note: This method is not symmetric with respect to the equals(Object) method in the base class.

My rule of thumb is never to compare timestamps for equality (which is pretty much useless anyway), but if you do have to check for equality, compare them using the result of getTime() (the number of milliseconds from january 1st 1970, 00:00).

Sholom answered 19/1, 2012 at 16:28 Comment(7)
Why "sadly"? Classes are supposed to provide a meaningful equals() implementation, not use the default one that is ignorant of actual data value.Bartolome
@Viruzzo: By doing this, it breaks the contract of equals: a date is equals to a timestamp, but a timestamp is not equal to a date. They shouldn't have had an inheritance relationship between those classes. And they should have been immutable.Clothespin
@JBNizet I agree on both should's; still, there is nothing wrong (it's in fact better) in providing a more specific override of equals. String too has compareTo(String), as does Integer and so on.Bartolome
Sadly because until Java 1.1 the API only implemented only the equals(Timestamp) method, and from 1.2 they added the equals(Object). Usually is not a good idea to overload the equals() method, specially on the precense of inheritance (where this imply that equals will be NOT symmetric). So, it is just the class was implemented and evolved over time to retain its original functionality and not breaking current working code.Sholom
Yes, but String, Integer, etc. do so without violating the equals contract. And they override Object's equals implementation. Timestamp overrides Date's equals implementation.Clothespin
@Viruzzo: there is nothing wrong about overriding equals() however overloading do have the problems described earlier. The Comparable<T> interface is even more strict about the mathematical constraints on their elements so the standard sorted collections work properly (like representing element in a definite order and being compatible with equals(), but equals() is not even symmetric).Sholom
I agree that Timestamp.equals(Date) is a bad idea (at least as it is implemented), but the answer refers to Timestamp.equals(Timestamp).Bartolome
W
0

A Small note on Implementaion inheritance and Type inheritance..

"An object's class defines how the object is implemented. In contrast, an object's type only refers to its interface. Class inheritance(Implementation inheritance) defines an object's implementation in terms of another object's implementation. Type inheritance describes when an object can be used in place of another."

Timestamp and Date classes have implementation inheritance as JAVADOC said.

Wiegand answered 3/12, 2014 at 7:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.