2012-02-04 40 views
2

我正在Ruby 1.8.7上使用Rails 3.2.1和Jbuilder構建一個簡單的JSON API(1.9.x可能會幫助我,但我的託管提供者只有1.8.7)。Rails:JSON中的時間#to_f精度?

由於API消費者期望時間戳爲花車,我目前只是在做的時候一個簡單的to_f屬性:

json.updated_at record.updated_at.to_f #=> 1328242368.02242 

to_f招致精度損失。當客戶端請求自給定時間點以來已修改的記錄時,這會導致一些問題,因爲SQL查詢找到客戶端用於引用的相同記錄。即當試圖找到比上述示例更新的記錄時,SQL查詢(例如updated_at > Time.at(1328242368.02242))會返回相同的記錄,因爲updated_at的實際值比給定的時間戳更精確並且分數更大。其實record.updated_at.useC#=> 224250.022425秒。注意額外的小數。

如此最佳地,時間戳應該用1個額外的十進制進行JSON化,例如, 1328242368.022425,但我找不到一種方法來實現這一點。

updated_at.to_i #=> 1328242368  # seconds 
updated_at.useC#=> 22425   # microseconds 
updated_at.to_f #=> 1328242368.02242 # precision loss 

# Hacking around `to_f` doesn't help 
decimals = updated_at.usec/1000000.0 #=> 0.022425   # yay, decimals! 
updated_at.to_i + decimals    #=> 1328242368.02242 # dammit! 

我四處尋找設置默認浮點精度的方法,但我很難過。有任何想法嗎?

編輯:我應該補充說API消費者沒有運行JavaScript,所以浮點數可以有有更高的精度。它會打破JS兼容性(因此JSON規範)添加另一個數字(十進制或其他),因爲我相信JS浮動無法處理。因此,也許我需要一個完全不同的方法......

+0

你可以輸出時間爲字符串:'「#{updated_at.to_i}。#{updated_at.usec}」'? (好吧,根據需要在'usec'中加上前導零。) – millimoose 2012-02-04 01:12:28

+0

是的,沒有。如果我輸出字符串,我可以做很多類似的事情。但我需要輸出一個浮點數。如果我創建一個字符串,然後運行'to_f',我又回到了開始的位置:精確度損失。 – Flambino 2012-02-04 01:43:53

+0

你可以輸出BigDecimals而不是浮動嗎? JSON庫可能會將它們視爲數字。 – millimoose 2012-02-04 01:47:07

回答

1

在評論想來想去,最好的選擇似乎是的Monkeypatching ActiveSupport::JSON,使其處理BigDecimal S中的一樣Numeric S:

class BigDecimal 
    def as_json(options = nil) self end #:nodoc: 
    def encode_json(encoder) to_s end #:nodoc: 
end 

這將覆蓋Rails團隊的決定,以阻止序列化的BigDecimal在JSON反序列化器中被解析爲浮點數(並失去精度),而不支持十進制數。

+0

感謝您的幫助! – Flambino 2012-02-04 16:06:29