From aa93e3cb20455872a55ad3b3c84e7679ef889bf2 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 2 Dec 2019 17:58:00 +0900 Subject: [PATCH] Fix `since` and `ago` with a duration which has empty parts Related #37839. Sometimes divisions of `ActiveSupport::Duration` makes the instance's parts attribute empty, but it has a value actually (e.g. `(1.minute / 60) # => @value=1 (second), but empty parts`). In that case we should respect `value` as the source of seconds. --- activesupport/lib/active_support/duration.rb | 12 ++++++++---- activesupport/test/core_ext/duration_test.rb | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 4cce0a6967..fe87cbd436 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -403,8 +403,14 @@ module ActiveSupport private def sum(sign, time = ::Time.current) - parts.inject(time) do |t, (type, number)| - if t.acts_like?(:time) || t.acts_like?(:date) + unless time.acts_like?(:time) || time.acts_like?(:date) + raise ::ArgumentError, "expected a time or date, got #{time.inspect}" + end + + if parts.empty? + time.since(sign * value) + else + parts.inject(time) do |t, (type, number)| if type == :seconds t.since(sign * number) elsif type == :minutes @@ -414,8 +420,6 @@ module ActiveSupport else t.advance(type => sign * number) end - else - raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index f3608cf14d..ed4b0dd438 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -204,7 +204,9 @@ class DurationTest < ActiveSupport::TestCase def test_since_and_ago t = Time.local(2000) assert_equal t + 1, 1.second.since(t) + assert_equal t + 1, (1.minute / 60).since(t) assert_equal t - 1, 1.second.ago(t) + assert_equal t - 1, (1.minute / 60).ago(t) end def test_since_and_ago_without_argument