Rails / Ruby:TimeWithZone比较莫名其妙地失败了等价值

时间:2014-08-01 10:58:41

标签: ruby-on-rails ruby datetime comparison

我在当前项目中使用DateTime比较时遇到了可怕的时间(没有双关语),特别是比较了两个ActiveSupport :: TimeWithZone实例。问题是我的TimeWithZone实例都具有相同的值,但所有比较都表明它们是不同的。

在执行调试期间暂停(使用RubyMine),我可以看到以下信息:

timestamp = {ActiveSupport::TimeWithZone} 2014-08-01 10:33:36 UTC
started_at = {ActiveSupport::TimeWithZone} 2014-08-01 10:33:36 UTC

timestamp.inspect = "Fri, 01 Aug 2014 10:33:36 UTC +00:00"
started_at.inspect = "Fri, 01 Aug 2014 10:33:36 UTC +00:00"

然而,比较表明价值不相等:

timestamp <=> started_at = -1

我在搜索中找到的最接近的答案(Comparison between two ActiveSupport::TimeWithZone objects fails)表明这里存在相同的问题,我尝试了适用的解决方案但没有成功(尝试过db:test:准备我不会运行Spring )。

此外,即使我尝试转换为显式类型,它们在比较时仍然不等同。

TO_TIME:

timestamp.to_time = {Time} 2014-08-01 03:33:36 -0700
started_at.to_time = {Time} 2014-08-01 03:33:36 -0700

timestamp.to_time <=> started_at.to_time = -1

to_datetime:

timestamp.to_datetime = {Time} 2014-08-01 03:33:36 -0700
started_at.to_datetime = {Time} 2014-08-01 03:33:36 -0700

timestamp.to_datetime <=> started_at.to_datetime = -1    

&#34;解决方案&#34;到目前为止,我发现使用to_i转换这两个值,然后进行比较,但是对于我希望进行比较的所有代码来说,这是非常尴尬的(而且,似乎它应该是不必要的) :

timestamp.to_i = 1406889216
started_at.to_i = 1406889216

timestamp.to_i <=> started_at.to_i = 0

非常感谢任何建议!

2 个答案:

答案 0 :(得分:1)

解决

如上面的Jon Skeet所示,由于时间上隐藏的毫秒差异,比较失败了:

timestamp.strftime('%Y-%m-%d %H:%M:%S.%L') = "2014-08-02 10:23:17.000"
started_at.strftime('%Y-%m-%d %H:%M:%S.%L') = "2014-08-02 10:23:17.679"

这一发现让我走上了一条奇怪的道路,最终发现最终导致问题的原因。这是测试期间和使用MySQL作为我的数据库时出现此问题的组合。

这些问题仅在测试中显示,因为在测试中出现这种问题,我正在针对包含上述字段的几个相关模型运行一些测试。在测试期间,必须将一个模型的实例保存到数据库中 - 包含timestamp值的模型。然而,另一个模型正在执行处理,因此自我引用在测试代码中创建的自身实例。

这导致了第二个罪魁祸首,这就是我使用MySQL作为数据库的事实,当存储datetime值时,存储毫秒信息(不像,比方说,PostgreSQL)。

这总是意味着,从MySQL数据库中检索到ActiveRecord后读取的timestamp变量实际上被舍入并削减了毫秒数据,而started_at变量是在测试期间简单地保留在内存中,因此原始毫秒仍然存在。

我自己的(低于标准)解决方案是在我的测试中基本上强制两个模型(而不仅仅是一个)从数据库中检索自己。

<强> TLDR;如果可能的话,尽可能使用PostgreSQL!

答案 1 :(得分:0)

如果您将Ruby中生成的时间与数据库中加载的时间进行比较,就会出现这种情况。

例如:

time = Time.zone.now
Record.create!(mark: time)
record = Record.last

在这种情况下,record.mark == time将失败,因为Ruby将时间缩短到纳秒,而不同的数据库具有不同的精度。

如果使用的是Postgres DateTime类型,则将以毫秒为单位。

当您在record.mark.sec == time.msec-record.mark.nsec != time.nsec

中进行检查时,您会看到