Files
overnight-trading-bot/internal/timeutil/clock.go
T

94 lines
1.8 KiB
Go
Raw Normal View History

2026-06-07 21:01:40 +00:00
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
}