首页 > 解决方案 > 如何将 Rails DateTime 分辨率限制为微秒?

问题描述

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

在我运行 Mojave 的 MacBook 上,Time.zone.now.strftime("%N")总是会输出一个以 , 结尾的 9 位数字000(例如"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 使用微秒精度运行?
我的意图是不需要在每次测试中手动舍入或截断时间戳。

标签: ruby-on-railsdatetimerspec

解决方案


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

过去,我曾破解规范以强制重新加载对象,但正如您所强调的那样,这很丑陋,因为您必须为每次测试都这样做。最近我再次遇到了同样的问题,但这次我创建的服务对象无法“重新加载”,所以我开始尝试寻找更好的解决方案。

它并不完全理想,因为它需要猴子修补 Time 类,但是它可以工作!

module ForceTimePrecision
  def now
    super.round(6)
  end
end

Time.singleton_class.send(:prepend, ForceTimePrecision)

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


推荐阅读