Ruby Time.zone.now并考虑数据库精度

时间:2019-03-05 02:34:08

标签: ruby-on-rails ruby postgresql

我在Rails 3测试套件中进行了一个测试,该测试产生了一些断言,用于比较在本地计算机上传递但在我们的CI管道中失败的时间戳。该测试将时间戳存储在Postgres数据库的timestamp字段中,精度为6,并将存储的值与原始时间戳进行比较,非常类似于以下示例:

tmp_time = Time.zone.now
u = User.find(1)
u.updated_at = tmp_time
u.save!
u.reload

assert_equal u.updated_at.to_i, tmp_time.to_i # passes...
assert_equal u.updated_at, tmp_time # fails...
assert_equal u.updated_at.to_f, tmp_time.to_f # fails...

我认为问题在于Ruby的时间表示比存储的值具有更高的精度。

除了比较中不那么精确之外,补偿由于精度导致的值的微小差异的最佳方法是什么?我们已经考虑了覆盖Time.zone.now,但是认为这将导致下游问题。

谢谢。

2 个答案:

答案 0 :(得分:2)

我建议对此类测试使用[Timecop] [1]。我还要补充一点,手动触摸时间戳是不好的做法,因此我什至不确定自己要测试什么。听起来您不信任时间戳。但是,如果必须的话,在这里:

Timecop.freeze
tmp_time = Time.zone.now
u = User.find(1)
u.updated_at = tmp_time
u.save!
u.reload

#your tests should pass now

但是,如果不这样做,[请参见核心导轨上的此公开问题] [2]。 如果这与您的问题有关,Timecop应该防止它发生。但是,如果没有,那么恭喜您,您已经找到了一个核心的导轨边缘盒。

答案 1 :(得分:1)

问题可能与数据库的精度无关,而是从定义tmp_time到保存之间只有一小段时间。

您会看到.to_f的时间表示立即改变:

irb(main):011:0> 2.times.map { Time.now.to_f }
=> [1551755487.5737898, 1551755487.573792]

使用.to_i时,通常看不到这种差异,因为它会四舍五入到最接近的秒数。

您可以使用Timecop(如另一个答案所述)来解决此问题:

irb(main):013:0> Timecop.freeze { 2.times.map { Time.now.to_f } }
=> [1551755580.12368, 1551755580.12368]