slog: correctly represent the zero Time in a Value
Storing the zero time.Time in a Value and then retrieving it
gave a non-zero time. The reason is that UnixNanos is called
to store a time.Time in a Value, and its result is undefined
for the zero time (or any time before the year 1678 or after 2262,
but I don't think we need to worry about those).
One solution, implemented in this CL, is to use a nil *time.Location
to represent the zero time. the time.Time.Location method never
returns nil, so a Value with a nil *time.Location in its `any` field
cannot be mistaken for any other Value, time.Time or otherwise.
Change-Id: Icba66f2ec16e21ed574c3336e1996ed759ab1c17
Reviewed-on: https://go-review.googlesource.com/c/exp/+/463817
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/slog/value.go b/slog/value.go
index 4e928b9..ffe5907 100644
--- a/slog/value.go
+++ b/slog/value.go
@@ -97,6 +97,13 @@
// TimeValue returns a Value for a time.Time.
// It discards the monotonic portion.
func TimeValue(v time.Time) Value {
+ if v.IsZero() {
+ // UnixNano on the zero time is undefined, so represent the zero time
+ // with a nil *time.Location instead. time.Time.Location method never
+ // returns nil, so a Value with any == timeLocation(nil) cannot be
+ // mistaken for any other Value, time.Time or otherwise.
+ return Value{any: timeLocation(nil)}
+ }
return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
}
@@ -271,7 +278,11 @@
}
func (v Value) time() time.Time {
- return time.Unix(0, int64(v.num)).In(v.any.(timeLocation))
+ loc := v.any.(timeLocation)
+ if loc == nil {
+ return time.Time{}
+ }
+ return time.Unix(0, int64(v.num)).In(loc)
}
// LogValuer returns v's value as a LogValuer. It panics
diff --git a/slog/value_test.go b/slog/value_test.go
index 31403ca..67f8730 100644
--- a/slog/value_test.go
+++ b/slog/value_test.go
@@ -190,6 +190,14 @@
}
}
+func TestZeroTime(t *testing.T) {
+ z := time.Time{}
+ got := TimeValue(z).Time()
+ if !got.IsZero() {
+ t.Errorf("got %s (%#[1]v), not zero time (%#v)", got, z)
+ }
+}
+
type replace struct {
v Value
}