2023-01-27 21:37:20 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2020-04-05 21:47:10 +00:00
|
|
|
|
|
|
|
// Package tstime defines Tailscale-specific time utilities.
|
|
|
|
package tstime
|
|
|
|
|
|
|
|
import (
|
2023-03-07 01:40:38 +00:00
|
|
|
"context"
|
2022-09-28 23:37:31 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2020-04-05 21:47:10 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2023-03-07 02:05:51 +00:00
|
|
|
// Parse3339 is a wrapper around time.Parse(time.RFC3339, s).
|
2020-11-18 19:59:02 +00:00
|
|
|
func Parse3339(s string) (time.Time, error) {
|
2023-03-07 02:05:51 +00:00
|
|
|
return time.Parse(time.RFC3339, s)
|
2020-11-18 19:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse3339B is Parse3339 but for byte slices.
|
|
|
|
func Parse3339B(b []byte) (time.Time, error) {
|
2023-03-07 02:05:51 +00:00
|
|
|
var t time.Time
|
|
|
|
if err := t.UnmarshalText(b); err != nil {
|
|
|
|
return Parse3339(string(b)) // reproduce same error message
|
|
|
|
}
|
|
|
|
return t, nil
|
2020-11-18 19:59:02 +00:00
|
|
|
}
|
2022-09-28 23:37:31 +00:00
|
|
|
|
2023-03-07 02:05:51 +00:00
|
|
|
// ParseDuration is more expressive than [time.ParseDuration],
|
2022-11-17 05:01:09 +00:00
|
|
|
// also accepting 'd' (days) and 'w' (weeks) literals.
|
2022-09-28 23:37:31 +00:00
|
|
|
func ParseDuration(s string) (time.Duration, error) {
|
|
|
|
for {
|
|
|
|
end := strings.IndexAny(s, "dw")
|
|
|
|
if end < 0 {
|
|
|
|
break
|
|
|
|
}
|
2022-11-17 05:01:09 +00:00
|
|
|
start := end - (len(s[:end]) - len(strings.TrimRight(s[:end], "0123456789")))
|
2022-09-28 23:37:31 +00:00
|
|
|
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)
|
|
|
|
}
|
2023-03-07 01:40:38 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|