golang / protobuf

Go support for Google's protocol buffers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

timestamppb.AsTime is incorrectly implemented for nil/zero values

pequalsnp opened this issue · comments

What version of protobuf and what language are you using?

I am using google.golang.org/protobuf v1.33.0 but the issue is present in the current code

What did you do?

If you call AsTime on a nil timestamppb.Timestamp you would expect to get a zero value time.Time back, but what you get is a time.Time for unix epoch zero, which is not the same thing. See https://go.dev/src/time/time.go?s=8279:8361#L361. Golang time.Time zero value is year 1, not 1970.

https://github.com/protocolbuffers/protobuf-go/blob/master/types/known/timestamppb/timestamp.pb.go#L199

What did you expect to see?

nil.AsTime().IsZero() == true

What did you see instead?

nil.AsTime().IsZero() == false

package main

import (
	"fmt"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	var a *timestamppb.Timestamp
	fmt.Println(a.AsTime().IsZero())
}

That will print false

Anything else we should know about your project / environment?

The disagreement here happens because of a difference of what the zero value of a timestamppb is, vs the zero value of Golang’s time.Time value.

Note:

func main() {
	var a *timestamppb.Timestamp
	var t time.Time
	fmt.Println(a.AsTime())
	fmt.Println(t)
}

runs and prints:

1970-01-01 00:00:00 +0000 UTC
0001-01-01 00:00:00 +0000 UTC

Note that the zero value of a timestamppb is the Unix epoch, but the zero value time.Time is Jan 1st, 1 AD.

Summary: This is intended behavior, even if it is unexpected.

P.S.: Huh, after reading the whole report, you seem to understand all of this already?

This is an unfortunate an edge-case in bridging the Go type system and the protobuf type system and there are no good answers other than being as consistent as possible across the module about how this is handled.

Of note, protoreflect.Message.IsValid says:

// IsValid reports whether the message is valid.
//
// An invalid message is an empty, read-only value.
//
// An invalid message often corresponds to a nil pointer of the concrete
// message type, but the details are implementation dependent.
// Validity is not part of the protobuf data model, and may not
// be preserved in marshaling or other operations.

That is, a nil pointer is semantically treated as an "empty, read-only value".
In the protobuf data model, reading the "seconds" and "nanos" fields of an empty Timestamp message would report 0, which results in a date of 1970-01-01.