protobuf typeに日付型のcom.google.protobuf.Timestampをつかってみた


protobufのtypeに日付型をつかいたいケースがあったので com.google.protobuf.Timestampをつかってみた。

google/protobufを覗いてみると公式に載っているtypeの他にも使えそうなものがあるので定義に迷ったときは一読をおすすめする。

ここからは順にprotoの定義からgrpc-server, grpc-clientにおけるcom.google.protobuf.Timestampの使い方をまとめていく。※コードはすべてkotlinで、grpc-javaをつかっている

protoの定義方法

protoファイルの抜粋になるが importを追加してtypeを定義するだけで使える。

1
2
3
4
5
6
7
8
9
import "google/protobuf/timestamp.proto";

message TaskOutbound {
  uint32 task_id = 1;
  string title = 2;
  google.protobuf.Timestamp finishedAt = 3;
  google.protobuf.Timestamp createdAt = 4;
  google.protobuf.Timestamp updatedAt = 5;
}

finishedAt, createdAt, updatedAtに google.protobuf.Timestampを定義していることが分かる。

protoの全体はこちらから確認できるので参考にしてほしい。

grpc-serverではどうするか

LocalDateTimeからgoogle.protobuf.Timestampの型へ変換しているコードは次のようになる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private fun getOutbound(entity: Task): TaskOutbound {
    val builder = TaskOutbound.newBuilder()
            .setTaskId(entity.id!!)
            .setTitle(entity.title)
            .setCreatedAt(getTimestamp(entity.createdAt))
            .setUpdatedAt(getTimestamp(entity.updatedAt))

    if (entity.finishedAt != null)
        builder.setFinishedAt(getTimestamp(entity.finishedAt))

    return builder.build()
}

private fun getTimestamp(date: LocalDateTime): Timestamp.Builder {
    return Timestamp.newBuilder().setSeconds(date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
}

grpc-serverはレスポンスを返すことになるが、google.protobuf.Timestampを適応したfinishedAt, createdAt, updatedAtはgoogle.protobuf.TimestampのBuilderクラスのインスタンスオブジェクトをセットする必要がある。

grpc-clinetではどうするか

clientはserverから受け取ったgoogle.protobuf.Timestampの値をyyyy-MM-dd’T’HH:mm:ss’Z’な文字列のString型でエンドポイントへ返却したい。コードは次のようになる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
data class TaskModel(val id: Long, val title: String, val finishedAt: String?, val createdAt: String, val updatedAt: String) {
    constructor(entity: TaskOutbound) : this(
            id = entity.taskId.toLong(),
            title = entity.title,
            finishedAt = entity.finishedAt.let {
                if (it != null)
                    Instant.ofEpochMilli(it.seconds).atZone(ZoneId.systemDefault()).toLocalDateTime().convert(DateUtil.Format.FULL_UTC)
                else null
            },
            createdAt = Instant.ofEpochMilli(entity.createdAt.seconds).atZone(ZoneId.systemDefault()).toLocalDateTime().convert(DateUtil.Format.FULL_UTC),
            updatedAt = Instant.ofEpochMilli(entity.updatedAt.seconds).atZone(ZoneId.systemDefault()).toLocalDateTime().convert(DateUtil.Format.FULL_UTC)
    )
}

google.protobuf.TimestampにはgetSeconds()のメソッドが用意されていてUNIXタイムが取得できる。この値からエンドポイントに必要な型へ変換することができる。

まとめ

コード

コードは次のレポジトリにコミットしてあるので良かったら確認してみてください。