tailscale/tstime/tstime.go
Joe Tsai 7e6c5a2db4
tstime: rely on stdlib parse functionality (#7482)
The time.Parse function has been optimized to the point
where it is faster than our custom implementation.
See upstream changes in:

* https://go.dev/cl/429862
* https://go.dev/cl/425197
* https://go.dev/cl/425116

Performance:

	BenchmarkGoParse3339/Z     38.75 ns/op    0 B/op    0 allocs/op
	BenchmarkGoParse3339/TZ    54.02 ns/op    0 B/op    0 allocs/op
	BenchmarkParse3339/Z       40.17 ns/op    0 B/op    0 allocs/op
	BenchmarkParse3339/TZ      87.06 ns/op    0 B/op    0 allocs/op

We can see that the stdlib implementation is now faster.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2023-03-06 18:05:51 -08:00

62 lines
1.4 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package tstime defines Tailscale-specific time utilities.
package tstime
import (
"context"
"strconv"
"strings"
"time"
)
// Parse3339 is a wrapper around time.Parse(time.RFC3339, s).
func Parse3339(s string) (time.Time, error) {
return time.Parse(time.RFC3339, s)
}
// Parse3339B is Parse3339 but for byte slices.
func Parse3339B(b []byte) (time.Time, error) {
var t time.Time
if err := t.UnmarshalText(b); err != nil {
return Parse3339(string(b)) // reproduce same error message
}
return t, nil
}
// ParseDuration is more expressive than [time.ParseDuration],
// also accepting 'd' (days) and 'w' (weeks) literals.
func ParseDuration(s string) (time.Duration, error) {
for {
end := strings.IndexAny(s, "dw")
if end < 0 {
break
}
start := end - (len(s[:end]) - len(strings.TrimRight(s[:end], "0123456789")))
n, err := strconv.Atoi(s[start:end])
if err != nil {
return 0, err
}
hours := 24
if s[end] == 'w' {
hours *= 7
}
s = s[:start] + s[end+1:] + strconv.Itoa(n*hours) + "h"
}
return time.ParseDuration(s)
}
// Sleep is like [time.Sleep] but returns early upon context cancelation.
// It reports whether the full sleep duration was achieved.
func Sleep(ctx context.Context, d time.Duration) bool {
timer := time.NewTimer(d)
defer timer.Stop()
select {
case <-ctx.Done():
return false
case <-timer.C:
return true
}
}