Date comparisons

I occasionally get this error:

‘A puzzle once featured, should no longer be nominated’ FAILED
expected: Sun May 04 09:10:26 -0700 2008,
got: Sun May 04 09:10:26 -0700 2008 (using ==)
./spec/models/puzzle_spec.rb:180:

So, the dates looks the same to me. Any ideas for how to debug?

Joe

I’ve seen that one too. Maybe has to do with how equality is defined in
the
Time or DateTime class.

I get around it by comparing the string-ified versions:

foo.time.to_s.should == expected_time.to_s

On Sat, May 3, 2008 at 12:17 PM, Joe Van D. [email protected] wrote:

So, the dates looks the same to me. Any ideas for how to debug?
Just because too objects have the same to_s representation don’t mean
they are equal:

Are these, perhaps times rather than dates?

k$ irb
irb(main):001:0> Time.now == Time.now
=> false
irb(main):002:0> a, b = Time.now, Time.now
=> [Sat May 03 12:23:12 -0400 2008, Sat May 03 12:23:12 -0400 2008]
irb(main):003:0> a == b
=> false

This is a similar issue to Floats where there’s more precision than
the exernal representation shows.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Just because too objects have the same to_s representation don’t mean
they are equal:

The important equality in this case is what matters to the tester.

This is a similar issue to Floats where there’s more precision than
the exernal representation shows.

Is there more precision than seconds in a Time instance?

irb(main):006:0> a,b = Time.now, Time.now
=> [Sat May 03 11:06:31 -0700 2008, Sat May 03 11:06:31 -0700 2008]
irb(main):007:0> puts a.to_i, b.to_i
1209837991
1209837991
=> nil

On Sat, May 3, 2008 at 1:12 PM, Steve D. [email protected]
wrote:

Is there more precision than seconds in a Time instance?

irb(main):006:0> a,b = Time.now, Time.now
=> [Sat May 03 11:06:31 -0700 2008, Sat May 03 11:06:31 -0700 2008]
irb(main):007:0> puts a.to_i, b.to_i
1209837991
1209837991
=> nil

This came up this morning in rspec; Time tracks microseconds which
blows up equality:

a, b = Time.now, Time.now
=> [Sat May 03 13:15:31 -0500 2008, Sat May 03 13:15:31 -0500 2008]
a == b
=> false
[a.usec, b.usec]
=> [93308, 93309]

Because of this, I tend to use ‘should be_between()’ when handling
times; someone else suggested the .to_i trick which is probably closer
to what is intended, but for some reason I find it confusing.

k

On May 3, 2008, at 2:16 PM, Kyle H. wrote:

Because of this, I tend to use ‘should be_between()’ when handling
times; someone else suggested the .to_i trick which is probably closer
to what is intended, but for some reason I find it confusing.

If I’m in a rails project, I like to use to_s(:db) to compare the
times - that’s how they are stored in the database. Any greater
precision (at least in MySQL) gets lost when the datetime is read out
of the database.

Scott

Hi–

On May 3, 2008, at 9:17 AM, Joe Van D. wrote:

So, the dates looks the same to me. Any ideas for how to debug?

Joe

I monkeypatched DateTime (aieeeeee!) to get this effect. A custom
matcher is probably a better solution. Here’s the monkeypatch:

require File.dirname(FILE) + ‘/…/spec_helper’

class DateTime
def close?(other_date, difference)
(other_date.to_time.to_i - DateTime.now.to_time.to_i).abs <
difference
end
end

#Delete this context and add some real ones
context “should be able to create a project viewer” do
fixtures :project_viewers

specify “fixtures should load two ProjectViewers” do
pb = ProjectViewer.create(:comments => ‘a
comment’, :last_viewed_at => DateTime.now)
pb.last_viewed_at.should_be_close(DateTime.now, 100)
end
end

If your code uses Date#now, always make sure you stub it in your
specs. Always.

Yes, that was my first idea as well. The Time class is a little
fucked up in that

< a, b = Time.now, Time.now
< a == b #=> false

So if you’re using Time anywhere, you really ought to be stubbing it.

“always”
:slight_smile:

Pat

On 5.5.2008, at 11.02, Pat M. wrote:

Yes, that was my first idea as well. The Time class is a little
fucked up in that

< a, b = Time.now, Time.now
< a == b #=> false

That’s definitely a gotcha but I wouldn’t necessarily say it’s fucked
up. It’s just that Time#now returns the current (exact) point of time,
so running it successively on the same machine will by definition
return different values (by a tiny margin but still).

The fact that the textual representation of the two look exactly the
same certainly makes it confusing the first time get bitten by it,
though :slight_smile:

Cheers,
//jarkko


Jarkko L.

http://www.railsecommerce.com
http://odesign.fi

The (pretty much universal) problem with dates and times is that people
use
“date” and “time” to mean different things. There’s a java library
called
joda that provides a really clean vocabulary around this.

An instant is a point in time. You shouldn’t be able to ask for two
instants and get the same answer however close together you ask.
(There’s
probably something philosophical in there somewhere.)

A datetime is a type of instant with millisecond precision that can
tell
you the time and date it represents, based on a timezone. The Ruby Time
class represents an instant but renders itself as a datetime, hence the
confusion (it has microsecond precision that you only get to see if you
know
to ask).

A local date is a day when something happens, say 4th May 2008. My
understanding of 4th May in the UK is bounded by different start and end
instants than, say, David’s in the US, because of timezones, but we both
know what we “mean” by 4th May 2008.

A duration is a length of time in milliseconds.

Given these atoms you can have fairly sensible conversations about times
and
dates. For instance, in the current example I might do this:

Instant = Time # make it clear what I’m using it for

Instant.new == Instant.new # false, but now I know why

Cheers,
Dan

ps. and as Aslak says, make sure you control “time” in your examples -
either by stubbing Time or by injecting your own Clock abstraction.

2008/5/5 Jarkko L. [email protected]:

On May 4, 2008, at 11:07 PM, Aslak Hellesøy wrote:

If your code uses Date#now, always make sure you stub it in your
specs. Always.

Yes, but the OP’s question was why do two “same” date objects compare
as different. This is a typical problem with floating-point and
anything that counts time. I used DateTime.now in my sample code
below, but certainly I agree that specifying an exact object either by
stubbing or explicit constant specification is a better idea. There
will be cases, however, where a strict equality test can’t be trusted
and one must compare something as being within some “close” tolerance.

-s