Skip to content

Commit

Permalink
Retain offset and fraction 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.

It also maintains the correct fractional second value as well.

Fixes #11350.

Backports:
4842535
1b38737
  • Loading branch information
pixeltrix committed Jul 9, 2013
1 parent 6b44d6e commit 3f97aab
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
6 changes: 6 additions & 0 deletions activesupport/CHANGELOG.md
@@ -1,5 +1,11 @@
## unreleased ## ## unreleased ##


* Make `Time.at_with_coercion` retain the second fraction and offset from UTC.

Fixes #11350

*Neer Friedman*, *Andrew White*

* Fix return value from `BacktraceCleaner#noise` when the cleaner is configured * Fix return value from `BacktraceCleaner#noise` when the cleaner is configured
with multiple silencers. with multiple silencers.


Expand Down
11 changes: 8 additions & 3 deletions activesupport/lib/active_support/core_ext/time/calculations.rb
Expand Up @@ -69,10 +69,15 @@ def current
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument # instances can be used when called with a single argument
def at_with_coercion(*args) def at_with_coercion(*args)
if args.size == 1 && args.first.acts_like?(:time) return at_without_coercion(*args) if args.size != 1
at_without_coercion(args.first.to_i)
# 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 else
at_without_coercion(*args) at_without_coercion(time_or_number)
end end
end end
alias_method :at_without_coercion, :at alias_method :at_without_coercion, :at
Expand Down
37 changes: 37 additions & 0 deletions activesupport/test/core_ext/time_ext_test.rb
Expand Up @@ -752,6 +752,12 @@ def test_at_with_datetime
end end
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 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'])) 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 @@ -763,6 +769,37 @@ def test_at_with_time_with_zone
end end
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? 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['UTC']) )
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
Expand Down

0 comments on commit 3f97aab

Please sign in to comment.