Ruby中DateTime的毫秒分辨率
我有一个像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
是否丢弃毫秒。
现在,有趣的是,在Rails和Postgres中, created_at
确实显示毫秒。 但其他时间戳字段(如上面的happen_at)则没有。 (也许Rails对created_at
使用NOW()
函数而不是传入DateTime)。
这导致了我的终极问题:
如何让ActiveRecord在时间戳字段上保留毫秒分辨率?
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可以将它们的手保持不动。
在上面的代码m.happened_at = '2012-01-01T00:00:00.32323'.to_datetime
更改为m.happened_at = '2012-01-01T00:00:00.32323'
解决了问题,但我不知道为什么。
当我在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找到了该url 。
我认识到这不是这个问题的答案,但我希望这个说明可以帮助有人挣扎。
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来启用此行为。