94 lines
1.8 KiB
Go
94 lines
1.8 KiB
Go
|
|
package timeutil
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
type Clock interface {
|
||
|
|
Now() time.Time
|
||
|
|
Sleep(ctxDone <-chan struct{}, d time.Duration) bool
|
||
|
|
}
|
||
|
|
|
||
|
|
type RealClock struct {
|
||
|
|
Loc *time.Location
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c RealClock) Now() time.Time {
|
||
|
|
now := time.Now()
|
||
|
|
if c.Loc != nil {
|
||
|
|
return now.In(c.Loc)
|
||
|
|
}
|
||
|
|
return now
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c RealClock) Sleep(ctxDone <-chan struct{}, d time.Duration) bool {
|
||
|
|
timer := time.NewTimer(d)
|
||
|
|
defer timer.Stop()
|
||
|
|
select {
|
||
|
|
case <-timer.C:
|
||
|
|
return true
|
||
|
|
case <-ctxDone:
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type TimeOfDay struct {
|
||
|
|
Duration time.Duration
|
||
|
|
}
|
||
|
|
|
||
|
|
func ParseTimeOfDay(raw string) (TimeOfDay, error) {
|
||
|
|
parsed, err := time.Parse("15:04:05", raw)
|
||
|
|
if err != nil {
|
||
|
|
return TimeOfDay{}, fmt.Errorf("parse time of day %q: %w", raw, err)
|
||
|
|
}
|
||
|
|
return TimeOfDay{
|
||
|
|
Duration: time.Duration(parsed.Hour())*time.Hour +
|
||
|
|
time.Duration(parsed.Minute())*time.Minute +
|
||
|
|
time.Duration(parsed.Second())*time.Second,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t *TimeOfDay) UnmarshalText(text []byte) error {
|
||
|
|
parsed, err := ParseTimeOfDay(string(text))
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
*t = parsed
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t TimeOfDay) String() string {
|
||
|
|
total := int64(t.Duration.Seconds())
|
||
|
|
h := total / 3600
|
||
|
|
m := (total % 3600) / 60
|
||
|
|
s := total % 60
|
||
|
|
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (t TimeOfDay) On(date time.Time, loc *time.Location) time.Time {
|
||
|
|
local := date.In(loc)
|
||
|
|
y, m, d := local.Date()
|
||
|
|
midnight := time.Date(y, m, d, 0, 0, 0, 0, loc)
|
||
|
|
return midnight.Add(t.Duration)
|
||
|
|
}
|
||
|
|
|
||
|
|
type Window struct {
|
||
|
|
Start TimeOfDay
|
||
|
|
End TimeOfDay
|
||
|
|
}
|
||
|
|
|
||
|
|
func (w Window) Contains(now time.Time, loc *time.Location) bool {
|
||
|
|
start := w.Start.On(now, loc)
|
||
|
|
end := w.End.On(now, loc)
|
||
|
|
return !now.Before(start) && now.Before(end)
|
||
|
|
}
|
||
|
|
|
||
|
|
func Drift(local, server time.Time) time.Duration {
|
||
|
|
d := local.Sub(server)
|
||
|
|
if d < 0 {
|
||
|
|
return -d
|
||
|
|
}
|
||
|
|
return d
|
||
|
|
}
|