30

我有一个类似的字符串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 的全部目的。)

更新:
非常有用的响应表明 RubyDateTime没有减少毫秒;使用#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

4 回答 4

23

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" 

查看connection_adapters/column.rbActiveRecord 源代码并检查该string_to_time方法的作用。你的字符串会沿着这fallback_string_to_time条路走,并且尽可能地保留小数秒。其他地方可能会发生一些奇怪的事情,鉴于我在 Rails 源代码中看到的奇怪事情,尤其是数据库方面的事情,我不会感到惊讶。我会尝试手动将字符串转换为对象,这样 ActiveRecord 就不会把手放在它们身上。

于 2013-01-01T02:14:17.870 回答
9

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

于 2013-01-01T10:34:03.307 回答
3

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

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

我认识到这不是解决这个问题的答案,但我希望这篇笔记可以帮助那些在这个问题上苦苦挣扎的人。

于 2013-11-07T09:35:33.823 回答
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 的猴子补丁来启用此行为。

于 2013-01-01T02:03:59.150 回答