aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew White <andyw@pixeltrix.co.uk>2014-01-26 16:56:33 +0000
committerAndrew White <andyw@pixeltrix.co.uk>2014-01-26 16:56:33 +0000
commitb5944928700cfc13ed8f615acb54116a3039f345 (patch)
treea4b167f1c0b2064f4fe02291e6d03b96a9ba291a
parent718d3b0bc53bb5da4e5fc32d1a27f2119e6c747c (diff)
downloadrails-b5944928700cfc13ed8f615acb54116a3039f345.tar.gz
rails-b5944928700cfc13ed8f615acb54116a3039f345.tar.bz2
rails-b5944928700cfc13ed8f615acb54116a3039f345.zip
Maintain current timezone when changing time during DST overlap
Currently if a time is changed during DST overlap in the autumn then the method `period_for_local` will return the DST period. However if the original time is not DST then this can be surprising and is not what is generally wanted. This commit changes that behavior to maintain the current period if it's in the list of periods returned by `periods_for_local`. It is possible to alter the behavior of `period_for_local` by specifying a second argument but since we may be change from another time that could be either DST or not then this would give inconsistent results. Fixes #12163.
-rw-r--r--activesupport/CHANGELOG.md12
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb12
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb4
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb5
4 files changed, 30 insertions, 3 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index adaeb99c87..ba450b85c4 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,15 @@
+* Maintain the current timezone when calling `change` during DST overlap
+
+ Currently if a time is changed during DST overlap in the autumn then the method
+ `period_for_local` will return the DST period. However if the original time is
+ not DST then this can be surprising and is not what is generally wanted. This
+ commit changes that behavior to maintain the current period if it's in the list
+ of periods returned by `periods_for_local`.
+
+ Fixes #12163.
+
+ *Andrew White*
+
* Added `Hash#compact` and `Hash#compact!` for removing items with nil value from hash.
*Celestino Gomes*
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 50db7da9d9..d47aee00f4 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -45,7 +45,7 @@ module ActiveSupport
def initialize(utc_time, time_zone, local_time = nil, period = nil)
@utc, @time_zone, @time = utc_time, time_zone, local_time
- @period = @utc ? period : get_period_and_ensure_valid_local_time
+ @period = @utc ? period : get_period_and_ensure_valid_local_time(period)
end
# Returns a Time or DateTime instance that represents the time in +time_zone+.
@@ -292,6 +292,12 @@ module ActiveSupport
end
end
+ def change(options)
+ new_time = time.change(options)
+ periods = time_zone.periods_for_local(new_time)
+ self.class.new(nil, time_zone, new_time, periods.include?(period) ? period : nil)
+ end
+
%w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method_name} # def month
@@ -367,12 +373,12 @@ module ActiveSupport
end
private
- def get_period_and_ensure_valid_local_time
+ def get_period_and_ensure_valid_local_time(period)
# we don't want a Time.local instance enforcing its own DST rules as well,
# so transfer time values to a utc constructor if necessary
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
begin
- @time_zone.period_for_local(@time)
+ period || @time_zone.period_for_local(@time)
rescue ::TZInfo::PeriodNotFound
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
@time += 1.hour
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index beaac42fa1..eb785d46ce 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -352,6 +352,10 @@ module ActiveSupport
tzinfo.period_for_local(time, dst)
end
+ def periods_for_local(time) #:nodoc:
+ tzinfo.periods_for_local(time)
+ end
+
def self.find_tzinfo(name)
TZInfo::TimezoneProxy.new(MAPPING[name] || name)
end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 5494824a40..43fe572bfc 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -511,6 +511,11 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal "Fri, 31 Dec 1999 19:00:30 EST -05:00", @twz.change(:sec => 30).inspect
end
+ def test_change_at_dst_boundary
+ twz = ActiveSupport::TimeWithZone.new(Time.at(1319936400).getutc, ActiveSupport::TimeZone['Madrid'])
+ assert_equal twz, twz.change(:min => 0)
+ end
+
def test_advance
assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect
assert_equal "Mon, 31 Dec 2001 19:00:00 EST -05:00", @twz.advance(:years => 2).inspect