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.

Show comment
Hide comment
@gkop

gkop Sep 7, 2015

Contributor

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).

Contributor

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.

Show comment
Hide comment
@padde

padde Sep 23, 2016

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

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

@frankywahl

This comment has been minimized.

Show comment
Hide comment
@frankywahl

frankywahl Jun 16, 2017

Contributor

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
Contributor

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.