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

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

View file

@ -752,6 +752,17 @@ class DurationTest < ActiveSupport::TestCase
assert (1.day + 12.hours).variable? assert (1.day + 12.hours).variable?
end 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 private
def eastern_time_zone def eastern_time_zone
if Gem.win_platform? if Gem.win_platform?