How to use `omitempty` with protobuf Timestamp in Golang

Issue

I have an optional field on my struct called ExpireTime. It has a time.Time type and a json:"expire_time,omitempty" tag to not send it, when it is empty. This part works perfectly fine.

When I want to use the same field via GRPC, I run into an issue when converting it to the protobuf timestamp format.

type Timestamp struct {

    // 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.
    Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
    // 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.
    Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
    // contains filtered or unexported fields
}
ExpireTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"`

The issue is that an empty time.Time{} object will be converted to a negative seconds value corresponding to 0001-01-01T00:00:00Z. Having the omitEmpty flag will not be applied in this case as the value is not zeroed out. What could I do to omit this field, when it is actually empty? Thanks!

Solution

As you say time.Time{} converts to 0001-01-01T00:00:00Z; this is working as intended. Note that you also need to be careful converting in the opposite direction (a zero TimeStamp will become 1970-01-01T00:00:00Z).

However generally the Timestamp will be part of a message, for example:

message MyMessage{
   google.protobuf.Timestamp  comm_time = 1;
}

Running this through protoc will result in something like:

type MyMessage struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    CommTime *timestamppb.Timestamp `protobuf:"bytes, 1,opt,name=comm_time,json=commTime,proto3" json:"comm_time,omitempty"`
}

This means you should be able to achieve the result you are looking for with CommTime=nil; e.g.

sourceTime := time.Time{}  // Whatever time you want to encode
var commTime *timestamp.Timestamp
if !sourceTime.IsZero() {
        commTime = timestamppb.New(sourceTime)
}

msg := MyMessage{
   CommTime: commTime,
}

Answered By – Brits

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.