コーヒー飲みながら仕事したい

仕事で使う技術的なことの備忘録とか

ProtocolBuffers の C# 版の Timestamp について

前に ProtocolBuffers の C# 版の導入をやってみたのですが、

tassi-yuzukko.hatenablog.com

このときに proto ファイル import google/protobuf/timestamp.proto が読込めないせいで以下のようにクラスファイル変換のコンパイルに失敗していました。(このときはこの行自体をコメントアウトしてやり過ごしていましたが・・・)

> protoc -I=./ --csharp_out=./ ./addressbook.proto
google/protobuf/timestamp.proto: File not found.
addressbook.proto: Import "google/protobuf/timestamp.proto" was not found or had errors.
addressbook.proto:49:3: "google.protobuf.Timestamp" is not defined.

今回、ちょっとこの Timestamp という型を使ってみたくなったので、解決策を模索しました。

コンパイル時にパスを指定する

結論から言うと、以下の issue に書かれている通りです。

github.com

以下のように --proto_path オプションを使用してコンパイルします。 (ここでは $(NuGetPackageRoot)C:\Users\<ユーザー名>\.nuget\packages とします)

> protoc -I=./  --proto_path=`$(NuGetPackageRoot)/google.protobuf.tools/3.5.1/tools` --csharp_out=./ ./addressbook.proto

$(NuGetPackageRoot)/google.protobuf.tools/3.5.1/tools 内に、くだんの google/protobuf/timestamp.proto が存在するからです。
要は、 proto ファイルでの import は、コンパイル時に外部の定義先を指定することが必須のようですね。

Timestamp 型とはそもそも何モノ?

proto ファイル上では、以下のように定義されています。

message Timestamp {

  // Represents seconds of UTC time since Unix epoch
  // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
  // 9999-12-31T23:59:59Z inclusive.
  int64 seconds = 1;

  // Non-negative fractions of a second at nanosecond resolution. Negative
  // second values with fractions must still have non-negative nanos values
  // that count forward in time. Must be from 0 to 999,999,999
  // inclusive.
  int32 nanos = 2;
  • seconds フィールド
    • UNIX 時間」とか「 POSIX 時間」とか呼ばれている、1970年1月1日0時0分0秒(UTC)からの経過秒数
    • C言語でいう time() 関数の戻り値である time_t 型のそれ(参考)
  • nonos フィールド
    • XXXミリ秒YYYマイクロ秒ZZZナノ秒を、 XXX,YYY,ZZZ の9桁の整数として表すもの

つまり、なんてことない、結構プリミティブ型に近いような拡張型ですね。

C# での使い方( DateTime 型との互換)

C# での時間は DateTime 型が良く使われると思います。
この互換については、Timestamp クラスに静的メソッドとして以下が用意されているので、双方簡単に変換ができます。

  • Timestamp型を返す FromDateTime(DateTime dateTime)
  • DateTime型を返す ToDateTime()

詳細は、以下を参照してください。
Google.Protobuf.WellKnownTypes.Timestamp Class Reference  |  Protocol Buffers  |  Google Developers

ハマったこと・・・

ProtocolBuffers はバイナリにシリアライズする規格です。しかし、誤って文字列にシリアライズしてしまっていました。
たまたまかもしれませんが、プリミティブ型を文字列にシリアライズしても、正常にデシリアライズできていました。そこで Timestamp 型を文字列にシリアライズすると例外も出ずに失敗してしまい、「なぜ Timestamp 型を使用するとシリアライズできなくなるのか・・・」とハマってしまったので備忘録として残しておきます・・・。