From e05c2c07ba4ec93e84dcf228b4c617f98ad2489d Mon Sep 17 00:00:00 2001 From: Daniel George Holz Date: Wed, 14 Aug 2024 16:46:31 +0100 Subject: [PATCH] Calculate travel_offset to align with the precision of argument to Timecop.travel (#421) * Calculate travel_offset to align with clock precision Subtracting two `Time`s returns a `Float`, which may not be accurate down to subsecond resolution. Because `Float`s are stored as double- precision values (IEEE 754), they can have resolutions much higher than the typical minimum clock precision of 10e-9 seconds. Which can result in two `Time` object not comparing as equal when they are the same down to the nanosecond, when one has had a travel_offset applied to it. * Use the realtime clock's resolution when testing travel_offset's alignment * Improve failure message for travel_offset alignment test * Test that time_offset keeps precision of very high times passed to Timecop.travel * Mention this change in History.md --- History.md | 2 ++ lib/timecop/time_stack_item.rb | 2 +- test/time_stack_item_test.rb | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 91442a9..4f2eaf2 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,8 @@ ## Unreleased +- Calculate travel_offset to align with the precision of argument to Timecop.travel ([#421](https://github.com/travisjeffery/timecop/pull/421)) + ## v0.9.10 - Make Process.clock_gettime configurable and turned off by default (for backwards compatability) ([#427](https://github.com/travisjeffery/timecop/pull/427)) diff --git a/lib/timecop/time_stack_item.rb b/lib/timecop/time_stack_item.rb index ef49150..82373d6 100644 --- a/lib/timecop/time_stack_item.rb +++ b/lib/timecop/time_stack_item.rb @@ -157,7 +157,7 @@ def parse_time(*args) end def compute_travel_offset - time - Time.now_without_mock_time + time.to_r - Time.now_without_mock_time.to_r end def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds diff --git a/test/time_stack_item_test.rb b/test/time_stack_item_test.rb index ed5ccf7..e65bbeb 100644 --- a/test/time_stack_item_test.rb +++ b/test/time_stack_item_test.rb @@ -296,4 +296,22 @@ def test_datetime_timezones assert_equal dt, now, "#{dt.to_f}, #{now.to_f}" end end + + def test_travel_offset_aligns_to_clock + t = Time.now + stack_item = Timecop::TimeStackItem.new(:travel, t) + travel_offset_denom = stack_item.travel_offset.to_r.denominator + clock_resolution = Process.clock_getres(:CLOCK_REALTIME, :hertz) + assert_equal 0, clock_resolution.modulo(travel_offset_denom), + "travel offset precision (#{travel_offset_denom}) does not align with clock resolution (#{clock_resolution})" + end + + def test_travel_offset_aligns_to_travel_time + t = Time.now + 0.001_002_003_004 + stack_item = Timecop::TimeStackItem.new(:travel, t) + travel_offset_denom = stack_item.travel_offset.to_r.denominator + travel_time_denom = t.to_r.denominator + assert_equal 0, travel_time_denom.modulo(travel_offset_denom), + "travel offset precision (#{travel_offset_denom}) does not align with travel time precision (#{travel_time_denom})" + end end