From e42be5a06094d0be3cc8f90ad0f90518734c8123 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Wed, 28 Jun 2023 15:16:52 -0700 Subject: [PATCH] tstime/mono: fix Time.Unmarshal (#8480) Calling both mono.Now() and time.Now() is slow and leads to unnecessary precision errors. Instead, directly compute mono.Time relative to baseMono and baseWall. This is the opposite calculation as mono.Time.WallTime. Updates tailscale/corp#8427 Signed-off-by: Joe Tsai --- tstime/mono/mono.go | 8 +++++--- tstime/mono/mono_test.go | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/tstime/mono/mono.go b/tstime/mono/mono.go index 6b02bb41a..4042c53a5 100644 --- a/tstime/mono/mono.go +++ b/tstime/mono/mono.go @@ -104,7 +104,8 @@ func (t Time) WallTime() time.Time { // MarshalJSON formats t for JSON as if it were a time.Time. // We format Time this way for backwards-compatibility. -// This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged. +// Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged +// across different invocations of the Go process. This is best-effort only. // Since t is a monotonic time, it can vary from the actual wall clock by arbitrary amounts. // Even in the best of circumstances, it may vary by a few milliseconds. func (t Time) MarshalJSON() ([]byte, error) { @@ -113,7 +114,8 @@ func (t Time) MarshalJSON() ([]byte, error) { } // UnmarshalJSON sets t according to data. -// This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged. +// Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged +// across different invocations of the Go process. This is best-effort only. func (t *Time) UnmarshalJSON(data []byte) error { var tt time.Time err := tt.UnmarshalJSON(data) @@ -124,6 +126,6 @@ func (t *Time) UnmarshalJSON(data []byte) error { *t = 0 return nil } - *t = Now().Add(-time.Since(tt)) + *t = baseMono.Add(tt.Sub(baseWall)) return nil } diff --git a/tstime/mono/mono_test.go b/tstime/mono/mono_test.go index a78f49791..f425abe9a 100644 --- a/tstime/mono/mono_test.go +++ b/tstime/mono/mono_test.go @@ -33,6 +33,21 @@ func TestUnmarshalZero(t *testing.T) { } } +func TestJSONRoundtrip(t *testing.T) { + want := Now() + b, err := want.MarshalJSON() + if err != nil { + t.Errorf("MarshalJSON error: %v", err) + } + var got Time + if err := got.UnmarshalJSON(b); err != nil { + t.Errorf("UnmarshalJSON error: %v", err) + } + if got != want { + t.Errorf("got %v, want %v", got, want) + } +} + func BenchmarkMonoNow(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ {