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
@@ -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

This comment has been minimized.

Copy link
Contributor

@gkop gkop replied Sep 7, 2015

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

This comment has been minimized.

Copy link

@padde padde replied Sep 23, 2016

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

@frankywahl

This comment has been minimized.

Copy link
Contributor

@frankywahl frankywahl replied Jun 16, 2017

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.
You can’t perform that action at this time.