You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
4.6 KiB
127 lines
4.6 KiB
From 28938dd64c8b4fa1943d0b878d3d832b94fa12a3 Mon Sep 17 00:00:00 2001
|
|
From: Andrew White <andrew.white@unboxed.co>
|
|
Date: Sat, 20 May 2017 16:33:09 +0100
|
|
Subject: [PATCH] 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.
|
|
---
|
|
activesupport/CHANGELOG.md | 22 +++++++++++++++
|
|
activesupport/lib/active_support/duration.rb | 41 ++++++++++++++++++++++++----
|
|
activesupport/test/core_ext/duration_test.rb | 34 +++++++++++++++++++++++
|
|
3 files changed, 91 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
|
|
index c50c1902fe53..bae573cf37ac 100644
|
|
--- a/activesupport/CHANGELOG.md
|
|
+++ b/activesupport/CHANGELOG.md
|
|
@@ -1,3 +1,25 @@
|
|
+* Fix implicit coercion 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.
|
|
+
|
|
+ *Andrew White*
|
|
+
|
|
## Rails 5.1.0 (April 27, 2017) ##
|
|
|
|
* `ActiveSupport::EventedFileUpdateChecker` no longer listens to
|
|
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
|
|
index d4424ed792a4..39deb2313f5a 100644
|
|
--- a/activesupport/lib/active_support/duration.rb
|
|
+++ b/activesupport/lib/active_support/duration.rb
|
|
@@ -37,27 +37,56 @@ def <=>(other)
|
|
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
|