Comparing identical DateTime objects in ruby — why are these two DateTime.now's not equal?

后端 未结 4 836
后悔当初
后悔当初 2021-01-13 12:55

In a rails app, I have model code of the form:

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

  models.each         


        
相关标签:
4条回答
  • 2021-01-13 13:29

    Possible clue; the act of saving and reloading truncates the seconds_fraction part of the DateTime. The date field becomes an instance of ActiveSupport::TimeWithZone. Just saving without reloading doesn't do this; the real DateTime object is still there.

    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 
    

    Edit: of course calling models.each is going to load the models there and then because of the lazy loading behaviour. Props to the other answerer. As an experiment, try setting models to Model.where(resource: resource).to_a.

    0 讨论(0)
  • 2021-01-13 13:32

    DateTime.now doesn't work for comparison with Database datetime object.

    Example:

    1. If you have the server running in Pacific Time Zone, you will expect to get the

      DateTime.now --> Fri, 25 Oct 2013 15:28:21 -0800

    (Daylight Saving Time in October supposed to be -7 hour from UTC, but somehow the datetime.now always give you -8 from UTC)

    1. I think if you use Time.zone.now instead of DateTime.now will give you the correct comparison.

      Time.zone.now --> Fri, 25 Oct 2013 15:27:17 PDT -07:00

    0 讨论(0)
  • 2021-01-13 13:35

    I assume that this problem is due to the lazy scope that you use: models = Model.where(resource: resource)

    models is a proxy collection and will be resolved by rails at some point and might be re-evaluated without you knowing exactly.

    so when you change an attribute and you don't reload the object before checking a property it might not be up to date.

    0 讨论(0)
  • 2021-01-13 13:37

    You have three separate methods defined above with three separate operated_at local variables. Local variables are limited to the scope of the method which defines them.

    You need to define instance variables, which persist throughout a class. For example, you could:

    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  
    

    This would enable you to access operated_at throughout all of the class methods.

    0 讨论(0)
提交回复
热议问题