aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/duration.rb
diff options
context:
space:
mode:
authorAndrew White <andrew.white@unboxed.co>2017-05-20 16:33:09 +0100
committerAndrew White <andrew.white@unboxed.co>2017-05-20 16:36:25 +0100
commit28938dd64c8b4fa1943d0b878d3d832b94fa12a3 (patch)
treefb444105f088ecee23471d5f045f43dfe4e23546 /activesupport/lib/active_support/duration.rb
parent1c2dd9cb66ceda3c8febfed4f3f2cb39ae92bd4a (diff)
downloadrails-28938dd64c8b4fa1943d0b878d3d832b94fa12a3.tar.gz
rails-28938dd64c8b4fa1943d0b878d3d832b94fa12a3.tar.bz2
rails-28938dd64c8b4fa1943d0b878d3d832b94fa12a3.zip
Fix implicit calculations with scalars and durations
Previously calculations where the scalar is first would be converted to a duration of seconds but this causes issues with dates being converted to times, e.g: Time.zone = "Beijing" # => Asia/Shanghai date = Date.civil(2017, 5, 20) # => Mon, 20 May 2017 2 * 1.day # => 172800 seconds date + 2 * 1.day # => Mon, 22 May 2017 00:00:00 CST +08:00 Now the `ActiveSupport::Duration::Scalar` calculation methods will try to maintain the part structure of the duration where possible, e.g: Time.zone = "Beijing" # => Asia/Shanghai date = Date.civil(2017, 5, 20) # => Mon, 20 May 2017 2 * 1.day # => 2 days date + 2 * 1.day # => Mon, 22 May 2017 Fixes #29160, #28970.
Diffstat (limited to 'activesupport/lib/active_support/duration.rb')
-rw-r--r--activesupport/lib/active_support/duration.rb41
1 files changed, 35 insertions, 6 deletions
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index d4424ed792..39deb2313f 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -37,27 +37,56 @@ module ActiveSupport
end
def +(other)
- calculate(:+, other)
+ if Duration === other
+ seconds = value + other.parts[:seconds]
+ new_parts = other.parts.merge(seconds: seconds)
+ new_value = value + other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:+, other)
+ end
end
def -(other)
- calculate(:-, other)
+ if Duration === other
+ seconds = value - other.parts[:seconds]
+ new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
+ new_parts = new_parts.merge(seconds: seconds)
+ new_value = value - other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:-, other)
+ end
end
def *(other)
- calculate(:*, other)
+ if Duration === other
+ new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
+ new_value = value * other.value
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:*, other)
+ end
end
def /(other)
- calculate(:/, other)
+ if Duration === other
+ new_parts = other.parts.map { |part, other_value| [part, value / other_value] }.to_h
+ new_value = new_parts.inject(0) { |total, (part, value)| total + value * Duration::PARTS_IN_SECONDS[part] }
+
+ Duration.new(new_value, new_parts)
+ else
+ calculate(:/, other)
+ end
end
private
def calculate(op, other)
if Scalar === other
Scalar.new(value.public_send(op, other.value))
- elsif Duration === other
- Duration.seconds(value).public_send(op, other)
elsif Numeric === other
Scalar.new(value.public_send(op, other))
else