1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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 }`.
This commit is contained in:
Sean Griffin 2016-11-29 13:02:14 -05:00
parent 4806317b65
commit 32f215c301
2 changed files with 20 additions and 3 deletions

View file

@ -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

View file

@ -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?