如何将Rails DateTime分辨率限制为微秒?

时间:2019-04-11 12:32:32

标签: ruby-on-rails datetime rspec

Rails支持DateTime的纳秒分辨率,但我注意到它取决于正在运行该应用程序的计算机。

在运行Mojave的MacBook上,Time.zone.now.strftime("%N")将始终输出以000结尾的9位数字(例如"122981000")。这意味着在Mac上,分辨率限制为微秒级。

但是,在Linux上,同一命令返回的数字具有完整的纳秒分辨率(例如"113578523")。

我在使用rspec时需要解决一些DateTime值的问题。
当我在Mac上进行开发时,测试可以顺利通过,但是当我们的CI(Travis)将运行相同的测试时,它将失败,如下所示:

       expected: 2019-04-09 19:14:27.214939637 -0300
            got: 2019-04-09 19:14:27.214939000 -0300

这里的问题是我们的数据库Postgres限制为微秒,就像Mac一样,它不会失败。我将DateTime存储在数据库中,然后将其读回并与内存中的内容进行比较。 DB舍入到微秒,因此比较失败。

是否可以迫使Rails以微秒的精度运行?
我的意图是不需要在每个测试中手动舍入或截断时间戳。

2 个答案:

答案 0 :(得分:1)

我以前曾经碰到过这个问题,在这里新创建的ActiveRecord对象在内存中(在Travis linux VM上)的创建/更新字段上具有9位精度,这与存储在Postgres中的不同(6位数字)。我遇到了同样的问题,这些测试在本地通过了(macOS),但在CI构建中失败了。

过去,我破解了规范以强制重新加载对象,该对象可以正常工作,但是正如您强调的那样,这很丑陋,因为您必须在每次测试时都这样做。最近,我再次遇到相同的问题,但是这次是使用我创建的服务对象创建的,无法“重新加载”,因此我着手尝试寻找更好的解决方案。

这不是完全理想的,因为它需要猴子为Time类打补丁,但是它可以工作!

subscription = pubsub.subscription(subscription_name)

修补时间单例的机制(因为我不能直接覆盖Time.now)基于此答案https://stackoverflow.com/a/60665577/989981

答案 1 :(得分:0)

按照 sgbett 答案建议的舍入有时会向上或向下舍入,因此在我的情况下它不匹配,在我的应用程序中,我们不需要比秒更精确的值,因此我们添加了一个匹配器以用于这种情况。

RSpec::Matchers.define :eql_time do |expected|
    match do |actual|
      # Rails has more precision than postgres so datetime objects do not match unless
      # matched up to seconds
      expected.to_i == actual.to_i
    end
end