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

Ticket #6835: add-duration-to-date-time2.diff

File add-duration-to-date-time2.diff, 10.8 kB (added by eventualbuddha, 2 years ago)
  • activesupport/test/core_ext/numeric_ext_test.rb

    old new  
    3434      assert seconds.from_now >= now + seconds 
    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 
    3767end 
    3868 
     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 
     84end 
     85 
    3986class NumericExtSizeTest < Test::Unit::TestCase 
    4087  def test_unit_in_terms_of_another 
    4188    relationships = { 
  • activesupport/lib/active_support/core_ext/time/calculations.rb

    old new  
    55      module Calculations 
    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 
    1015        module ClassMethods 
     
    190195        def tomorrow 
    191196          self.since(1.day) 
    192197        end 
     198         
     199        def plus_with_duration(other) #:nodoc: 
     200          if ActiveSupport::CoreExtensions::Numeric::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::CoreExtensions::Numeric::Duration === other 
     209            other.until(self) 
     210          else 
     211            minus_without_duration(other) 
     212          end 
     213        end 
    193214      end 
    194215    end 
    195216  end 
  • activesupport/lib/active_support/core_ext/numeric/time.rb

    old new  
    33    module Numeric #:nodoc: 
    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 
    1231      # date and time arithmetic 
    1332      module Time 
    1433        def seconds 
    15           self 
     34          Duration.new(self, [[:seconds, self]]) 
    1635        end 
    1736        alias :second :seconds 
    1837 
    1938        def minutes 
    20           self * 60 
     39          Duration.new(self * 60, [[:seconds, self * 60]]) 
    2140        end 
    2241        alias :minute :minutes   
    2342         
    2443        def hours 
    25           self * 60.minutes 
     44          Duration.new(self * 3600, [[:seconds, self * 3600]]) 
    2645        end 
    2746        alias :hour :hours 
    2847         
    2948        def days 
    30           self * 24.hours 
     49          Duration.new(self * 24.hours, [[:days, self]]) 
    3150        end 
    3251        alias :day :days 
    3352 
    3453        def weeks 
    35           self * 7.days 
     54          Duration.new(self * 7.days, [[:days, self * 7]]) 
    3655        end 
    3756        alias :week :weeks 
    3857         
    3958        def fortnights 
    40           self * 2.weeks 
     59          Duration.new(self * 2.weeks, [[:days, self * 14]]) 
    4160        end 
    4261        alias :fortnight :fortnights 
    4362         
    4463        def months 
    45           self * 30.days 
     64          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          Duration.new(self * 365.25.days, [[:years, self]]) 
    5170        end 
    5271        alias :year :years 
    5372 
     
    6786        # Reads best without arguments:  10.minutes.from_now 
    6887        alias :from_now :since 
    6988      end 
     89       
     90      class Duration < Builder::BlankSlate 
     91        attr_accessor :value, :parts 
     92         
     93        def initialize(value, parts) 
     94          @value, @parts = value, parts 
     95        end 
     96         
     97        def +(other) 
     98          if Duration === other 
     99            Duration.new(value + other.value, @parts + other.parts) 
     100          else 
     101            Duration.new(value + other, @parts + [[:seconds, other]]) 
     102          end 
     103        end 
     104         
     105        def -(other) 
     106          self + (-other) 
     107        end 
     108         
     109        def -@ 
     110          Duration.new(-value, parts.map { |type,number| [type, -number] }) 
     111        end 
     112         
     113        def is_a?(klass) 
     114          klass == Duration || super 
     115        end 
     116         
     117        def self.===(other) 
     118          other.is_a?(Duration) rescue super 
     119        end 
     120         
     121        def since(time = ::Time.now) 
     122          sum(1, time) 
     123        end 
     124        alias :from_now :since 
     125         
     126        def ago(time = ::Time.now) 
     127          sum(-1, time) 
     128        end 
     129        alias :until :ago 
     130         
     131        def inspect 
     132          "(#{value}, #{parts.inspect})" 
     133        end 
     134         
     135        protected 
     136         
     137        def sum(sign, time = ::Time.now) 
     138          parts.inject(time) do |t,(type,number)| 
     139            case t 
     140            when ::Time 
     141              if type == :seconds 
     142                t + (sign * number) 
     143              else 
     144                t.advance(type => sign * number) 
     145              end 
     146            when ::Date 
     147              raise ArgumentError, "Adding seconds to a Date does not make sense" if type == :seconds 
     148              t.advance(type => sign * number) 
     149            end 
     150          end 
     151        end 
     152         
     153        private 
     154         
     155        def method_missing(method, *args, &block) 
     156          value.send(method, *args) 
     157        end 
     158      end 
    70159    end 
    71160  end 
    72161end 
  • activesupport/lib/active_support/core_ext/date/calculations.rb

    old new  
     1module ActiveSupport #:nodoc: 
     2  module CoreExtensions #:nodoc: 
     3    module Date #:nodoc: 
     4      # Enables the use of time calculations within Time itself 
     5      module Calculations 
     6        def self.included(base) #:nodoc: 
     7          base.send(:include, ClassMethods) 
     8           
     9          base.send(:alias_method, :plus_without_duration, :+) 
     10          base.send(:alias_method, :+, :plus_with_duration) 
     11           
     12          base.send(:alias_method, :minus_without_duration, :-) 
     13          base.send(:alias_method, :-, :minus_with_duration) 
     14        end 
     15 
     16        module ClassMethods 
     17          def plus_with_duration(other) 
     18            if ActiveSupport::CoreExtensions::Numeric::Duration === other 
     19              other.since(self) 
     20            else 
     21              plus_without_duration(other) 
     22            end 
     23          end 
     24           
     25          def minus_with_duration(other) 
     26            self.plus_with_duration(-other) 
     27          end 
     28 
     29          def advance(options) 
     30            d = ::Date.new(year + (options.delete(:years) || 0), month, day) 
     31            d = d >> options.delete(:months) if options[:months] 
     32            d = d + options.delete(:days) if options[:days] 
     33            d 
     34          end 
     35        end 
     36      end 
     37    end 
     38  end 
     39end 
  • activesupport/lib/active_support/core_ext/date.rb

    old new  
    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