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.
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.