diff --git a/crtime/monotonic.go b/crtime/monotonic.go index 5cd2c7f..7f088da 100644 --- a/crtime/monotonic.go +++ b/crtime/monotonic.go @@ -44,6 +44,18 @@ func (m Mono) Elapsed() time.Duration { return time.Duration(NowMono() - m) } +// ToUTC returns the UTC time corresponding to the monotonic time. +// +// The time is derived from the current wall clock, adjusted by the difference +// in the monotonic clock values. Note that if the wall clock has been changed +// since the Mono value was obtained, the result does not reflect the wall clock +// at that point in time. +func (m Mono) ToUTC() time.Time { + now := time.Now() + adjustment := time.Duration(m) - now.Sub(startTime) + return now.UTC().Add(adjustment) +} + // MonoFromTime converts a time.Time to a Mono value. If the time has a // monotonic component, it is used. func MonoFromTime(t time.Time) Mono { diff --git a/crtime/monotonic_test.go b/crtime/monotonic_test.go index 3d484ad..a182b34 100644 --- a/crtime/monotonic_test.go +++ b/crtime/monotonic_test.go @@ -30,4 +30,17 @@ func TestMono(t *testing.T) { d := NowMono() require.LE(t, b, c) require.LE(t, c, d) + + t.Run("ToUTC", func(t *testing.T) { + const d = 50 * time.Millisecond + const tolerance = time.Millisecond + + start := NowMono() + expected := time.Now().UnixNano() + time.Sleep(d) + actual := start.ToUTC().UnixNano() + if actual < expected-tolerance.Nanoseconds() || actual > expected+tolerance.Nanoseconds() { + t.Fatalf("actual - expected = %s", time.Duration(actual-expected)) + } + }) }