Go で timestamppb を使いこなそう:テストで大活躍するタイムスタンプ活用ガイド
1. はじめに:timestamppbは何に使える?
timestamppb
は Protocol Buffers の timestamp.proto
から生成された Go の型で、Unix エポックからの秒数とナノ秒数を保持します。 google.protobuf.Timestamp
を Go で扱いやすい形にしたものなので、UTC の絶対時刻として時刻情報を記録できます。たとえばテストコードで擬似的な時刻を作成したり、取得した時刻を比較するときに役立ちます。
- Go の
time.Time
を protobuf のメッセージとして表現したい場合に利用します。 -
CheckValid
メソッドを使えば範囲外の値を事前に検証できます。 - JSON としてシリアライズするときに、RFC 3339 形式の文字列になるため、外部とのやり取りもしやすいです。
たとえば、時刻を固定してテストをしたい場面では timestamppb.New(...)
で一度作成しておくと、protobuf のフィールドとして直接比較できたり、 AsTime
で元の time.Time
に戻したりと便利に使えます。
2. Goのtime.Timeとの相互変換
timestamppb
を使う上で最も重要なのは、 time.Time
との相互変換です。テストで扱いたい時刻を New
でメッセージ化して、結果を検証するときは AsTime
で戻す形が一般的です。
1. Go の時刻を timestamppb へ変換
import "google.golang.org/protobuf/types/known/timestamppb"
ts := timestamppb.New(time.Now())
// これで *timestamppb.Timestamp を取得
Now
を使えば「現在時刻」のメッセージを手軽に作れます。固定時刻を生成したい場合は time.Date(...)
から New
を呼ぶと便利です。
2. timestamppb を Go の時刻へ変換
t := ts.AsTime()
// time.Time として普通に扱える
AsTime
は内部的に Unix epoch からの経過秒数とナノ秒数を Go の time.Time
に変換します。これによって標準の time パッケージにあるフォーマットや計算関数がフルに使えます。
3. 異常な範囲や値をチェック
if err := ts.CheckValid(); err != nil {
// 範囲外の場合はエラー
}
Protocol Buffers の timestamp.proto
は 0001-01-01T00:00:00Z から 9999-12-31T23:59:59Z までの範囲を想定しているため、もし範囲外ならエラーが返ります。テストで想定外の時刻を混入させないための安全策として活用できます。
これらの関数を押さえておくだけで、テスト時に protobuf の Timestamp と Go の時刻を行き来できるようになります。たとえば「固定の 2025 年の時刻を生成してイベントを検証したい」といったシナリオでも、コードがすっきり書けるようになります。
3. テストでの活用例
timestamppb
はテストコードで時刻を扱う際に特に便利です。ここでは、固定した時刻を用いて検証する場合と、現在の時刻を仮想的にセットする場合の二つの例を簡単に紹介します。
1. 固定時刻の検証
func TestEventTime(t *testing.T) {
// 2025-12-31T23:59:59Z を作成
fixed := time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC)
ts := timestamppb.New(fixed)
// AsTime で戻して比較
got := ts.AsTime()
if !got.Equal(fixed) {
t.Errorf("got %v, want %v", got, fixed)
}
}
- このように、時刻を固定してメッセージを作成すると、テスト時のブレが減ります。
-
AsTime()
で取り出してEqual
すると、微妙なずれやオフセットの問題に悩まされにくいです。
2. 現在時刻の仮想設定
package main
import (
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestNowTimestamp(t *testing.T) {
// 現在時刻
tsNow := timestamppb.Now()
// 直後に Now() を呼び出した time.Now() と大差ないかを確認
now := time.Now()
if now.Sub(tsNow.AsTime()) > 2*time.Second {
t.Errorf("timestamp is too far from now")
}
}
-
timestamppb.Now()
は現時点の時刻を*Timestamp
で返します。 - 少しの誤差はあり得るので、サンプルでは 2 秒以内であれば許容する形です。
まとめ:
-
New
やNow
で簡単にメッセージを作成でき、AsTime
で元のtime.Time
に取り出せます。 - 固定時刻を用いるテストでは、日付や時刻を確実に把握できるため、再現性が高くなります。
- 環境依存が激しい部分が減るので、テストが安定しやすいです。
4. まとめ:まずはNewとAsTimeを押さえよう
timestamppb.Timestamp
は Protocol Buffers 形式で時刻を表したいときに便利な型です。Go のテストコードで使う場合、特に New
と AsTime
を押さえておくだけで扱いやすくなります。次のようなポイントが重要です。
-
New(...) でタイムスタンプを生成する
- 固定時刻を決めてテストしたいときは
timestamppb.New(myFixedTime)
で手軽に作れます。 - 現在時刻を示すメッセージを作りたい場合は
timestamppb.Now()
が便利です。
- 固定時刻を決めてテストしたいときは
-
AsTime() で Go の時刻へ戻す
- メッセージ化された時刻を再び
time.Time
として取り扱うためにはAsTime()
を呼び出します。 - 取得後は標準の time パッケージが提供する演算やフォーマットを利用できるため、テスト時にさまざまな検証を行いやすいです。
- メッセージ化された時刻を再び
-
CheckValid() で範囲外を検出する
- 0001 年から 9999 年までの時刻のみ有効という制限があるため、想定外の値が使われた場合はエラーを返します。
- テストで異常な入力を模擬するときにも役立ちます。
短いテストコードで時刻を固定し、 AsTime()
で比較するだけでも効率的な検証が可能です。 timestamppb
は time.Time
との相互変換が簡単なので、まずは New
と AsTime
を軸に試してみることをおすすめします。
Discussion