Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 5940

Show
Ignore:
Timestamp:
01/15/07 06:54:50 (2 years ago)
Author:
nzkoz
Message:

Make 1.months and friends accurate by introducing a Duration class. #6835 [eventualbuddha]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activesupport/CHANGELOG

    r5924 r5940  
    11*SVN* 
     2 
     3* Make 1.months and friends accurate by introducing a Duration class.  #6835 [eventualbuddha] 
    24 
    35* Document Inflector.ordinalize and merge docs from String inflections.  #7023 [smeade] 
  • trunk/activesupport/lib/active_support.rb

    r5223 r5940  
    3939 
    4040require 'active_support/values/time_zone' 
     41require 'active_support/duration' 
    4142 
    4243require 'active_support/json' 
  • trunk/activesupport/lib/active_support/core_ext/date.rb

    r738 r5940  
    11require 'date' 
     2require File.dirname(__FILE__) + '/date/calculations' 
    23require File.dirname(__FILE__) + '/date/conversions' 
    34 
    45class Date#:nodoc: 
     6  include ActiveSupport::CoreExtensions::Date::Calculations 
    57  include ActiveSupport::CoreExtensions::Date::Conversions 
    68end 
  • trunk/activesupport/lib/active_support/core_ext/numeric/time.rb

    r4029 r5940  
    44      # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. 
    55      # 
    6       # If you need precise date calculations that doesn't just treat months as 30 days, then have 
    7       # a look at Time#advance. 
     6      # These methods use Time#advance for precise date calculations when using from_now, ago, etc.  
     7      # as well as adding or subtracting their results from a Time object. For example: 
     8      # 
     9      #   # equivalent to Time.now.advance(:months => 1) 
     10      #   1.month.from_now 
     11      # 
     12      #   # equivalent to Time.now.advance(:years => 2) 
     13      #   2.years.from_now 
     14      # 
     15      #   # equivalent to Time.now.advance(:months => 4, :years => 5) 
     16      #   (4.months + 5.years).from_now 
    817      #  
    9       # Some of these methods are approximations, Ruby's core  
     18      # While these methods provide precise calculation when used as in the examples above, care 
     19      # should be taken to note that this is not true if the result of `months', `years', etc is 
     20      # converted before use: 
     21      # 
     22      #   # equivalent to 30.days.to_i.from_now 
     23      #   1.month.to_i.from_now 
     24      # 
     25      #   # equivalent to 365.25.days.to_f.from_now 
     26      #   1.year.to_f.from_now 
     27      # 
     28      # In such cases, Ruby's core  
    1029      # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and  
    1130      # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision 
     
    1332      module Time 
    1433        def seconds 
    15           self 
     34          ActiveSupport::Duration.new(self, [[:seconds, self]]) 
    1635        end 
    1736        alias :second :seconds 
    1837 
    1938        def minutes 
    20           self * 60 
     39          ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]]) 
    2140        end 
    2241        alias :minute :minutes   
    2342         
    2443        def hours 
    25           self * 60.minutes 
     44          ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]]) 
    2645        end 
    2746        alias :hour :hours 
    2847         
    2948        def days 
    30           self * 24.hours 
     49          ActiveSupport::Duration.new(self * 24.hours, [[:days, self]]) 
    3150        end 
    3251        alias :day :days 
    3352 
    3453        def weeks 
    35           self * 7.days 
     54          ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]]) 
    3655        end 
    3756        alias :week :weeks 
    3857         
    3958        def fortnights 
    40           self * 2.weeks 
     59          ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]]) 
    4160        end 
    4261        alias :fortnight :fortnights 
    4362         
    4463        def months 
    45           self * 30.days 
     64          ActiveSupport::Duration.new(self * 30.days, [[:months, self]]) 
    4665        end 
    4766        alias :month :months 
    4867 
    4968        def years 
    50           (self * 365.25.days).to_i 
     69          ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]]) 
    5170        end 
    5271        alias :year :years 
  • trunk/activesupport/lib/active_support/core_ext/time/calculations.rb

    r5523 r5940  
    66        def self.included(base) #:nodoc: 
    77          base.extend(ClassMethods) 
     8           
     9          base.send(:alias_method, :plus_without_duration, :+) 
     10          base.send(:alias_method, :+, :plus_with_duration) 
     11          base.send(:alias_method, :minus_without_duration, :-) 
     12          base.send(:alias_method, :-, :minus_with_duration) 
    813        end 
    914 
     
    191196          self.since(1.day) 
    192197        end 
     198         
     199        def plus_with_duration(other) #:nodoc: 
     200          if ActiveSupport::Duration === other 
     201            other.since(self) 
     202          else 
     203            plus_without_duration(other) 
     204          end 
     205        end 
     206 
     207        def minus_with_duration(other) #:nodoc: 
     208          if ActiveSupport::Duration === other 
     209            other.until(self) 
     210          else 
     211            minus_without_duration(other) 
     212          end 
     213        end 
    193214      end 
    194215    end 
  • trunk/activesupport/test/core_ext/numeric_ext_test.rb

    r4595 r5940  
    3535    end 
    3636  end 
     37   
     38  def test_irregular_durations 
     39    assert_equal @now.advance(:days => 3000), 3000.days.since(@now) 
     40    assert_equal @now.advance(:months => 1), 1.month.since(@now) 
     41    assert_equal @now.advance(:months => -1), 1.month.until(@now) 
     42    assert_equal @now.advance(:years => 20), 20.years.since(@now) 
     43  end 
     44   
     45  def test_duration_addition 
     46    assert_equal @now.advance(:days => 1, :months => 1), (1.day + 1.month).since(@now) 
     47    assert_equal @now.advance(:days => 7), (1.week + 5.seconds - 5.seconds).since(@now) 
     48    assert_equal @now.advance(:years => 2), (4.years - 2.years).since(@now) 
     49  end 
     50   
     51  def test_time_plus_duration 
     52    assert_equal @now + 8, @now + 8.seconds 
     53    assert_equal @now + 22.9, @now + 22.9.seconds 
     54    assert_equal @now.advance(:days => 15), @now + 15.days 
     55    assert_equal @now.advance(:months => 1), @now + 1.month 
     56  end 
     57   
     58  def test_chaining_duration_operations 
     59    assert_equal @now.advance(:days => 2, :months => -3), @now + 2.days - 3.months 
     60    assert_equal @now.advance(:days => 1, :months => 2), @now + 1.day + 2.months 
     61  end 
     62   
     63  def test_duration_after_convertion_is_no_longer_accurate 
     64    assert_equal 30.days.to_i.since(@now), 1.month.to_i.since(@now) 
     65    assert_equal 365.25.days.to_f.since(@now), 1.year.to_f.since(@now) 
     66  end 
     67end 
     68 
     69class NumericExtDateTest < Test::Unit::TestCase 
     70  def setup 
     71    @today = Date.today 
     72  end 
     73 
     74  def test_date_plus_duration 
     75    assert_equal @today + 1, @today + 1.day 
     76    assert_equal @today >> 1, @today + 1.month 
     77    assert_raises(ArgumentError) { @today + 1.second } 
     78  end 
     79   
     80  def test_chaining_duration_operations 
     81    assert_equal @today.advance(:days => 2, :months => -3), @today + 2.days - 3.months 
     82    assert_equal @today.advance(:days => 1, :months => 2), @today + 1.day + 2.months 
     83  end 
    3784end 
    3885