From 0719b9412db6b51f5d954fd90e71a9b50dd98a4d Mon Sep 17 00:00:00 2001 From: BK Date: Sun, 25 Oct 2020 12:06:53 +1100 Subject: [PATCH] Fix: timezone bug - rounding problem Why this commit? TimeWithZone instances were unexpectedly being rounded up. What changes were made and why? Internally, .to_f was being called on TimeWithZone instances. This can lead to inaccuracies if Rationals are involved. Using .to_r instead of .to_f will be more accurate, but it does come with a slight computational cost. --- .../lib/active_support/core_ext/time/calculations.rb | 4 +++- activesupport/test/core_ext/time_with_zone_test.rb | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index eed34965e398f..be1f85b573875 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -47,7 +47,9 @@ def at_with_coercion(*args) # Time.at can be called with a time or numerical value time_or_number = args.first - if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime) + if time_or_number.is_a?(ActiveSupport::TimeWithZone) + at_without_coercion(time_or_number.to_r).getlocal + elsif time_or_number.is_a?(DateTime) at_without_coercion(time_or_number.to_f).getlocal else at_without_coercion(time_or_number) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 9471bcf118444..678511a6661fb 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1162,6 +1162,15 @@ def test_use_zone_raises_on_invalid_timezone assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone end + def test_end_of_period_timezone_equality_with_DateTime + Time.use_zone "UTC" do + without_time_zone = "2019-01-01 00:00:00Z".to_time.end_of_month + zoned = without_time_zone.in_time_zone + + assert_equal(Time.zone.at(without_time_zone), Time.zone.at(zoned)) + end + end + def test_time_zone_getter_and_setter Time.zone = ActiveSupport::TimeZone["Alaska"] assert_equal ActiveSupport::TimeZone["Alaska"], Time.zone