0

In Ruby 2.1.2, I can successfully compare the result of Time.parse and Time.utc for the same time, and it returns the expected true:

Time.parse("2015-02-09T22:38:43Z") == Time.utc(2015, 2, 9, 22, 38, 43)
=> true

However, this same comparison counterintuitively returns false when the second value is not an integer:

Time.parse("2015-02-09T22:38:43.1Z") == Time.utc(2015, 2, 9, 22, 38, 43.1)
=> false

This is despite the fact that the second values are still integers and are still equivalent:

Time.parse("2015-02-09T22:38:43.1Z").sec
=> 43
Time.utc(2015, 2, 9, 22, 38, 43.1).sec
=> 43
Time.parse("2015-02-09T22:38:43.1Z").sec == Time.utc(2015, 2, 9, 22, 38, 43.1).sec
=> true

Moreover, the comparison results in true between successive calls of the same methods:

Time.parse("2015-02-09T22:38:43.1Z") == Time.parse("2015-02-09T22:38:43.1Z")
=> true
Time.utc(2015, 2, 9, 22, 38, 43.1) == Time.utc(2015, 2, 9, 22, 38, 43.1)
=> true

Why is this so? Is this a bug, or am I missing something?

eirikir
  • 3,802
  • 3
  • 21
  • 39
  • 1
    Please rephrase your wording. It is misleading/confusing. Where you claim comparison fails, it isn't failing, it is succeeding and is returning `false`. – sawa Feb 11 '15 at 18:04
  • This appears to be a pure Ruby question, in which case you should remove all reference to Rails, especially the Rails tags. – Cary Swoveland Feb 11 '15 at 18:47

2 Answers2

5

Ruby compares fractional seconds as well as seconds when comparing times. For some reason your times receive different fractional seconds:

Time.parse("2015-02-09T22:38:43.1Z").subsec
# => (1/10)
Time.utc(2015, 2, 9, 22, 38, 43.1).subsec
# => (14073748835533/140737488355328)
Jesper
  • 4,535
  • 2
  • 22
  • 34
  • 2
    Sounds like the usual floating point shenanigans- 43.1 can't be represented exactly. – Frederick Cheung Feb 11 '15 at 18:27
  • I.e. Precision. When comparing 2 numbers that only matter if they are "close enough" (i.e. floating point and time values), you have to make sure they are within acceptable bounds. – Berin Loritsch Feb 12 '15 at 12:08
-1

I believe you are running into an issue with precision. The reason the integer seconds compare works is due to the precision of integers, and Ruby performed a .to_i on the parsed version for you.

Underlying the Time class is an integer that represents a very precise integer number, and parsing and explicit values might be treated just differently enough to cause problems. If sub-second precision is not important to you, then it would be best to compare seconds:

Time.parse("2015-02-09T22:38:43.1Z").to_i == Time.utc(2015, 2, 9, 22, 38, 43.1).to_i

In the above case you are comparing seconds since the Unix epoch (Jan 1, 1970)

Another option would be to create a function to compare two times within a certain precision. Many unit testing frameworks provide that feature. Essentially if T1 == T2 within 0.1 seconds, it's good enough.

The easiest way to do a "within" comparison would be like this:

def within(time1, time2, precision)
    return (time1 - time2).abs < precision
end

NOTE: the above works with time, floating points, and fractions.

Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
  • Anyone care to comment on why my answer was downvoted? It is correct. The `Time.parse` and `Time.utc` methods are logically similar, but due to precision and rounding issues have differing fractional values. However, if 0.1 second granularity is all you need, they would be equivalent if you tested for that. – Berin Loritsch Feb 12 '15 at 12:11