aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Griffin <sean@seantheprogrammer.com>2016-11-29 13:02:14 -0500
committerSean Griffin <sean@seantheprogrammer.com>2016-11-29 13:02:14 -0500
commit32f215c3014e580e512db5e8772d0deadf3d6497 (patch)
tree3d0c3e9a972f371e0040c134f72c362faa3fda1a
parent4806317b65314c28a2608fc4e55fb0a160db2b7d (diff)
downloadrails-32f215c3014e580e512db5e8772d0deadf3d6497.tar.gz
rails-32f215c3014e580e512db5e8772d0deadf3d6497.tar.bz2
rails-32f215c3014e580e512db5e8772d0deadf3d6497.zip
Treat combined durations as a single unit
Prior to this commit, `3.months - 3.months` would result in a duration that has the "parts" of `[[:months, 3], [:months, -3]]`. This would mean that it was subtly different than `2.months - 2.months`. When applied to a time, the date might actually change if the resulting day doesn't exist however many months in the future, even though in both cases we just wanted to add `0`, which should always be an identity operation. With this change, we now store the parts as a hash, so `3.months - 3.months` is simply stored as `{ months: 0 }`.
-rw-r--r--activesupport/lib/active_support/duration.rb12
-rw-r--r--activesupport/test/core_ext/duration_test.rb11
2 files changed, 20 insertions, 3 deletions
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 82322291d0..ad2e139ea6 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -15,16 +15,22 @@ module ActiveSupport
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
def initialize(value, parts) #:nodoc:
- @value, @parts = value, parts
+ @value, @parts = value, parts.to_h
+ @parts.default = 0
end
# Adds another Duration or a Numeric to this Duration. Numeric values
# are treated as seconds.
def +(other)
if Duration === other
- Duration.new(value + other.value, @parts + other.parts)
+ parts = @parts.dup
+ other.parts.each do |(key, value)|
+ parts[key] += value
+ end
+ Duration.new(value + other.value, parts)
else
- Duration.new(value + other, @parts + [[:seconds, other]])
+ seconds = @parts[:seconds] + other
+ Duration.new(value + other, @parts.merge(seconds: seconds))
end
end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index b7b4a9dd00..26a9ea4aee 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -345,6 +345,17 @@ class DurationTest < ActiveSupport::TestCase
end
end
+ def test_adding_durations_do_not_hold_prior_states
+ time = Time.parse("Nov 29, 2016")
+ # If the implementation adds and subtracts 3 months, the
+ # resulting date would have been in February so the day will
+ # change to the 29th.
+ d1 = 3.months - 3.months
+ d2 = 2.months - 2.months
+
+ assert_equal time + d1, time + d2
+ end
+
private
def eastern_time_zone
if Gem.win_platform?