This week's book giveaway is in the Mac OS forum.
We're giving away four copies of a choice of "Take Control of Upgrading to Yosemite" or "Take Control of Automating Your Mac" and have Joe Kissell on-line!
See this thread for details.
The moose likes Beginning Java and the fly likes Comparing Date and Timestamp: unexpected result Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


JavaRanch » Java Forums » Java » Beginning Java
Bookmark "Comparing Date and Timestamp: unexpected result" Watch "Comparing Date and Timestamp: unexpected result" New topic
Author

Comparing Date and Timestamp: unexpected result

Kjeld Sigtermans
Ranch Hand

Joined: Aug 10, 2006
Posts: 122
Hi,

Given this code:Why does now.before(later) evaluate to false?

Cheers,
Kjeld


Kjeld Sigtermans - SCJP 1.4 - SCWCD 1.4
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4397
    
    8

This is taken from the Javadocs for Timestamp:
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 an object that isn't an instance of java.sql.Timestamp, because the nanos component of a date is unknown.

I think that explains it. I would expect that Date.before() compares the Date component of the Timestamp, so the extra millisecond is ignored.
Joanne Neal
Rancher

Joined: Aug 05, 2005
Posts: 3602
    
  15
Did you read the note in the javadoc for the Timestamp class ? This suggests to me you shouldn't rely on comparisons between a Timestamp and a Date if the difference is less than a second.


Joanne
Kjeld Sigtermans
Ranch Hand

Joined: Aug 10, 2006
Posts: 122
Thank you both for your fast replies.
As far as I understand, the method a.before(b) simply compares the difference between a.getTime() and b.getTime(). The method equals is never called on Timestamp.
When constructing a Timestamp using a milliseconds argument, there are no nanoseconds.
So strictly following Javadoc for new Timestamp(long time) and new Date(long time), which I used in the example, I would expect before(...) to return true in this case.
Joanne Neal
Rancher

Joined: Aug 05, 2005
Posts: 3602
    
  15
Kjeld Sigtermans wrote:When constructing a Timestamp using a milliseconds argument, there are no nanoseconds.

Yes there are. The milliseconds from the Date object are stored in the nanoseconds part of the Timestamp. The Date part of the Timestamp only contains complete seconds. So when comparing a Date with a Timestamp (whether for the equals method or the before method) only the complete seconds will be considered.

If you look at the source code for Timestamp, the first thing the single parameter constructor does is

i.e. it sets the milliseconds component to zero. So the only time your code would work as you expect is if the milliseconds component of now was 999, so adding 1 would click over to a new second.
Jesper de Jong
Java Cowboy
Saloon Keeper

Joined: Aug 16, 2005
Posts: 14193
    
  20

This is just one example of why the date and time API of the standard Java library is broken...

A much better library for working with dates and times is Joda Time.


Java Beginners FAQ - JavaRanch SCJP FAQ - The Java Tutorial - Java SE 8 API documentation
Kjeld Sigtermans
Ranch Hand

Joined: Aug 10, 2006
Posts: 122
Joanne,

As far as I know, the precision of Date is milliseconds, not seconds. When using new Date(), a Date is constructed based on the System millis, calling the overloaded new Date(long millis). When using getTime() that value is returned (however not always, the source does some checks I did not go in to).
The constructor of Timestamp(long millis) divides the given millis into a millis part and a nanos part. So the millis part always ends with 000 and the nanos part holds that lower significance multiplied by a million.
The precision of Timestamp is in nanoseconds, when calling its getTime() method, the number of nanosecs are divided by a million again and added to the millisecs part.
Therefore, when comparing Date.getTime and Timestamp.getTime both are millisecond representations, having a millisecond precision.

Regarding the other comment: I use JodaTime extensively, but I still want to know why my code example returns false.

Edit: it's even worse, sometimes it returns true...
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4397
    
    8

Kjeld Sigtermans wrote:The constructor of Timestamp(long millis) divides the given millis into a millis part and a nanos part. So the millis part always ends with 000 and the nanos part holds that lower significance multiplied by a million.

No. The constructor divides it into a seconds part and a nanos part. Yes, the Date part could cope with millisecond accuracy. But that's not how it's been implemented.
Kjeld Sigtermans
Ranch Hand

Joined: Aug 10, 2006
Posts: 122
Yes, the millis part of Timestamp has a precision in seconds (as I already pointed out indirectly by writing that the lower three digits are always 000), and the nanos part holds those lower three digits (the remainder) multiplied by a million.
But, when calling before or after, and indirectly its getTime() method, the nanos are divided by a million and added to the millis (seconds) part again. So the given constructor parameter is restored for comparison.

Meanwhile I noticed my code example sometimes returns true.
Joanne Neal
Rancher

Joined: Aug 05, 2005
Posts: 3602
    
  15
An example of what happens may help.

You create a Date object - lets say 09-03-2011 12:28:34.287

You then pass the number of milliseconds this represents (+1) (lets say that's XXX288 - only the miiliseconds part is important) to the Timestamp constructor

The constructor rounds that time down to the nearest second so it is now XXX000 and passes this to its super constructor. So the Date part of the Timestamp now represents the time 09-03-2011 12:28:34.000.

You now call which returns false, which is correct because 09-03-2011 12:28:34.287 is after 09-03-2011 12:28:34.000.

The one time your code will return true is if the milliseconds part of now is 999. Adding one will increment the number of seconds, so now you will be comparing 09-03-2011 12:28:34.999 to 09-03-2011 12:28:35.000.

Look at the source code for Timestamp and it should be clearer what is happening.
Kjeld Sigtermans
Ranch Hand

Joined: Aug 10, 2006
Posts: 122
I can verify all this (thanks for the example btw!) but the part in the code that confuses me is the method Date.getMillisOf(Date date).
It will only return fastTime (and therefore the rounded down value) when some variable cdate is null. They should have used getTime() to make the comparison. Then Timestamp.getTime() would have returned the correct millisecond value.

Meanwhile I went with Jesper's tip to use JodaTime and added this:
It does correctly return false.
 
GeeCON Prague 2014
 
subject: Comparing Date and Timestamp: unexpected result