Aaron K
Aaron K

Reputation: 6961

Time.now does not match the same Time as generated from Time.parse or Time.at

While testing some Ruby code, I ran into some strange behavior. Can someone please explain why Time.now doesn't match the same time created from either Time.at or parsed from Time.parse?

[1] pry(main)> require 'time'
=> true
[2] pry(main)> t = Time.now
=> 2013-04-04 19:46:49 -0400
[3] pry(main)> i = Time.at t.to_i
=> 2013-04-04 19:46:49 -0400
[4] pry(main)> t == i
=> false
[5] pry(main)> t.to_i == i.to_i
=> true
[6] pry(main)> p = Time.parse t.to_s
=> 2013-04-04 19:46:49 -0400
[7] pry(main)> t == p
=> false
[8] pry(main)> t.to_i == p.to_i
=> true
[8] pry(main)> t.class
=> Time
[9] pry(main)> i.class
=> Time
[10] pry(main)> p.class
=> Time
[11] pry(main)> t.inspect
=> "2013-04-04 19:46:49 -0400"
[12] pry(main)> i.inspect
=> "2013-04-04 19:46:49 -0400"
[13] pry(main)> p.inspect
=> "2013-04-04 19:46:49 -0400"

Update 1

It seems even trying to get sub-second precision shows the same behavior:

[1] pry(main)> t = Time.now
=> 2013-04-04 20:04:47 -0400
[2] pry(main)> f = Time.at t.to_f
=> 2013-04-04 20:04:47 -0400
[3] pry(main)> t == f
=> false
[4] pry(main)> t.to_f
=> 1365120287.902954
[5] pry(main)> f.to_f
=> 1365120287.902954

Upvotes: 3

Views: 129

Answers (3)

the Tin Man
the Tin Man

Reputation: 160551

I'll take a shot at 'splaining what you're seeing. Here are several comparisons of a Time value, and what happens when converting between different formats. I'm not going to do any equality checks, because you'll be able to see whether a value should match simply by looking at it:

require 'time'                                         
t_now = Time.now # => 2013-04-04 20:10:17 -0700

That's the inspect output, which throws away a lot of information and precision. It's good enough for use by mortals.

t_now.to_f # => 1365131417.613106

That's the value the computer sees usually, with microseconds.

t_now.to_i # => 1365131417

That's the same time with microseconds all gone.

Time.at(t_now)      # => 2013-04-04 20:10:17 -0700
Time.at(t_now.to_f) # => 2013-04-04 20:10:17 -0700
Time.at(t_now.to_i) # => 2013-04-04 20:10:17 -0700
t_now.to_s          # => "2013-04-04 20:10:17 -0700"

Normal inspect and to_s output won't show any difference in the precision as long as the integer portion of the value is intact.

Time.parse(t_now.to_s)      # => 2013-04-04 20:10:17 -0700
Time.parse(t_now.to_s).to_f # => 1365131417.0

Parsing loses the resolution, unless you present a value that contains the fractional time and define the parsing format so strptime knows what to do with it. The default parser formats are designed for general-use, not high-precision, so we have to use strftime instead of allowing to_s to have its way with the value, and strptime to know what all those numbers mean:

T_FORMAT = '%Y/%m/%d-%H:%M:%S.%N' # => "%Y/%m/%d-%H:%M:%S.%N"
t_now.strftime(T_FORMAT)          # => "2013/04/04-20:10:17.613106000"
Time.strptime(t_now.strftime(T_FORMAT), T_FORMAT).to_f # => 1365131417.613106

Upvotes: 3

kejadlen
kejadlen

Reputation: 1599

The fractional seconds are different:

>> t = Time.now
=> 2013-04-04 17:34:34 -0700
>> f = Time.at(t.to_f)
=> 2013-04-04 17:34:34 -0700
>> t.usec
=> 571153
>> f.usec
=> 571152
>> t.nsec
=> 571153000
>> f.nsec
=> 571152925

Upvotes: 2

tadman
tadman

Reputation: 211560

It's probable that the two times are actually different but you're not seeing the differences with the representations you've chosen.

Try:

t.to_f == i.to_f

I think you'll see that t has more precision than i does.

Internally Time is represented as a floating point value, with sub-second accuracy. Typically you can depend on it being accurate to within one millionth of a second, and sometimes even more depending on your OS.

Upvotes: 1

Related Questions