diff options
author | Sean Griffin <sean@seantheprogrammer.com> | 2016-11-29 13:02:14 -0500 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2016-11-29 13:02:14 -0500 |
commit | 32f215c3014e580e512db5e8772d0deadf3d6497 (patch) | |
tree | 3d0c3e9a972f371e0040c134f72c362faa3fda1a | |
parent | 4806317b65314c28a2608fc4e55fb0a160db2b7d (diff) | |
download | rails-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.rb | 12 | ||||
-rw-r--r-- | activesupport/test/core_ext/duration_test.rb | 11 |
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? |