Skip to content

Commit

Permalink
Fix Time.now/DateTime.now/Date.today to return results in a system ti…
Browse files Browse the repository at this point in the history
…mezone after #travel_to

There is a bug in the current implementation of #travel_to:
it remembers a timezone of its argument, and all stubbed methods start
returning results in that remembered timezone. However, the expected
behaviour is to return results in a system timezone.

It can lead to bugs in tests like this one:
faker-ruby/faker#2861
  • Loading branch information
907th authored and byroot committed Dec 2, 2023
1 parent 469c3db commit aedb808
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 0 deletions.
9 changes: 9 additions & 0 deletions activesupport/CHANGELOG.md
@@ -1,3 +1,12 @@
* Fix `Time.now/DateTime.now/Date.today' to return results in a system timezone after `#travel_to'.

There is a bug in the current implementation of #travel_to:
it remembers a timezone of its argument, and all stubbed methods start
returning results in that remembered timezone. However, the expected
behaviour is to return results in a system timezone.

*Aleksei Chernenkov*

* Add `ErrorReported#unexpected` to report precondition violations.

For example:
Expand Down
4 changes: 4 additions & 0 deletions activesupport/lib/active_support/testing/time_helpers.rb
Expand Up @@ -169,6 +169,10 @@ def travel_to(date_or_time, with_usec: false)
now = date_or_time.to_time.change(usec: 0)
end

# +now+ must be in local system timezone, because +Time.at(now)+
# and +now.to_date+ (see stubs below) will use +now+'s timezone too!
now = now.getlocal

stubs = simple_stubs
stubbed_time = Time.now if stubs.stubbing(Time, :now)
stubs.stub_object(Time, :now) { at(now) }
Expand Down
38 changes: 38 additions & 0 deletions activesupport/test/time_travel_test.rb
Expand Up @@ -122,6 +122,44 @@ def test_time_helper_travel_to_with_time_zone
end
end

def test_time_helper_travel_to_with_different_system_and_application_time_zones
with_env_tz "US/Eastern" do # system time zone: -05
expected_time_in_2021 = Time.new(2021)

with_tz_default ActiveSupport::TimeZone["Ekaterinburg"] do # application time zone: +05
destination_time = Time.new(2023, 12, 1, 5, 6, 7, 5 * 3600)

# All stubbed methods are expected to return values in system (-05) time zone
expected_utc_offset = -5 * 3600
expected_time = Time.new(2023, 11, 30, 19, 6, 7, expected_utc_offset)
expected_datetime = DateTime.new(2023, 11, 30, 19, 6, 7, -Rational(5, 24))
expected_date = Date.new(2023, 11, 30)

travel_to destination_time do
assert_equal expected_time, Time.now
assert_equal expected_time.to_fs(:db), Time.now.to_fs(:db)
assert_equal expected_utc_offset, Time.now.utc_offset

assert_equal expected_datetime, DateTime.now
assert_equal expected_datetime.to_fs(:db), DateTime.now.to_fs(:db)
assert_equal expected_utc_offset, DateTime.now.utc_offset

assert_equal expected_date, Date.today

# Time.new with no args equals to Time.now
assert_equal expected_time, Time.new
assert_equal expected_time.to_fs(:db), Time.new.to_fs(:db)
assert_equal expected_utc_offset, Time.new.utc_offset

# Time.new with any args falls back to original Ruby implementation
assert_equal expected_time_in_2021, Time.new(2021)
assert_equal expected_time_in_2021.to_fs(:db), Time.new(2021).to_fs(:db)
assert_equal expected_time_in_2021.utc_offset, Time.new(2021).utc_offset
end
end
end
end

def test_time_helper_travel_to_with_string_for_time_zone
with_env_tz "US/Eastern" do
with_tz_default ActiveSupport::TimeZone["UTC"] do
Expand Down

0 comments on commit aedb808

Please sign in to comment.