Skip to content

Commit

Permalink
Retain UTC offset when using Time.at_with_coercion
Browse files Browse the repository at this point in the history
The standard Ruby behavior for Time.at is to return the same type of
time when passing an instance of Time as a single argument. Since the
an ActiveSupport::TimeWithZone instance may be a different timezone than
the system timezone and DateTime just understands offsets the best we
can do is to return an instance of Time with the correct offset.

Fixes #11350.
  • Loading branch information
pixeltrix committed Jul 9, 2013
1 parent 4842535 commit 1b38737
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 3 deletions.
6 changes: 6 additions & 0 deletions activesupport/CHANGELOG.md
@@ -1,3 +1,9 @@
* Make `Time.at_with_coercion` retain the second fraction and offset from UTC.

Fixes #11350

*Neer Friedman*, *Andrew White*

* Make `HashWithIndifferentAccess#select` always return the hash, even when
`Hash#select!` returns `nil`, to allow further chaining.

Expand Down
11 changes: 8 additions & 3 deletions activesupport/lib/active_support/core_ext/time/calculations.rb
Expand Up @@ -33,10 +33,15 @@ def current
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument
def at_with_coercion(*args)
if args.size == 1 && args.first.acts_like?(:time)
at_without_coercion(args.first.to_f)
return at_without_coercion(*args) if args.size != 1

# 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)
at_without_coercion(time_or_number.to_f).getlocal(time_or_number.utc_offset)
else
at_without_coercion(*args)
at_without_coercion(time_or_number)
end
end
alias_method :at_without_coercion, :at
Expand Down
33 changes: 33 additions & 0 deletions activesupport/test/core_ext/time_ext_test.rb
Expand Up @@ -700,6 +700,12 @@ def test_at_with_datetime
end
end

def test_at_with_datetime_maintains_offset
with_env_tz 'US/Eastern' do
assert_equal 3600, Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0, '+1')).utc_offset
end
end

def test_at_with_time_with_zone
assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']))

Expand All @@ -711,10 +717,37 @@ def test_at_with_time_with_zone
end
end

def test_at_with_time_with_zone_maintains_offset
with_env_tz 'US/Eastern' do
assert_equal 0, Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['London'])).utc_offset
assert_equal 3600, Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 7, 1, 0, 0, 0), ActiveSupport::TimeZone['London'])).utc_offset
end
end

def test_at_with_time_microsecond_precision
assert_equal Time.at(Time.utc(2000, 1, 1, 0, 0, 0, 111)).to_f, Time.utc(2000, 1, 1, 0, 0, 0, 111).to_f
end

def test_at_with_utc_time
with_env_tz 'US/Eastern' do
assert_equal Time.utc(2000), Time.at(Time.utc(2000))
assert_equal 0, Time.at(Time.utc(2000)).utc_offset
assert_equal 'UTC', Time.at(Time.utc(2000)).zone
end
end

def test_at_with_local_time
with_env_tz 'US/Eastern' do
assert_equal Time.local(2000), Time.at(Time.local(2000))
assert_equal -18000, Time.at(Time.local(2000)).utc_offset
assert_equal 'EST', Time.at(Time.local(2000)).zone

assert_equal Time.local(2000, 7, 1), Time.at(Time.local(2000, 7, 1))
assert_equal -14400, Time.at(Time.local(2000, 7, 1)).utc_offset
assert_equal 'EDT', Time.at(Time.local(2000, 7, 1)).zone
end
end

def test_eql?
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) )
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
Expand Down

0 comments on commit 1b38737

Please sign in to comment.