RubyDateTimeオブジェクトをjsonにシリアル化したいのですが。残念ながら、私のアプローチは対称的ではありません。
require 'date'
date = DateTime.now
DateTime.parse(date.to_s) == date
=> false
任意のstrftime/parse stringの組み合わせを使用できますが、より良いアプローチが必要だと思います。
RubyDateTimeオブジェクトをjsonにシリアル化したいのですが。残念ながら、私のアプローチは対称的ではありません。
require 'date'
date = DateTime.now
DateTime.parse(date.to_s) == date
=> false
任意のstrftime/parse stringの組み合わせを使用できますが、より良いアプローチが必要だと思います。
残念ながら、受け入れられた答えは良い解決策ではありません。いつものように、marshal / unmarshalは最後の手段としてのみ使用する必要があるツールですが、この場合、アプリが破損する可能性があります。
OPは、日付をJSONにシリアル化することに特に言及しました。RFC 7159による:
JSONテキストは、UTF-8、UTF-16、またはUTF-32でエンコードする必要があります。デフォルトのエンコードはUTF-8であり、UTF-8でエンコードされたJSONテキストは、最大数の実装で正常に読み取られるという意味で相互運用可能です。他のエンコーディング(UTF-16やUTF-32など)のテキストを正常に読み取ることができない多くの実装があります。
それでは、元帥から得られるものを見てみましょう。
marsh = Marshal.dump(DateTime.now)
# => "\x04\bU:\rDateTime[\vi\x00i\x03\xE0\x7F%i\x02s\xC9i\x04\xF8z\xF1\"i\xFE\xB0\xB9f\f2299161"
puts marsh.encoding
# -> #<Encoding:ASCII-8BIT>
marsh.encode(Encoding::UTF_8)
# -> Encoding::UndefinedConversionError: "\xE0" from ASCII-8BIT to UTF-8
人間が読めない値を返すことに加えて、Marshal.dump
UTF-8に変換できない値を提供します。つまり、それを(有効な)JSONに入れる唯一の方法は、base-64などの何らかの方法でエンコードすることです。
それをする必要はありません。日付と時刻を表す非常に相互運用可能な方法がすでにあります:ISO8601。なぜそれがJSON(および一般的に)に最適なのかについては説明しませんが、ここでの答えはそれを十分にカバーしています:「正しい」JSON日付形式とは何ですか?。
Ruby 1.9.3以降、DateTimeクラスには、ISO8601の日付をそれぞれ解析およびフォーマットするためのiso8601
クラスメソッドとインスタンスメソッドがあります。3
後者は、小数秒(ミリ秒など)の精度を指定するための引数を取ります。
require "date"
date = DateTime.now
str = date.iso8601(9)
puts str
# -> 2016-06-28T09:35:58.311527000-05:00
DateTime.iso8601(str) == date
# => true
58.311
より小さな精度を指定すると、egがに等しくないため、これが機能しない可能性があることに注意してください58.311527
。DateTimeのドキュメントには次のように書かれているので、(ナノ秒)の精度は9
私には安全に思えます。
小数の精度は最大でナノ秒と想定されています。
ただし、より高い精度を使用する可能性のあるシステムと相互運用している場合は、それを考慮に入れる必要があります。
最後に、RubyのJSONライブラリを自動的にシリアル化に使用する場合は、 andメソッドiso8601
をオーバーライドします。as_json
to_json
unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
require 'json'
end
require 'date'
class DateTime
def as_json(*)
iso8601(9)
end
def to_json(*args)
as_json.to_json(*args)
end
end
puts DateTime.now.to_json
# -> "2016-06-28T09:35:58.311527000-05:00"
to_s
メソッドとto_json
メソッド(提供)はどちらもrequire 'json'
、DateTimeオブジェクトによって格納されているナノ秒を無視しますdate
。古き良きMarshal
配達:
require 'date'
date = DateTime.now
m_date = Marshal.dump(date)
p Marshal.load(m_date) == date # => true
これは、date
が1秒未満の値であり、#to_s
メソッドがISO時間形式を秒単位で返すため、比較が成功しないためです。
1.9.3p327 :021 > date = DateTime.now
=> #<DateTime: 2012-11-28T07:32:40+09:00 ((2456259j,81160s,283019000n),+32400s,2299161j)>
1.9.3p327 :022 > DateTime.parse(date.to_s)
=> #<DateTime: 2012-11-28T07:32:40+09:00 ((2456259j,81160s,0n),+32400s,2299161j)>
ですから実際には違います
1秒未満を気にしない場合は、比較が成功するかどうかを忘れてください。
DateTime#marshal_load
または、とDateTime#marshal_dump
を1.9.3に使用できます。(私は今までこれを知りませんでした。。)
それは次のように機能します:
date1 = DateTime.now
dump = date1.marshal_dump
date2 = DateTime.new.marshal_load(dump)
date1 == date2 # => true