From 8c26e22b39871f64db49e7d6d6726e27dc459a9e Mon Sep 17 00:00:00 2001 From: Dan Barry Date: Sun, 12 Oct 2008 22:05:41 -0500 Subject: [PATCH] Date.durations_between: Date.days_between, Date.weeks_between, Date.months_between, Date.years_between --- .../active_support/core_ext/date/calculations.rb | 30 +++++++++++++ activesupport/lib/active_support/duration.rb | 8 ++++ activesupport/test/core_ext/date_ext_test.rb | 44 ++++++++++++++++++++ activesupport/test/core_ext/duration_test.rb | 17 ++++++++ 4 files changed, 99 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 43d70c7..7bd4847 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -30,6 +30,36 @@ module ActiveSupport #:nodoc: def current ::Time.zone_default ? ::Time.zone.today : ::Date.today end + + def durations_between(start, finish, duration) + return durations_between(finish, start, duration) if start > finish + count = 0 + multiplier = 512 + while (multiplier > 0) + while ((check = start + duration * multiplier) <= finish) + count += multiplier + start = check + end + multiplier /= 2 + end + count + ((finish - start) / (finish + duration - finish)) + end + + def days_between(start, finish) + durations_between(start, finish, 1.day) + end + + def weeks_between(start, finish) + durations_between(start, finish, 1.week) + end + + def months_between(start, finish) + durations_between(start, finish, 1.month) + end + + def years_between(start, finish) + durations_between(start, finish, 1.year) + end end # Tells whether the Date object's date lies in the past diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 8eae85d..3739ce4 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -31,6 +31,14 @@ module ActiveSupport Duration.new(-value, parts.map { |type,number| [type, -number] }) end + def *(count) + Duration.new(value * count, parts.map { |type, number| [type, number * count] }) + end + + def /(divisor) + self * (1.0 / divisor) + end + def is_a?(klass) #:nodoc: klass == Duration || super end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index b53c754..a5fad46 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -256,6 +256,50 @@ class DateExtCalculationsTest < Test::Unit::TestCase end end + def test_durations_between + assert_equal 2, Date.durations_between(Date.new(1999, 12, 29), Date.new(1999, 12, 31), 1.day) + end + + def test_durations_between_wrapping + assert_equal 1, Date.durations_between(Date.new(1999, 12, 31), Date.new(2000, 1, 1), 1.day) + end + + def test_durations_between_fractions + assert_equal 1.25, Date.durations_between(Date.new(1999, 12, 25), Date.new(1999, 12, 30), 4.days) + end + + def test_days_between + assert_equal 2, Date.days_between(Date.new(1999, 12, 29), Date.new(1999, 12, 31)) + end + + def test_days_between_wrapping + assert_equal 3, Date.days_between(Date.new(1999, 12, 30), Date.new(2000, 1, 2)) + end + + def test_weeks_between + assert_equal 2, Date.weeks_between(Date.new(1999, 12, 5), Date.new(1999, 12, 19)) + end + + def test_months_between + assert_equal 2, Date.months_between(Date.new(1999, 10, 1), Date.new(1999, 12, 1)) + end + + def test_months_between_wrapping + assert_equal 4, Date.months_between(Date.new(1999, 10, 1), Date.new(2000, 2, 1)) + end + + def test_months_between_fractions + assert_equal 1.5, Date.months_between(Date.new(1999, 10, 15), Date.new(1999, 11, 30)) + end + + def test_years_between + assert_equal 2, Date.years_between(Date.new(1999, 12, 31), Date.new(2001, 12, 31)) + end + + def test_years_between_fractions + assert_equal 1.2, Date.years_between(Date.new(1999, 6, 1), Date.new(2000, 8, 13)) + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index f802ed8..7f73a4b 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -19,6 +19,23 @@ class DurationTest < Test::Unit::TestCase assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration" end + def test_multiplication + assert_equal 2.months, 1.month * 2 + assert_equal 18.months - 6.days, (6.months - 2.days) * 3 + assert_equal -40.years - 8.months - 4.days, (10.years + 2.months + 1.day) * -4 + end + + def test_multiplication_associativity + assert_equal 1.month * 2, 2 * 1.month + assert_equal 1.month * 1.5, 1.5 * 1.month + end + + def test_division + assert_equal 1.month, 2.months / 2 + assert_equal 6.months - 2.days, (18.months - 6.days) / 3 + assert_equal -10.years - 2.months - 1.day, (40.years + 8.months + 4.days) / -4 + end + def test_argument_error begin 1.second.ago('') -- 1.5.4.2