Islands in the byte stream

Technical notes by a software engineer

msgpack-ruby に timestamp型を実装した

timestamp型というのは2017年8月にMessagePackに追加された型です。

frsyuki.hatenablog.com

しかし、おそらくMessagePack開発元がtimestamp型を使っていないためか、長らく msgpack-ruby には実装されていませんでしたし、msgpack-java の実装もpull-request はあるもののマージされていません。

ところでKibela Web APIはMessagePackをサポートしています。

github.com

ここでGraphQLのDateTIme型をMessagePackのtimestamp型で表現したくなったので msgpack-ruby にもtimestamp型を実装しました。

MessagePack timestamp type by gfx · Pull Request #168 · msgpack/msgpack-ruby · GitHub

こちらは msgpack-ruby v1.3.0 で利用可能になっています。

なお v1.3.0 の時点ではデフォルトではtimestamp型を扱えるようにはなっていません。というのも、Ruby on RailsではRuby built-in classのTimeではなく ActiveSupport::TimeWithZone を使うのが通例であり、デフォルトで MessagePack timestamp <-> Ruby Time のマッピングがあるとかえって誤解を招くのではないかと考えたからです。

AS::TWZを MessagePack timestamp typeにマッピングするにはこんな感じでいけます。

# for Rails v5.2 + msgpack-ruby v1.3

MessagePack::DefaultFactory.register_type(
  MessagePack::Timestamp::TYPE,
  ActiveSupport::TimeWithZone,
  packer: -> (time) do
    MessagePack::Timestamp.to_msgpack_ext(time.tv_sec, time.tv_nsec)
  end,
  unpacker: -> (payload) do
    tv = MessagePack::Timestamp.from_msgpack_ext(payload)

    # FIXME: Rails v6 supports Time.zone.at(sec, nsec, :nanoseconds)
    ::Time.at(tv.sec, tv.nsec, :nanosecond).utc.in_time_zone(Time.zone)
  end,
)