(Time.now.utc.to_date + 1.month + 15.days)!=(Time.now.utc.to_date + 15.days + 1.month)

时间:2014-10-16 15:43:35

标签: ruby-on-rails ruby

这怎么可能?

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

4 个答案:

答案 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)

10月有31天,11月没有。这意味着它取决于你如何计算第31 + 1个月。

对于第一个例子:

  • 现在+ 1个月= 16-Nov
  • 16-Nov + 15天= 1-Dec

对于第二个例子:

  • 现在+ 15天= 31-Oct
  • 31-Oct + 1个月= 30-Nov

答案 2 :(得分:4)

10月有31天。当您将10月16日的15天添加到10月31日。添加一个月会带您到下个月的同一天 - 11月31日,但是没有11月31日,所以它将带您到11月30日。

如果您先添加月份,则会将您带到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日。

这就是惯例。