这怎么可能?
Time.now.utc.to_date + 1.month + 15.days #=> Mon, 01 Dec 2014
Time.now.utc.to_date + 15.days + 1.month #=> Sun, 30 Nov 2014
有人见过吗?
/编辑
我想我的问题是错误的。那你们怎么解释呢?
Time.now.utc.to_date + (15.days + 1.month) #=> Mon, 08 Dec 2014
Time.now.utc.to_date + (1.month + 15.days) #=> Tue, 09 Dec 2014
(15.days + 1.month) #=> 3888000
(1.month + 15.days) #=> 3888000
答案 0 :(得分:6)
首先看看Integer#month,它会返回ActiveSupport::Duration
的实例。在rails控制台:
~/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
这意味着:如果我发送:+给一个带有ActiveSupport :: Duration实例作为参数的Date实例,它会调用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
记住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
当提前recive month: 1
从stdlib调用Date#>>时,该工作与ActiveSupport :: Duration#+不同。在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)>
很明显,日期#&gt;&gt;不要添加天数,添加月份并保留日期编号。如果这一天对目标月份无效,则修复它。添加修正的月数不会修复添加的天数,因为这取决于开始日期。
现在我们可以说Date#+与ActiveSupport :: Duration#+不同,我们知道原因。
anwer是使用ActiveSupport调用的日期#+ ::持续时间实例(比如持续时间)不关心duration.value,它使用的是duration.parts,在每种情况下都不同。
答案 1 :(得分:5)
对于第一个例子:
对于第二个例子:
答案 2 :(得分:4)
如果您先添加月份,则会将您带到11月16日。然后再添加15天会将您带到12月1日。
答案 3 :(得分:2)
当你这样做时:
(15.days + 1.month) #=> 3888000
(1.month + 15.days) #=> 3888000
您没有操作日期,您正在操作秒数(Rails Numeric < Object
)。为了证明,让我们把它转换回几天:
> 3888000 / 60 / 60 / 24
=> 45
45 = 30 + 15.所以我们知道,当操作数秒或数天时,编译器在运行Numerics时默认将1.month
解释为30天。参见数字参考:
http://api.rubyonrails.org/classes/Numeric.html#method-i-seconds
正如您在上面的链接中所看到的,当您使用Numerics操作日期时,rails会调用advance(options)
方法,该方法负责执行正确的日期操作。请参阅github上的高级定义:
https://github.com/rails/rails/blob/ffc273577a795bb41068bfc2a1bb575ec51a9712/activesupport/lib/active_support/core_ext/time/calculations.rb#L99
此外,使用Time.now.utc.to_date + (1.month + 15.days)
操作日期时,+ ()
函数实际上会调用advance(options)
方法,如下所示:
(Time.now.utc.to_date.advance(month:1)).advance(days:15) #fistCase
当您使用Time.now.utc.to_date + (15.days + 1.month)
时,将会调用此内容:
(Time.now.utc.to_date.advance(days:15)).advance(month:1) #secondCase
所以,让我们测试#firstCase:
oct16 = Date.new(2014, 10, 16)
> oct16 + (1.month + 15.days)
=> Mon, 01 Dec 2014
> (oct16.advance(months:1)).advance(days:15)
=> Mon, 01 Dec 2014
#firstCase结论是,它调用提前(月份:1)导致11月16日,然后它在11月16日调用.advance(天数:15)并转到Dez-01
让我们检查#secondCase:
> oct16 + (15.days + 1.month)
=> Sun, 30 Nov 2014
> (oct16.advance(days:15)).advance(months:1)
=> Sun, 30 Nov 2014
#secondCase结论是,它调用提前(天数:15),结果是10月31日,而不是调用提前(月:1)的最后结果,这将给我们11月31日,但等等! 11月31日不存在!因此,口译员足够明智,因为你是在月的最后一天(10月31日),当你加1个月或提前(月:1)时,你要求他带你到在下个月的最后一天,在那种情况下,11月30日。
这就是惯例。