2012-02-04 18 views
2

Ruby 1.8.7でRails 3.2.1とJbuilderを使用して単純なJSON APIを構築しています(1.9.xは役に立ちますが、ホスティングプロバイダは1.8.7しかありません)。 APIの消費者は、フロートとしてタイムスタンプを期待しRails:JSONの#to_f精度?

ので、私は現在、ちょうど時間に簡単なto_f属性をやっている:

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

しかしto_fは、精度の損失を被ります。これは、クライアントが特定の時点以降に変更されたレコードを要求するときに、SQLクエリーがクライアントが参照用に使用するのと同じレコードを検出するときに、いくつかの問題を引き起こします。私。上記の例よりも新しいレコードを検索しようとすると、実際の値がupdated_atであるため、SQLクエリ(例:updated_at > Time.at(1328242368.02242))は同じレコードを返します。

実際にはrecord.updated_at.useC#=> 22425または0.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浮動小数点はそれを扱うことができないので、それはJS互換性(したがってJSON仕様)を破ることになります。だから、おそらく私は

+0

時刻を文字列として出力できますか? "#{updated_at.to_i}。#{updated_at.usec}" '? (まあ、必要に応じて 'usec'に先行する0を加えてください。) – millimoose

+0

はい、いいえ。私は文字列を出力していた場合、私はそれのようなものをたくさんすることができました。しかし、私はフロートを出力する必要があります。そして、文字列を作成してから 'to_f'を実行すると、私が始めたところに戻ります:精度損失。 – Flambino

+0

浮動小数点数の代わりにBigDecimalを出力できますか? JSONライブラリは、それらを数値として扱うことができます。 – millimoose

答えて

1

コメントで審議した後...全く異なるアプローチを必要とする、最良のオプションは、それがNumericのと同じBigDecimal Sを処理するためにActiveSupport::JSONをmonkeypatchingているように見える:

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

このRailsチームがシリアル番号BigDecimalが10進数をサポートしないJSONデシリアライザで浮動小数点として解析されないように(そして精度が失われないように)するのを無効にします。

+0

助けてくれてありがとう! – Flambino