比较ruby中相同的DateTime对象 - 为什么这两个DateTime.now不相等?

时间:2013-10-25 21:35:18

标签: ruby-on-rails ruby

在rails应用程序中,我有以下形式的模型代码:

def do_stuff(resource)
  models = Model.where(resource: resource)
  operated_at = DateTime.now

  models.each { |model| some_operation(model, operated_at) }

  some_other_operation models, operated_at
end

def some_operation(model, operated_at)
  model.date_time_field = operated_at
  model.save
end

def some_other_operation(models, operated_at)
  models.each do |model|
    if model.date_time_field < operated_at
      # do something
    end    
  end
end

在这种情况下,将始终执行some_other_operation的“do something”块。更奇怪的是,如果我在没有所有已定义函数的控制台中进行设置,则比较可以正常工作。例如:

> model = Model.first
> time_var = DateTime.now
> model.last_imported_at = time_var
=> Fri, 25 Oct 2013 21:14:06 +0000
> model.save
=> true
> model.last_imported_at < time_var
=> false
> model.last_imported_at == time_var
=> true

最后,如果在some_other_operation中我改为按以下方式进行比较:

if model.date_time_field.to_i < operated_at.to_i
  # do something
end

只有在预期时才会达到'do something'块。我怀疑这是因为to_i方法将删除DateTime对象上定义的秒的分数,并且operated_at变量实际上被重新定义为每个方法范围的DateTime.now。如果是这种情况,那么我想我的问题是如何强制operated_at不为每个范围重新定义?

4 个答案:

答案 0 :(得分:3)

可能的线索;保存和重新加载的行为会截断DateTime的seconds_fraction部分。日期字段成为ActiveSupport::TimeWithZone的实例。只是保存而不重新加载不会这样做;实际的DateTime对象仍在那里。

2.0.0-p195 :001 > dt            =  DateTest.create
2.0.0-p195 :002 > right_now     =  DateTime.now
2.0.0-p195 :004 > dt.created_at =  right_now
2.0.0-p195 :005 > dt.created_at == right_now
 => true 

2.0.0-p195 :006 > dt.save
2.0.0-p195 :007 > dt.created_at == right_now
 => true 

2.0.0-p195 :008 > dt = DateTest.find(dt.id)
2.0.0-p195 :009 > dt.created_at == right_now
 => false 

编辑:当然调用models.each将在那里加载模型,然后由于延迟加载行为。道具给另一个回答者。作为实验,请尝试将models设置为Model.where(resource: resource).to_a

答案 1 :(得分:1)

您有三个单独的方法,上面定义了三个独立的operate_at局部变量。局部变量仅限于定义它们的方法的范围。

您需要定义实例变量,这些变量在整个类中都存在。例如,您可以:

def Model
  attr_accessor :operated_at

  def do_stuff(resource)
    models = Model.where(resource: resource)
    operated_at = DateTime.now

    models.each { |model| some_operation(model, operated_at) }

    some_other_operation models, operated_at
  end

  def some_operation(model, operated_at)
    model.date_time_field = operated_at
    model.save
  end

  def some_other_operation(models, operated_at)
    models.each do |model|
      if model.date_time_field < operated_at
        # do something
      end    
    end
  end
end  

这使您可以在所有类方法中访问operate_at。

答案 2 :(得分:0)

我认为此问题是由于您使用的惰性范围造成的:models = Model.where(resource: resource)

模型是一个代理集合,将在某些时候由rails解决,并且可能在您不确切知道的情况下重新评估。

所以当您更改属性并且在检查属性之前没有reload该对象时,它可能不是最新的。

答案 3 :(得分:0)

DateTime.now无法与Database datetime对象进行比较。

示例:

  1. 如果服务器在太平洋时区运行,您将获得

      
        

    DateTime.now - &gt;     星期五,2013年10月25日15:28:21 -0800

  2.          (10月的夏令时应该是UTC的-7小时,但不知何故,datetime.now总是从UTC给你-8)

      

    1. 我认为如果你使用Time.zone.now而不是DateTime.now会给你正确的比较。

        
          

      Time.zone.now - &gt;     星期五,2013年10月25日15:27:17 PDT -07:00

    2.