Ruby中DateTime的毫秒分辨率

时间:2013-01-01 01:53:11

标签: ruby postgresql datetime activerecord rails-postgresql

我有一个像2012-01-01T01:02:03.456这样的字符串,我使用ActiveRecord存储在Postgres数据库TIMESTAMP中。

不幸的是,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支持微秒级分辨率。如何才能相应地保存我的时间戳?我需要至少毫秒的分辨率。

(PS是的,我可以在postgres中以毫秒整数列进行破解;这种方式会破坏ActiveRecord的整个目的。)

更新
非常有用的回答显示Ruby的DateTime 砍掉毫秒;使用#to_f显示它。但是,做:

m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
m.save!
m.reload
m.happened_at.to_f

是否会丢弃毫秒。

现在,有趣的是created_at确实在Rails和Postgres中显示毫秒。但是其他时间戳字段(如上面的happened_at)则没有。 (也许Rails对NOW()使用created_at函数而不是传入DateTime。

这导致了我的终极问题:
如何让ActiveRecord在时间戳字段上保留毫秒分辨率?

4 个答案:

答案 0 :(得分:18)

ActiveRecord应该保留数据库的完整精度,你只是没有正确地看它。使用strftime%N格式查看小数秒。例如,psql说明了这一点:

=> select created_at from models where id = 1;
         created_at         
----------------------------
 2012-02-07 07:36:20.949641
(1 row)

和ActiveRecord说:

> Model.find(1).created_at.strftime('%Y-%m-%d %H:%M:%S.%N')
 => "2012-02-07 07:36:20.949641000" 

所以一切都在那里,你只需要知道如何看待它。

另请注意,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" 

查看ActiveRecord源中的connection_adapters/column.rb并查看string_to_time方法的作用。你的字符串将沿fallback_string_to_time路径向下移动,并保留小数秒,尽可能接近。在其他地方可能会发生一些奇怪的事情,鉴于我在Rails源代码中看到的奇怪事情,尤其是数据库方面,我不会感到惊讶。我试着用手将字符串转换为对象,这样ActiveRecord就可以将它们从手中移开。

答案 1 :(得分:9)

将上面代码中的m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime更改为m.happened_at = '2012-01-01T00:00:00.32323'解决了问题,但我不明白为什么。

答案 2 :(得分:3)

当我在OS X(Mavericks)上使用RVM提供的二进制Ruby 2.0.0-p247时,我结束了这里,当从Postgres检索时间时,这导致四舍五入到整数秒。我自己重建Ruby(rvm reinstall 2.0.0 --disable-binary)为我解决了这个问题。

请参阅我https://github.com/wayneeseguin/rvm/issues/2189找到的https://github.com/rails/rails/issues/12422

我认识到这不是这个问题的答案,但我希望这个说明可能有助于有人挣扎。

答案 3 :(得分:1)

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

那就是说,我怀疑ActiveRecord在保存数据时错误地隐藏了这些信息;请记住,它与数据库无关,因此需要保证在所有数据库目标中都能正常工作的方法。虽然Postgres在时间戳中假设微秒信息,但MySQL没有,所以我怀疑AR选择最低公分母。如果没有进入AR的内脏,我无法确定。您可能需要Postgres特定的monkeypatch来启用此行为。