搜索 UNIX 时间戳给出不同的结果

Searching UNIX timestamp gives differing results

以下查询给出了不同的结果,两者的结果应该是两个。我在我的数据库 (postgres) 中使用时间戳列,并正在搜索其 end_at 列小于或等于给定 UNIX 时间戳的对象。

puts object.time_records.where('time_records.end_at <= ?', object.time_records.second.end_at).count #=> 2 (Correct)
puts object.time_records.where('time_records.end_at <= ?', DateTime.strptime(object.time_records.second.end_at.to_i.to_s, '%s')).count # => 1 (Incorrect)
puts object.time_records.where('time_records.end_at <= ?', Time.at(object.time_records.second.end_at.to_i)).count # => 1 (Incorrect)

如果我播种一些数据,查询中使用的时间戳可能是,例如:

1473024092

然后如果我打印对象的时间戳:

puts object.time_records.pluck(:end_at).map(&:to_i)

我得到以下结果:

1472419292
1473024092
1473628892
1474233692

从这些可以看出,正确的结果应该是二。如果有人遇到类似的事情,我将不胜感激在正确方向上的指示。

就其价值而言,这发生在我为 gem 编写的规范中。我尝试了 in_time_zone.utc 的不同组合来解析和转换为时间戳,它们都提供相同的结果。即使转换为时间戳并直接返回时间,并且当 to_s 对两者相等时,测试是否相等也会导致错误。

我运行 irb中的一个例子:

2.3.0 :001 > now = Time.now
 => 2016-08-28 21:58:43 +0100 
2.3.0 :002 > timestamp = now.to_i
 => 1472417923 
2.3.0 :003 > parsed_timestamp = Time.at(timestamp)
 => 2016-08-28 21:58:43 +0100 
2.3.0 :004 > now.eql?(parsed_timestamp)
 => false 
2.3.0 :005 > now == parsed_timestamp
 => false 
2.3.0 :006 > now === parsed_timestamp
 => false 
2.3.0 :007 > now.class
 => Time 
2.3.0 :008 > parsed_timestamp.class
 => Time 

问题是分数次。 UNIX 时间戳精确到秒,因此在转换时 to_i,毫秒将被丢弃。

设置时间戳列的精度解决了这个问题:

class CreateTimeRecords < ActiveRecord::Migration
  def change
    create_table :time_records do |t|
      t.belongs_to :object, index: true, null: false

      t.datetime :start_at, null: false, index: true, precision: 0
      t.datetime :end_at, null: false, index: true, precision: 0

      t.timestamps null: false
    end
  end
end