Сперва посмотрим, пожалуйста, Integer#month, он возвращает экземпляр ActiveSupport::Duration
. На консоли рельсы:
~/rails/rfinan (1296000):1 > elapsed = 1.month
=> 2592000
~/rails/rfinan (1296000):1 > elapsed.value
=> 2592000
~/rails/rfinan (1296000):1 > elapsed.parts
=> [[:months,1]]
~/rails/rfinan (1296000):1 > elapsed.is_a? ActiveSupport::Duration
=> true
Это время для метода: ActiveSupport::Duration#+
~/rails/rfinan (1296000):1 > sum1 = 1.month + 15.days
=> 3888000
~/rails/rfinan (1296000):1 > sum2 = 15.days + 1.month
=> 3888000
~/rails/rfinan (1296000):1 > sum1.value
=> 3888000
~/rails/rfinan (1296000):1 > sum1.parts
=> [[:months,1],[:days,15]]
~/rails/rfinan (1296000):1 > sum2.value
=> 3888000
~/rails/rfinan (1296000):1 > sum2.parts
=> [[:days,15],[:months,1]]
~/rails/rfinan (1296000):1 > sum1 == sum2
=> true
~/rails/rfinan (1296000):1 > sum1.value == sum2.value
=> true
~/rails/rfinan (1296000):1 > sum1.parts == sum2.parts
=> false
Теперь Date#+, версия ActiveSupport.
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
alias_method :plus_without_duration, :+
alias_method :+, :plus_with_duration
Это означает, что: если я посылаю: + к экземпляру Date с экземпляром ActiveSupport :: Продолжительность в качестве параметра, он вызывает ActiveSupport::Duration#since, а последний вызывает ActiveSupport::Duration#sum, что впрыскивает над экземпляром даты и вызывает Date#advance на каждой из частей, например продолжительность:
def sum(sign, time = ::Time.current) #:nodoc:
parts.inject(time) do |t,(type,number)|
if t.acts_like?(:time) || t.acts_like?(:date)
if type == :seconds
t.since(sign * number)
else
t.advance(type => sign * number)
end
else
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
end
end
end
Remmember sum1.parts = sum2.parts ?, сумма отправки заранее к экземпляру даты упорядоченным. Посмотрим, что означает Date#advance
def advance(options)
options = options.dup
d = self
d = d >> options.delete(:years) * 12 if options[:years]
d = d >> options.delete(:months) if options[:months]
d = d + options.delete(:weeks) * 7 if options[:weeks]
d = d + options.delete(:days) if options[:days]
d
end
Когда авансовый month: 1
Получать он вызывает Date#>> из STDLIB, что работа diferently из ActiveSupport :: Продолжительность # +. На irb:
~ (main) > Date.new(2014,10,31) >> 1
=> #<Date: 2014-11-30 ((2456992j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 2
=> #<Date: 2014-12-31 ((2457023j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 3
=> #<Date: 2015-01-31 ((2457054j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 4
=> #<Date: 2015-02-28 ((2457082j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 5
=> #<Date: 2015-03-31 ((2457113j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 12
=> #<Date: 2015-10-31 ((2457327j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 1200
=> #<Date: 2114-10-31 ((2493486j,0s,0n),+0s,2299161j)>
~ (main) > Date.new(2014,10,31) >> 12000
=> #<Date: 3014-10-31 ((2822204j,0s,0n),+0s,2299161j)>
Понятно, что дата # >> не добавляет дней, добавляет месяцы и сохраняет номер дня. если день недействителен для целевого месяца, он исправляет его. Добавление фиксированного количества месяцев не фиксирует количество добавленных дней, так как зависит от даты начала.
Теперь можно сказать, что Date # + не совпадает с ActiveSupport :: Duration # +, и мы знаем почему.
Anwer - это дата # +, вызываемая с помощью ActiveSupport :: Срок действия (например, длительность) не заботится о duration.value, он использует duration.parts, которые различаются в каждом случае.
(A) Начните с начала, обратившись на север. Поверните на 90 градусов, а затем перейдите на 100 м. (B) Начните с начала, повернув на север. Идите вперед 100 м, а затем поверните на 90 градусов. Как возможно, что вы получаете разные результаты? Не все в этом мире является коммутативным. – sawa