-
Notifications
You must be signed in to change notification settings - Fork 19
/
cutoff.go
120 lines (102 loc) · 2.55 KB
/
cutoff.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2020 The Moov Authors
// Use of this source code is governed by an Apache License
// license that can be found in the LICENSE file.
package schedule
import (
"errors"
"fmt"
"sort"
"time"
"github.com/moov-io/base"
"github.com/moov-io/base/stime"
"github.com/rickar/cal/v2"
"github.com/robfig/cron/v3"
)
// CutoffTimes is a time.Ticker which fires on banking days to trigger processing
// events (like end-of-day, or same-day ACH).
type CutoffTimes struct {
C chan *Day
sched *cron.Cron
firstCutoff string
timeService stime.TimeService
}
type Day struct {
Time time.Time
Holiday *cal.Holiday
IsBankingDay bool
IsHoliday bool
IsWeekend bool
// FirstWindow is true when Time is the first cutoff time of the day
FirstWindow bool
}
func ForCutoffTimes(timeService stime.TimeService, tz string, timestamps []string) (*CutoffTimes, error) {
ct := &CutoffTimes{
C: make(chan *Day),
sched: cron.New(),
timeService: timeService,
}
if len(timestamps) > 0 {
sort.Strings(timestamps)
ct.firstCutoff = timestamps[0]
}
if err := ct.registerCutoffs(tz, timestamps); err != nil {
return nil, err
}
ct.sched.Start()
return ct, nil
}
func (ct *CutoffTimes) Stop() {
if ct == nil {
return
}
if ct.C != nil {
close(ct.C)
}
if ct.sched != nil {
ct.sched.Stop()
}
}
func (ct *CutoffTimes) maybeTick(location *time.Location) {
now := base.NewTime(ct.timeService.Now().In(location))
if !now.IsWeekend() {
ct.C <- &Day{
Time: now.Time,
Holiday: now.GetHoliday(),
IsBankingDay: now.IsBankingDay(),
IsHoliday: now.IsHoliday(),
IsWeekend: now.IsWeekend(),
FirstWindow: now.Format("15:04") == ct.firstCutoff,
}
}
}
func (ct *CutoffTimes) registerCutoffs(tz string, timestamps []string) error {
if len(timestamps) == 0 {
return errors.New("missing cutoff times")
}
for i := range timestamps {
if err := ct.register(tz, timestamps[i]); err != nil {
return fmt.Errorf("timestamp=%s error=%v", timestamps[i], err)
}
}
return nil
}
func (ct *CutoffTimes) register(tz string, timestamp string) error {
when, err := time.Parse("15:04", timestamp)
if err != nil {
return fmt.Errorf("failed to parse '%s' error=%v", timestamp, err)
}
var zone string
var location *time.Location
if tz != "" {
zone = fmt.Sprintf("CRON_TZ=%s", tz)
l, _ := time.LoadLocation(tz)
location = l
} else {
location = time.UTC
}
schedule := fmt.Sprintf(`%s %d %d * * *`, zone, when.Minute(), when.Hour())
ct.sched.AddFunc(schedule, func() {
ct.maybeTick(location)
})
return nil
}