timecop icon indicating copy to clipboard operation
timecop copied to clipboard

Nanoseconds drift when frozen in Rails 3.2.13 using 1.9.3-p448

Open cupakromer opened this issue 12 years ago • 21 comments
trafficstars

This is very similar to issue #56. However it seems to be broken with the most recent Ruby 1.9.3 patch level 1.9.3 (1.9.3-p448), Timecop (0.6.1), and Rails 3.2.13.

# Run this in rails console
require 'timecop'

t1 = Time.now
Timecop.freeze t1
t2 = Time.now

t1 == t2
# => false

t1.nsec == t2.nsec
# => false

t1.nsec
# => 811884000

t2.nsec
# => 811883926

Time.now == t2
# => true

Time.now.nsec
# => 811883926

This issue does not happen in irb or pry, only the Rails environment.

cupakromer avatar Jul 08 '13 18:07 cupakromer

FWIW I also tested with Ruby 1.9.3-p392 and it did not have this problem.

cupakromer avatar Jul 08 '13 18:07 cupakromer

It looks like the current master branch fixes this issue. It was related to #70.

When is the next patch release planned?

cupakromer avatar Jul 08 '13 19:07 cupakromer

Not sure, yet. I'd like to fix this https://github.com/travisjeffery/timecop/commit/5456bdfc6d7c6faa071a440c61991a9593ecf38f.

Maybe some issues with jRuby support as well.

travisjeffery avatar Jul 08 '13 23:07 travisjeffery

Just bit by this in ci, ruby 2.0.0-p247, rails 3.2.12. Going to try installing from master.

ghost avatar Jul 26 '13 18:07 ghost

Didn't help:

context 'user has not been soft deleted' do
  it 'soft-deletes the user' do
    Timecop.freeze do
      visit rails_admin.delete_path :user, user
      click_button "Yes, I'm sure"
      user.reload.deleted_at.should == Time.zone.now
    end
  end
end
Using /home/travis/.rvm/gems/ruby-2.0.0-p247
Installing rails (3.2.12)
Using timecop (0.6.2.2) from git://github.com/travisjeffery/timecop.git (at master)
1) Managing Users destroying user has not been soft deleted soft-deletes the user
   Failure/Error: user.reload.deleted_at.should == Time.zone.now
     expected: Fri, 26 Jul 2013 11:56:45 MST -07:00
          got: Fri, 26 Jul 2013 11:56:45 MST -07:00 (using ==)
     Diff:
   # ./spec/features/admin/users_spec.rb:193:in `block (5 levels) in <top (required)>'
   # ./spec/features/admin/users_spec.rb:189:in `block (4 levels) in <top (required)>'

ghost avatar Jul 26 '13 19:07 ghost

I just got bit by something similar and solved it by declaring a precision at which I need that equivalence. mock_redis/333733e84.

yaauie avatar Jul 26 '13 19:07 yaauie

@BM5k do you know the differences in the rational seconds? As a work around, you can use the RSpec be_within matcher: user.reload.deleted_at.should be_within(1.second).of(Time.zone.now)

Taking a peak at master, it seems like it may work (here). Are you able to step through and see which branch of that if statement gets run? Also, what if you don't use the block form of freeze?

cupakromer avatar Jul 26 '13 19:07 cupakromer

@BM5k one other thing to be aware of, is that some databases will strip the fractional seconds. This could just be a DB issue. So when the value is read back, nsec will be 0.

cupakromer avatar Jul 26 '13 20:07 cupakromer

@cupakromer Thanks for the info, I will definitely be playing with this more today. I will update with my results.

ghost avatar Jul 26 '13 20:07 ghost

@cupakromer

  • I get to line 54 of that if statement every time for this example
  • postgres does not appear to be stripping nsec values

The test runs as-is on my machine, but fails on the CI server (travis pro). The within(1.second).of trick works locally & on CI. Thanks again.

ghost avatar Jul 26 '13 21:07 ghost

@BM5k would be interesting to add some puts for Time.zone.now.nsec and user.reload.deleted_at.nsec before the should line to see what CI says.

cupakromer avatar Jul 26 '13 21:07 cupakromer

This was locally, it will take me a little while to get results from CI server.

Managing Users
  destroying
    user has not been soft deleted

From: /Users/bm5k/projects/devfu/iss/spec/features/admin/users_spec.rb @ line 192 :

    187: 
    188:       it 'soft-deletes the user', :focus do
    189:         Timecop.freeze do
    190:           visit rails_admin.delete_path :user, user
    191:           click_button "Yes, I'm sure"
 => 192:           binding.pry
    193:           user.reload.deleted_at.should == Time.zone.now
    194:         end
    195:       end
    196: 
    197:     end

[1] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)> Time.zone.now.nsec
=> 760641000
[2] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)> user.reload.deleted_at.nsec
=> 760641000
[3] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_4::Nested_1>)> 

ghost avatar Jul 27 '13 01:07 ghost

In the binding, if you run the expression on line 193 what happens? You may need to start taking apart the different fields. :grimacing: Also, could you paste how the user object is being created?

I setup a new Rails 4.0 project with Ruby 2.0.0, RSpec 2.14, and Timecop 0.6.2.2 and the following test passes:

describe 'Testing some timecop issues' do
  it do
    Timecop.freeze do
      user = User.create name: 'Fred', age: 20
      user.reload.created_at.should == Time.zone.now
    end
  end
end

If you want to jump on the RSpec irc channel sometime, I'd be happy to help live debug.

cupakromer avatar Jul 27 '13 16:07 cupakromer

Hi The same Error arise on ruby 2.0.0p353 and timecop-0.7.0. This code didn't work:

time = Time.now
Timecop.freeze(time) do

with:

Timecop.freeze(Time.local(2013,12,2, 0, 0, 0)) do
time = Time.now

Got it fixed.

I can't understand why it works. Freezing Time on a given variabel time or on Time.local, why does it work?

nallam avatar Dec 03 '13 10:12 nallam

Sure, it is not clear what I mean. I used to test a controller methnod:

time = Time.now
Timecop.freeze(time) do
  stub_relation.should_receive(:update_all).with(data_export_at: time)
  get :data_export, format: :csv
end

This worked fine with ruby 1.9.3-p327. But raised an error with ruby 1.9.3-p484 and 2.0.0-p353 because of the disparity of the expected and the get time. A debug showes that the ineqaulity based on nanoseconds.

With:

Timecop.freeze(Time.local(2013,12,2, 0, 0, 0)) do
    time = Time.now
    stub_relation.should_receive(:update_all).with(data_export_at: time)
    get :data_export, format: :csv
end

It works!!

I hope I described it clearly. thx

nallam avatar Dec 04 '13 08:12 nallam

After upgrading my Rails 2.3 application from Ruby 1.8.7 to 1.9.3 I faced the same problem.

Nowaker avatar Feb 17 '14 21:02 Nowaker

I've been having issues with nanoseconds - its worth noting that if you're seeing specs pass locally but fail on your CI server due to a nanosecond issue, and you're running OSX locally, that OSX doesn't support nanoseconds so Time will always have microsecond precision, just like the value coming out of your DB, so you won't see any issues. If you'r CI server is on Linux it will have nanosecond precision.

The trick is to freeze time to something with microsecond precision rather than using an arbitrary time like Time.now. It would be nice if you could configure Timecop to truncate nanoseconds automatically (unless that level of precision is important to you). You can do this in Rails using the #change method:

Time.zone.now.change(nsec: 0)

lukeredpath avatar Apr 24 '14 15:04 lukeredpath

@lukeredpath This makes sense... Thank you. I will try it.

Nowaker avatar Apr 24 '14 19:04 Nowaker

Please note that my above recommended fix only worked in Rails 3 by accident and no longer works in Rails 4 as Time.change doesn't support nsec. See https://github.com/rails/rails/issues/16392

lukeredpath avatar Aug 04 '14 16:08 lukeredpath

FWIW, seeing this on Rails 4.2.1, Timecop 0.7.3

tyre avatar Jul 30 '15 18:07 tyre

Seeing with Rails 4.2.3, Timecop 0.7.4 as well.

prurph avatar Aug 26 '15 20:08 prurph