Skip to content

Commit e42be5a

Browse files
authored
tstime/mono: fix Time.Unmarshal (tailscale#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 <[email protected]>
1 parent 075abd8 commit e42be5a

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

tstime/mono/mono.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ func (t Time) WallTime() time.Time {
104104

105105
// MarshalJSON formats t for JSON as if it were a time.Time.
106106
// We format Time this way for backwards-compatibility.
107-
// This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged.
107+
// Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged
108+
// across different invocations of the Go process. This is best-effort only.
108109
// Since t is a monotonic time, it can vary from the actual wall clock by arbitrary amounts.
109110
// Even in the best of circumstances, it may vary by a few milliseconds.
110111
func (t Time) MarshalJSON() ([]byte, error) {
@@ -113,7 +114,8 @@ func (t Time) MarshalJSON() ([]byte, error) {
113114
}
114115

115116
// UnmarshalJSON sets t according to data.
116-
// This is best-effort only. Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged.
117+
// Time does not survive a MarshalJSON/UnmarshalJSON round trip unchanged
118+
// across different invocations of the Go process. This is best-effort only.
117119
func (t *Time) UnmarshalJSON(data []byte) error {
118120
var tt time.Time
119121
err := tt.UnmarshalJSON(data)
@@ -124,6 +126,6 @@ func (t *Time) UnmarshalJSON(data []byte) error {
124126
*t = 0
125127
return nil
126128
}
127-
*t = Now().Add(-time.Since(tt))
129+
*t = baseMono.Add(tt.Sub(baseWall))
128130
return nil
129131
}

tstime/mono/mono_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ func TestUnmarshalZero(t *testing.T) {
3333
}
3434
}
3535

36+
func TestJSONRoundtrip(t *testing.T) {
37+
want := Now()
38+
b, err := want.MarshalJSON()
39+
if err != nil {
40+
t.Errorf("MarshalJSON error: %v", err)
41+
}
42+
var got Time
43+
if err := got.UnmarshalJSON(b); err != nil {
44+
t.Errorf("UnmarshalJSON error: %v", err)
45+
}
46+
if got != want {
47+
t.Errorf("got %v, want %v", got, want)
48+
}
49+
}
50+
3651
func BenchmarkMonoNow(b *testing.B) {
3752
b.ReportAllocs()
3853
for i := 0; i < b.N; i++ {

0 commit comments

Comments
 (0)