Ruby 中 DateTime 的毫秒分辨率
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14107354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Millisecond resolution of DateTime in Ruby
提问by SRobertJames
I have a string like 2012-01-01T01:02:03.456that I am storing in a Postgres database TIMESTAMP using ActiveRecord.
我有一个类似的字符串2012-01-01T01:02:03.456,我使用 ActiveRecord 存储在 Postgres 数据库 TIMESTAMP 中。
Unfortunately, Ruby seems to chop off the milliseconds:
不幸的是,Ruby 似乎截断了毫秒:
ruby-1.9.3-rc1 :078 > '2012-12-31T01:01:01.232323+3'.to_datetime
=> Mon, 31 Dec 2012 01:01:01 +0300
Postgrs supports microsecond resolution. How can I get my timestamp to be saved accordingly? I need at least millisecond resolution.
Postgrs 支持微秒分辨率。如何相应地保存我的时间戳?我需要至少毫秒的分辨率。
(PS Yes I could hack in a milliseconds integer column in postgres; that kind of defeats the whole purpose of ActiveRecord.)
(PS 是的,我可以在 postgres 中输入毫秒整数列;这种方式违背了 ActiveRecord 的全部目的。)
UPDATE:
The very helpful responses showed that Ruby's DateTimeis notchopping off milliseconds; using #to_fshows it. But, doing:
更新:
非常有帮助的答复显示,Ruby的DateTime是没有斩去毫秒; 使用#to_f显示它。但是,做:
m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
m.save!
m.reload
m.happened_at.to_f
Does drop the milliseconds.
确实下降了毫秒。
Now, the interesting thing is that created_atdoes show milliseconds, both in Rails and Postgres. But other timestamps fields (like happened_atabove) don't. (Perhaps Rails uses a NOW()function for created_atas opposed to passing in a DateTime).
现在,有趣的是created_at在 Rails 和 Postgres 中都显示毫秒。但其他时间戳字段(happened_at如上)没有。(也许 Rails 使用一个NOW()函数 forcreated_at而不是传入 DateTime)。
Which leads to my ultimate question:
How can I get ActiveRecord to preserve millisecond resolution on timestamp fields?
这引出了我的终极问题:
如何让 ActiveRecord 保留时间戳字段的毫秒分辨率?
采纳答案by SRobertJames
Changing m.happened_at = '2012-01-01T00:00:00.32323'.to_datetimein the code above to m.happened_at = '2012-01-01T00:00:00.32323'solves the problem, though I have no idea why.
更改m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime上面的代码以m.happened_at = '2012-01-01T00:00:00.32323'解决问题,但我不知道为什么。
回答by mu is too short
ActiveRecord should preserve the full precision from the database, you're just not looking at it properly. Use strftimeand the %Nformat to see the fractional seconds. For example, psqlsays this:
ActiveRecord 应该保留数据库的完整精度,只是您没有正确查看它。使用strftime和%N格式查看小数秒。例如,这样psql说:
=> select created_at from models where id = 1;
created_at
----------------------------
2012-02-07 07:36:20.949641
(1 row)
and ActiveRecord says this:
和 ActiveRecord 说:
> Model.find(1).created_at.strftime('%Y-%m-%d %H:%M:%S.%N')
=> "2012-02-07 07:36:20.949641000"
So everything is there, you just need to know how to see it.
所以一切都在那里,你只需要知道如何看待它。
Also note that ActiveRecord will probably give you ActiveSupport::TimeWithZoneobjects rather than DateTimeobjects but DateTimepreserves everything too:
另请注意,ActiveRecord 可能会为您提供ActiveSupport::TimeWithZone对象而不是DateTime对象,但DateTime也会保留所有内容:
> '2012-12-31T01:01:01.232323+3'.to_datetime.strftime('%Y-%m-%d %H:%M:%S.%N')
=> "2012-12-31 01:01:01.232323000"
Have a look at connection_adapters/column.rbin the ActiveRecord source and check what the string_to_timemethod does. Your string would go down the fallback_string_to_timepath and that preserves fractional seconds as near as I can tell. Something strange could be going on elsewhere, I wouldn't be surprised given the strange things I've seen in the Rails source, especially the database side of things. I'd try converting the strings to objects by hand so that ActiveRecord will keeps its hands off them.
查看connection_adapters/column.rbActiveRecord 源代码并检查该string_to_time方法的作用。你的字符串会沿着fallback_string_to_time路径走,并且尽可能地保留小数秒。其他地方可能会发生一些奇怪的事情,考虑到我在 Rails 源代码中看到的奇怪事情,尤其是数据库方面的事情,我不会感到惊讶。我会尝试手动将字符串转换为对象,以便 ActiveRecord 将手放在它们之外。
回答by Joseph Lord
I ended up here when I was suffering from using the RVM provided binary Ruby 2.0.0-p247 on OS X (Mavericks) which was causing rounding to whole values of seconds when retrieving times from Postgres. Rebuilding Ruby myself (rvm reinstall 2.0.0 --disable-binary) solved the issue for me.
当我在 OS X (Mavericks) 上使用 RVM 提供的二进制 Ruby 2.0.0-p247 时,我最终到了这里,这导致从 Postgres 检索时间时四舍五入到整个秒值。自己重建 Ruby ( rvm reinstall 2.0.0 --disable-binary) 为我解决了这个问题。
See https://github.com/wayneeseguin/rvm/issues/2189which I found via https://github.com/rails/rails/issues/12422.
见https://github.com/wayneeseguin/rvm/issues/2189我通过发现https://github.com/rails/rails/issues/12422。
I recognise that this is not THE answer to this issue but I hope this note might help someone struggling with it.
我认识到这不是这个问题的答案,但我希望这篇笔记可以帮助那些正在努力解决这个问题的人。
回答by Chris Heald
to_datetimedoes not destroy millisecond resolution of data - it's simply hidden because DateTime#to_sdoesn't display it.
to_datetime不会破坏数据的毫秒分辨率 - 它只是隐藏,因为DateTime#to_s不显示它。
[1] pry(main)> '2012-12-31T01:01:01.232323+3'.to_datetime
=> Mon, 31 Dec 2012 01:01:01 +0300
[2] pry(main)> '2012-12-31T01:01:01.232323+3'.to_datetime.to_f
=> 1356904861.232323
That said, I suspect that ActiveRecord is mistakenly hiding that information when persisting the data; remember that it is database-agnostic, so it takes approaches that are guaranteed to work across all of its database targets. While Postgres supposed microsecond information in timestamps, MySQL does not, so I suspect AR selects for the lowest common denominator. I couldn't be sure without getting into the guts of AR. You may need a Postgres-specific monkeypatch to enable this behavior.
也就是说,我怀疑 ActiveRecord 在保存数据时错误地隐藏了该信息;请记住,它与数据库无关,因此它采用的方法保证适用于其所有数据库目标。虽然 Postgres 假设时间戳中有微秒信息,但 MySQL 没有,所以我怀疑 AR 选择了最小的公分母。如果不深入了解 AR,我就无法确定。您可能需要特定于 Postgres 的monkeypatch 才能启用此行为。

