You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Serge Knystautas <se...@lokitech.com> on 2004/02/14 00:12:37 UTC

DateUtils.equals(d1, d2)

There was a bugzilla issue opened about this, and a brief discussion 
about such, but I wanted to move it to the list for better visibility.

Basically if you have 2 date objects (e.g., java.util.Date and 
java.sql.Timestamp), ObjectUtils.equals() will return that they are not 
equal even if they represent the same point in time.

Unless someone objects, I was going to add DateUtils.equals(Date d1, 
Date d2) and the equality check will be what's returned from each date's 
getTime().  Just as an example:

long now = System.currentTimeMillis();
Date date = new java.util.Date(now);
Date ts = new java.sql.Timestamp(now);

ObjectUtils.equals(date, ts) = false; (as of 2.1)
DateUtils.equals(date, ts) = true;

Anyone have a different idea or think this is a bad idea?

-- 
Serge Knystautas
President
Lokitech >>> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. sergek@lokitech.com


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by Serge Knystautas <se...@lokitech.com>.
Serge Knystautas wrote:
> Todd V. Jonker wrote:
>> I like your idea of calling getTime() and rounding to the second.  That
>> should work in all cases.
> 
> Not exactly that... more like this:
> 
> long ms1 = d1.getTime();
> if (d1 instanceof Timestamp) {
>     //First we remove milliseconds
>     //  that some Timestamp implementations include
>     ms1 /= 1000;
>     ms1 *= 1000;
>     //Now add milliseconds based on nano seconds that all impls have.
>     ms1 += ((Timestamp) d1).getNanos() / 1000;
> }

Actually, why not also add DateUtils.getMilliseconds(Date d) so people 
stuck with bad drivers can effectively get reliable getTime() behavior?

-- 
Serge Knystautas
President
Lokitech >> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. sergek@lokitech.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by Serge Knystautas <se...@lokitech.com>.
Todd V. Jonker wrote:
> I like your idea of calling getTime() and rounding to the second.  That
> should work in all cases.

Not exactly that... more like this:

long ms1 = d1.getTime();
if (d1 instanceof Timestamp) {
     //First we remove milliseconds
     //  that some Timestamp implementations include
     ms1 /= 1000;
     ms1 *= 1000;
     //Now add milliseconds based on nano seconds that all impls have.
     ms1 += ((Timestamp) d1).getNanos() / 1000;
}

-- 
Serge Knystautas
President
Lokitech >> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. sergek@lokitech.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by "Todd V. Jonker" <to...@consciouscode.com>.
Just checking instanceof Timestamp and JVM version concerns me, because
it seems to assume that the driver is doing the right thing.  I also have
no idea how this really works for different drivers, thus my queasiness.

I like your idea of calling getTime() and rounding to the second.  That
should work in all cases.

However, if either d1 or d2 do have nanosecond precision, so you really
want to ignore that information?  As a programmer I'd want to belive that

  DateUtils.equals(timestamp1, timestamp2) ==
  timestamp1.equals(timestamp2)

for non-null values.  The code below can't promise that.

How about always comparing to nanosecond precision, and just assume that
util.Date and sql.Date have those bits zero?  Essentially convert Dates
to Timestamp before comparing.  That might actually make this thing
semantically sound.  :-)


On Tue, 17 Feb 2004 10:59:24 -0500, "Serge Knystautas"
<se...@lokitech.com> said:
> DateUtils.equals(Date d1, Date d2) {
>    if (d1 == null || d2 == null) {
>      return d1 == d2;
>    }
>    boolean screwyTimestamp = d1 instanceof Timestamp
>                           || d2 instanceof Timestamp;
>    if (SystemUtils.isJavaVersionAtLeast(1.4) or !screwyTimestamp) {
>      return d1.getTime() == d2.getTime();
>    } else {
>      long ms1 = d1.getTime();
>      if (d1 instanceof Timestamp) {
>        ms1 += ((Timestamp) d1).getNanos() / 1000;
>      }
>      if (d2 instanceof Timestamp) {
>        ms2 += ((Timestamp) d2).getNanos() / 1000;
>      }
>      return ms1 == ms2;
>    }
> }
>
> This may not work if you've got an Oracle driver that runs in JDK 1.3 
> and so follows that convention, but then you're running in JDK 1.4.  I 
> don't know how that is handled.
> 
> If that's the case, we can skip the JVM detection, and instead if it's a 
> timestamp round getTime() to the nearest second and then add back in 
> nanos/1000.  This way regardless of what JVM you're in or driver you're 
> using, you should always get the correct number of milliseconds.
> 
> -- 
> Serge Knystautas
> President
> Lokitech >> software . strategy . design >> http://www.lokitech.com
> p. 301.656.5501
> e. sergek@lokitech.com


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by Serge Knystautas <se...@lokitech.com>.
Todd V. Jonker wrote:
> Serge, good point about Timestamp.getTime(), I missed that.

<snip />

> Given the fact that [lang] needs to work on all JDKs, this will be quite
> tricky to implement correctly.  And again I assert that attempting
> comparisons of this type are a Really Bad Idea.

Let me know what you think of this... (this needs unit testing and a 
careful eye, but just to sketch some code)...

DateUtils.equals(Date d1, Date d2) {
   if (d1 == null || d2 == null) {
     return d1 == d2;
   }
   boolean screwyTimestamp = d1 instanceof Timestamp
                          || d2 instanceof Timestamp;
   if (SystemUtils.isJavaVersionAtLeast(1.4) or !screwyTimestamp) {
     return d1.getTime() == d2.getTime();
   } else {
     long ms1 = d1.getTime();
     if (d1 instanceof Timestamp) {
       ms1 += ((Timestamp) d1).getNanos() / 1000;
     }
     if (d2 instanceof Timestamp) {
       ms2 += ((Timestamp) d2).getNanos() / 1000;
     }
     return ms1 == ms2;
   }
}

This may not work if you've got an Oracle driver that runs in JDK 1.3 
and so follows that convention, but then you're running in JDK 1.4.  I 
don't know how that is handled.

If that's the case, we can skip the JVM detection, and instead if it's a 
timestamp round getTime() to the nearest second and then add back in 
nanos/1000.  This way regardless of what JVM you're in or driver you're 
using, you should always get the correct number of milliseconds.

-- 
Serge Knystautas
President
Lokitech >> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. sergek@lokitech.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by "Todd V. Jonker" <to...@consciouscode.com>.
Serge, good point about Timestamp.getTime(), I missed that.

However, the spec changed twice between JDKs 1.3.1 and 1.4.2.

--------
Timestamp 1.3.1 doesn't override util.Date.getTime() and states:

"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."

"Also, the hashcode method uses the underlying java.util.Data
implementation and therefore does not include nanos in its computation."
--------
Timestamp 1.4.1 provides the same first blurb above, but this is
contradicted by the description of the now-overridden getTime(). I
suspect that the class description is incorrect.
--------
Timestamp 1.4.2 is consistent between both sections, as you point out.
--------

Given the fact that [lang] needs to work on all JDKs, this will be quite
tricky to implement correctly.  And again I assert that attempting
comparisons of this type are a Really Bad Idea.

Hibernate can also map Timestamp instead of Date.  But if you only use
date on the app-side of Hibernate, Date comparisons have always worked
for me in my unit testing.  If you go straight to the database and nab a
Timestamp, then you have conversion issues of course, so I'm guessing
that's what you're doing.

Perhaps a less controversial solution would be a set of conversion
functions:

    Date toDate(Timestamp timestamp);
    Timestamp toTimestamp(Date date);

The conversion would still be tricky to get correct on all JVMs, but at
least it would avoid a comparison API with questionable semantics.


(And yes, the Oracle driver we're stuck with in production is a buggy
mess regarding timestamps and dates, *sigh*.)



Timestamp
On Tue, 17 Feb 2004 00:45:05 -0500, "Serge Knystautas"
<se...@lokitech.com> said:
> Todd V. Jonker wrote:
> > Serge, I'm not sure that your proposed method will do what you want.
> > 
> > You can't compare the results of java.util.Date.getTime() and
> > java.sql.Timestamp.getTime() because the latter is only precise to the
> > second, not the millisecond.  Likewise, java.sql.Date.getTime() is only
> > precise to the second.
> 
> I understand java.sql.Timestamp is a bit screwy, but if you read the 
> javadoc for getTime() method, it says, "Returns the number of 
> milliseconds since January 1, 1970, 00:00:00 GMT represented by this 
> Timestamp object."  So it should return milliseconds even if it is 
> structurally holding something different underneath.
> 
> Regardless, it would help my situation... I'm using a db mapping layer 
> (hibernate) and it sets a java.util.Date object from the ResultSet.  My 
> app code then prints to user, parses the data back, and I set the Date. 
>   I want to check whether the value is changed, and I'm actually only 
> expecting resolution to date usually, sometimes to minute.
> 
> > Just my two cents from painful experience...
> 
> You may want to revisit the JDBC drivers that put you through this.  The 
> ones I use now all do getTime() as milliseconds, and some of them used 
> to do just seconds as the class-level javadoc implies.  Maybe this was 
> clarified in one of the more recent JVMs, dunno.
> 
> -- 
> Serge Knystautas
> President
> Lokitech >> software . strategy . design >> http://www.lokitech.com
> p. 301.656.5501
> e. sergek@lokitech.com


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by Serge Knystautas <se...@lokitech.com>.
Todd V. Jonker wrote:
> Serge, I'm not sure that your proposed method will do what you want.
> 
> You can't compare the results of java.util.Date.getTime() and
> java.sql.Timestamp.getTime() because the latter is only precise to the
> second, not the millisecond.  Likewise, java.sql.Date.getTime() is only
> precise to the second.

I understand java.sql.Timestamp is a bit screwy, but if you read the 
javadoc for getTime() method, it says, "Returns the number of 
milliseconds since January 1, 1970, 00:00:00 GMT represented by this 
Timestamp object."  So it should return milliseconds even if it is 
structurally holding something different underneath.

Regardless, it would help my situation... I'm using a db mapping layer 
(hibernate) and it sets a java.util.Date object from the ResultSet.  My 
app code then prints to user, parses the data back, and I set the Date. 
  I want to check whether the value is changed, and I'm actually only 
expecting resolution to date usually, sometimes to minute.

> Just my two cents from painful experience...

You may want to revisit the JDBC drivers that put you through this.  The 
ones I use now all do getTime() as milliseconds, and some of them used 
to do just seconds as the class-level javadoc implies.  Maybe this was 
clarified in one of the more recent JVMs, dunno.

-- 
Serge Knystautas
President
Lokitech >> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. sergek@lokitech.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: DateUtils.equals(d1, d2)

Posted by "Todd V. Jonker" <to...@consciouscode.com>.
Serge, I'm not sure that your proposed method will do what you want.

You can't compare the results of java.util.Date.getTime() and
java.sql.Timestamp.getTime() because the latter is only precise to the
second, not the millisecond.  Likewise, java.sql.Date.getTime() is only
precise to the second.

Unless you do rather snarly logic, it may not be meaningful to compare
any util.Date with any of the sql types.  Quoting from the Timestamp API:

"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."

To compare a until.Date with a sql.Timestamp you'll have to rebuild the
latter's time to millisecond precision by call getNanos(), rounding the
result to the millisecond, and adding it to getTime().  And if you want
"true" equality you'll have to fail if the submillisecond nanos aren't
zero.  Doing all of that is of debatable usefulness, because the use of
such a somewhat-generic equality method probably indicates that the
programmer doesn't understand the subtleties here and he's probably
writing a bug.  :-)

Just my two cents from painful experience...

.T.



On Fri, 13 Feb 2004 18:12:37 -0500, "Serge Knystautas"
<se...@lokitech.com> said:
> There was a bugzilla issue opened about this, and a brief discussion 
> about such, but I wanted to move it to the list for better visibility.
> 
> Basically if you have 2 date objects (e.g., java.util.Date and 
> java.sql.Timestamp), ObjectUtils.equals() will return that they are not 
> equal even if they represent the same point in time.
> 
> Unless someone objects, I was going to add DateUtils.equals(Date d1, 
> Date d2) and the equality check will be what's returned from each date's 
> getTime().  Just as an example:
> 
> long now = System.currentTimeMillis();
> Date date = new java.util.Date(now);
> Date ts = new java.sql.Timestamp(now);
> 
> ObjectUtils.equals(date, ts) = false; (as of 2.1)
> DateUtils.equals(date, ts) = true;
> 
> Anyone have a different idea or think this is a bad idea?
> 
> -- 
> Serge Knystautas
> President
> Lokitech >>> software . strategy . design >> http://www.lokitech.com
> p. 301.656.5501
> e. sergek@lokitech.com

-- 
Todd V. Jonker

Conscious Code Ltd
The Practice of Programming
www.consciouscode.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [lang] DateUtils.equals(d1, d2)

Posted by Stephen Colebourne <sc...@btopenworld.com>.
I'm +1 to adding this to DateUtils. Assuming there is a patch and unit test
of course ;-)
Stephen

----- Original Message -----
From: "Serge Knystautas" <se...@lokitech.com>
> There was a bugzilla issue opened about this, and a brief discussion
> about such, but I wanted to move it to the list for better visibility.
>
> Basically if you have 2 date objects (e.g., java.util.Date and
> java.sql.Timestamp), ObjectUtils.equals() will return that they are not
> equal even if they represent the same point in time.
>
> Unless someone objects, I was going to add DateUtils.equals(Date d1,
> Date d2) and the equality check will be what's returned from each date's
> getTime().  Just as an example:
>
> long now = System.currentTimeMillis();
> Date date = new java.util.Date(now);
> Date ts = new java.sql.Timestamp(now);
>
> ObjectUtils.equals(date, ts) = false; (as of 2.1)
> DateUtils.equals(date, ts) = true;
>
> Anyone have a different idea or think this is a bad idea?



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org