fix: equivalent negative durations add to the same time (#43795)

* bug: illustrate negative durations don't add to the same time

* fix: equivalent negative durations add to the same time

Co-authored-by: Caleb <me@cpb.ca>
Co-authored-by: Braden Staudacher <braden.staudacher@chime.com>

* Updates CHANGELOG with fix to `ActiveSupport::Duration.build`
This commit is contained in:
Caleb Buxton 2021-12-10 11:03:04 -08:00 committed by GitHub
parent 1410c3fd1d
commit ffc1e5f889
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 22 additions and 3 deletions

View File

@ -1,3 +1,10 @@
* Fix `ActiveSupport::Duration.build` to support negative values.
The algorithm to collect the `parts` of the `ActiveSupport::Duration`
ignored the sign of the `value` and accumulated incorrect part values. This
impacted `ActiveSupport::Duration#sum` (which is dependent on `parts`) but
not `ActiveSupport::Duration#eql?` (which is dependent on `value`).
*Caleb Buxton*, *Braden Staudacher*
Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activesupport/CHANGELOG.md) for previous changes.

View File

@ -191,13 +191,14 @@ module ActiveSupport
end
parts = {}
remainder = value.round(9)
remainder_sign = value <=> 0
remainder = value.round(9).abs
variable = false
PARTS.each do |part|
unless part == :seconds
part_in_seconds = PARTS_IN_SECONDS[part]
parts[part] = remainder.div(part_in_seconds)
parts[part] = remainder.div(part_in_seconds) * remainder_sign
remainder %= part_in_seconds
unless parts[part].zero?
@ -206,7 +207,7 @@ module ActiveSupport
end
end unless value == 0
parts[:seconds] = remainder
parts[:seconds] = remainder * remainder_sign
new(value, parts, variable)
end

View File

@ -752,6 +752,17 @@ class DurationTest < ActiveSupport::TestCase
assert (1.day + 12.hours).variable?
end
def test_duration_symmetry
time = Time.parse("Dec 7, 2021")
expected_time = Time.parse("2021-12-06 23:59:59")
assert_equal expected_time, time + -1.second
assert_equal expected_time, time + ActiveSupport::Duration.build(1) * -1
assert_equal expected_time, time + -ActiveSupport::Duration.build(1)
assert_equal expected_time, time + ActiveSupport::Duration::Scalar.new(-1)
assert_equal expected_time, time + ActiveSupport::Duration.build(-1)
end
private
def eastern_time_zone
if Gem.win_platform?