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 <joetsai@digital-static.net>
pull/8481/head
Joe Tsai 1 year ago committed by GitHub
parent 075abd8ec1
commit e42be5a060
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -104,7 +104,8 @@ func (t Time) WallTime() time.Time {
// MarshalJSON formats t for JSON as if it were a time.Time. // MarshalJSON formats t for JSON as if it were a time.Time.
// We format Time this way for backwards-compatibility. // 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. // 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. // Even in the best of circumstances, it may vary by a few milliseconds.
func (t Time) MarshalJSON() ([]byte, error) { func (t Time) MarshalJSON() ([]byte, error) {
@ -113,7 +114,8 @@ func (t Time) MarshalJSON() ([]byte, error) {
} }
// UnmarshalJSON sets t according to data. // 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 { func (t *Time) UnmarshalJSON(data []byte) error {
var tt time.Time var tt time.Time
err := tt.UnmarshalJSON(data) err := tt.UnmarshalJSON(data)
@ -124,6 +126,6 @@ func (t *Time) UnmarshalJSON(data []byte) error {
*t = 0 *t = 0
return nil return nil
} }
*t = Now().Add(-time.Since(tt)) *t = baseMono.Add(tt.Sub(baseWall))
return nil return nil
} }

@ -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) { func BenchmarkMonoNow(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

Loading…
Cancel
Save