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

Gab*_*tah 10 ruby comparison datetime ruby-on-rails

我在当前项目中使用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"
Run Code Online (Sandbox Code Playgroud)

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

timestamp <=> started_at = -1
Run Code Online (Sandbox Code Playgroud)

我在搜索中找到的最接近的答案(两个ActiveSupport :: TimeWithZone对象之间的比较失败)表明同样的问题,我尝试了适用的解决方案但没有成功(尝试db:test:prepare我不运行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
Run Code Online (Sandbox Code Playgroud)

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    
Run Code Online (Sandbox Code Playgroud)

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

timestamp.to_i = 1406889216
started_at.to_i = 1406889216

timestamp.to_i <=> started_at.to_i = 0
Run Code Online (Sandbox Code Playgroud)

任何建议将非常感谢!

Gab*_*tah 4

解决了

正如 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"
Run Code Online (Sandbox Code Playgroud)

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

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

这导致了第二个罪魁祸首,那就是我使用 MySQL 作为数据库,在存储datetime值时,时不存储毫秒信息(与 PostgreSQL 不同)。

毫无疑问,这意味着timestamp从 MySQL 数据库检索 ActiveRecord 后读取的变量实际上被四舍五入并去除了毫秒数据,而该started_at变量在测试期间只是保留在内存中,因此原始毫秒仍然是展示。

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

太长了;如果可能的话,请使用 PostgreSQL!