Skip to content
Permalink
Browse files
Fix rounding errors with #travel_to by resetting the usec on any pass…
…ed time to zero, so we only travel with per-second precision, not anything deeper than that.
  • Loading branch information
dhh committed Aug 17, 2014
1 parent 88d27ae commit 9f6e82ee4783e491c20f5244a613fdeb4024beb5
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 4 deletions.
@@ -1,3 +1,8 @@
* Fix rounding errors with #travel_to by resetting the usec on any passed time to zero, so we only travel
with per-second precision, not anything deeper than that.

*DHH*

* Fix ActiveSupport::TestCase not to order users' test cases by default.
If this change breaks your tests because your tests are order dependent, you need to explicitly call
ActiveSupport::TestCase.my_tests_are_order_dependent! at the top of your tests.
@@ -78,6 +78,10 @@ def travel(duration, &block)
# or <tt>Date.today</tt>, in order to honor the application time zone
# please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
#
# Note that the usec for the time passed will be set to 0 to prevent rounding
# errors with external services, like MySQL (which will round instead of floor,
# leading to off-by-one-second errors).
#
# This method also accepts a block, which will return the current time back to its original
# state at the end of the block:
#
@@ -90,7 +94,7 @@ def travel_to(date_or_time, &block)
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
now = date_or_time.midnight.to_time
else
now = date_or_time.to_time
now = date_or_time.to_time.change(usec: 0)
end

simple_stubs.stub_object(Time, :now, now)
@@ -188,19 +188,19 @@ def test_time_helper_travel
expected_time = Time.now + 1.day
travel 1.day

assert_equal expected_time, Time.now
assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
assert_equal expected_time.to_date, Date.today
end

def test_time_helper_travel_with_block
expected_time = Time.now + 1.day

travel 1.day do
assert_equal expected_time, Time.now
assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
assert_equal expected_time.to_date, Date.today
end

assert_not_equal expected_time, Time.now
assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db)
assert_not_equal expected_time.to_date, Date.today
end

@@ -235,4 +235,11 @@ def test_time_helper_travel_back
assert_not_equal expected_time, Time.now
assert_not_equal Date.new(2004, 11, 24), Date.today
end

def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
assert_equal 50, Time.now.sec
assert_equal 0, Time.now.usec
end
end
end

3 comments on commit 9f6e82e

@gkop
Copy link

@gkop gkop commented on 9f6e82e Sep 7, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you reconsider this? Many of us have used Timecop for years without this rounding behavior, and it seems strange to account for a limitation of MySQL by compromising part of Rails that isn't really related to the database (preventing us from employing #travel_to fruitfully on sub-second timescales).

@padde
Copy link

@padde padde commented on 9f6e82e Sep 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, this makes travel_to pretty much useless for assertions on Postgres timestamps.

@frankywahl
Copy link
Contributor

@frankywahl frankywahl commented on 9f6e82e Jun 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this very annoying as well. It took me a while to find out why this code would fail in a test.

def test_time_current 
   t = Time.current
   travel_to t do
      assert_equal t, Time.current
   end
end

Please sign in to comment.