Skip to content

Commit 44ca0f0

Browse files
committed
Workaround for Ruby UTC offset bug
Some Ruby versions have a bug when creating a Time object backed by a timezone object where they create a fractional-second UTC offset. For the added test, without this workaround, on Ruby 3.3.0: 1) Failure: TimeExtCalculationsTest#test_change_preserves_fractional_seconds_on_zoned_time [./test/core_ext/time_ext_test.rb:528]: --- expected +++ actual @@ -1 +1,3 @@ -"2005-01-30 00:00:00.99 -0500" +# encoding: US-ASCII +# valid: true +"2005-01-30 00:00:00.99 -045959" This is fixed in Ruby 3.3.1 and 3.2.4. We can remove the workaround when we expect users to be on those versions or newer.
1 parent 55c4ade commit 44ca0f0

File tree

2 files changed

+17
-0
lines changed

2 files changed

+17
-0
lines changed

activesupport/lib/active_support/core_ext/time/calculations.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ def change(options)
147147
elsif zone.respond_to?(:utc_to_local)
148148
new_time = ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, zone)
149149

150+
# Some versions of Ruby have a bug where Time.new with a zone object and
151+
# fractional seconds will end up with a broken utc_offset.
152+
# This is fixed in Ruby 3.3.1 and 3.2.4
153+
unless new_time.utc_offset.integer?
154+
new_time += 0
155+
end
156+
150157
# When there are two occurrences of a nominal time due to DST ending,
151158
# `Time.new` chooses the first chronological occurrence (the one with a
152159
# larger UTC offset). However, for `change`, we want to choose the

activesupport/test/core_ext/time_ext_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,16 @@ def test_change_preserves_offset_for_zoned_times_around_end_of_dst
519519
end
520520
end
521521

522+
def test_change_preserves_fractional_seconds_on_zoned_time
523+
with_tz_default "US/Eastern" do
524+
time = Time.new(2005, 10, 30, 00, 00, 0.99r, Time.zone) + 0
525+
time2 = time.change(month: 1)
526+
527+
assert_equal "2005-10-30 00:00:00.99 -0400", time.inspect
528+
assert_equal "2005-01-30 00:00:00.99 -0500", time2.inspect
529+
end
530+
end
531+
522532
def test_change_preserves_fractional_hour_offset_for_local_times_around_end_of_dst
523533
with_env_tz "Australia/Lord_Howe" do
524534
# DST ended just before 2005-03-27 2:00:00 AM in Australia/Lord_Howe, and

0 commit comments

Comments
 (0)