aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew White <andrew.white@unboxed.co>2017-11-11 15:19:29 +0000
committerAndrew White <andrew.white@unboxed.co>2017-11-15 09:48:50 +0000
commit2eea6458a14d99b19dffe8673015cde0800f128a (patch)
tree2dc03c75ff5b0cf9b151c6d55ab3ba86326f0a3c
parent23c41e4657a507c574e555339821f545c819e602 (diff)
downloadrails-2eea6458a14d99b19dffe8673015cde0800f128a.tar.gz
rails-2eea6458a14d99b19dffe8673015cde0800f128a.tar.bz2
rails-2eea6458a14d99b19dffe8673015cde0800f128a.zip
Handle `TZInfo::AmbiguousTime` errors
Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous times by choosing the later period, e.g. Ruby: ``` ENV["TZ"] = "Europe/Moscow" Time.local(2014, 10, 26, 1, 0, 0) # => 2014-10-26 01:00:00 +0300 ``` Before: ``` >> "2014-10-26 01:00:00".in_time_zone("Moscow") TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time. ``` After: ``` >> "2014-10-26 01:00:00".in_time_zone("Moscow") => Sun, 26 Oct 2014 01:00:00 MSK +03:00 ``` Fixes #17395.
-rw-r--r--activesupport/CHANGELOG.md27
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb2
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb12
-rw-r--r--activesupport/test/time_zone_test.rb26
4 files changed, 66 insertions, 1 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 4762c7d21d..904dab0e05 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,30 @@
+* Handle `TZInfo::AmbiguousTime` errors
+
+ Make `ActiveSupport::TimeWithZone` match Ruby's handling of ambiguous
+ times by choosing the later period, e.g.
+
+ Ruby:
+ ```
+ ENV["TZ"] = "Europe/Moscow"
+ Time.local(2014, 10, 26, 1, 0, 0) # => 2014-10-26 01:00:00 +0300
+ ```
+
+ Before:
+ ```
+ >> "2014-10-26 01:00:00".in_time_zone("Moscow")
+ TZInfo::AmbiguousTime: 26/10/2014 01:00 is an ambiguous local time.
+ ```
+
+ After:
+ ```
+ >> "2014-10-26 01:00:00".in_time_zone("Moscow")
+ => Sun, 26 Oct 2014 01:00:00 MSK +03:00
+ ```
+
+ Fixes #17395.
+
+ *Andrew White*
+
* Redis cache store.
```
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 4d1fbd4453..639a066e28 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -506,7 +506,7 @@ module ActiveSupport
# Available so that TimeZone instances respond like TZInfo::Timezone
# instances.
def period_for_local(time, dst = true)
- tzinfo.period_for_local(time, dst)
+ tzinfo.period_for_local(time, dst) { |periods| periods.last }
end
def periods_for_local(time) #:nodoc:
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 0f80a24758..ab96568956 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -50,6 +50,12 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_raise(ArgumentError) { @twz.in_time_zone(Object.new) }
end
+ def test_in_time_zone_with_ambiguous_time
+ with_env_tz "Europe/Moscow" do
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), Time.local(2014, 10, 26, 1, 0, 0).in_time_zone("Moscow")
+ end
+ end
+
def test_localtime
assert_equal @twz.localtime, @twz.utc.getlocal
assert_instance_of Time, @twz.localtime
@@ -1301,4 +1307,10 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase
assert_raise(ArgumentError) { @u.in_time_zone(Object.new) }
assert_raise(ArgumentError) { @z.in_time_zone(Object.new) }
end
+
+ def test_in_time_zone_with_ambiguous_time
+ with_tz_default "Moscow" do
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), "2014-10-26 01:00:00".in_time_zone
+ end
+ end
end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index acb0ecd226..862e872494 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -32,6 +32,12 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_period_for_local_with_ambigiuous_time
+ zone = ActiveSupport::TimeZone["Moscow"]
+ period = zone.period_for_local(Time.utc(2015, 1, 1))
+ assert_equal period, zone.period_for_local(Time.utc(2014, 10, 26, 1, 0, 0))
+ end
+
def test_from_integer_to_map
assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[-28800] # PST
end
@@ -195,6 +201,11 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_equal "EDT", twz.zone
end
+ def test_local_with_ambiguous_time
+ zone = ActiveSupport::TimeZone["Moscow"]
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.local(2014, 10, 26, 1, 0, 0)
+ end
+
def test_at
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
secs = 946684800.0
@@ -303,6 +314,11 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_iso8601_with_ambiguous_time
+ zone = ActiveSupport::TimeZone["Moscow"]
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.parse("2014-10-26T01:00:00")
+ end
+
def test_parse
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.parse("1999-12-31 19:00:00")
@@ -412,6 +428,11 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_equal "argument out of range", exception.message
end
+ def test_parse_with_ambiguous_time
+ zone = ActiveSupport::TimeZone["Moscow"]
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.parse("2014-10-26 01:00:00")
+ end
+
def test_rfc3339
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
twz = zone.rfc3339("1999-12-31T14:00:00-10:00")
@@ -604,6 +625,11 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime_with_ambiguous_time
+ zone = ActiveSupport::TimeZone["Moscow"]
+ assert_equal Time.utc(2014, 10, 25, 22, 0, 0), zone.strptime("2014-10-26 01:00:00", "%Y-%m-%d %H:%M:%S")
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get("America/New_York")
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)