-
Notifications
You must be signed in to change notification settings - Fork 0
/
timeslot.go
175 lines (147 loc) · 4.73 KB
/
timeslot.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package usts
import (
"fmt"
"time"
cron "github.com/robfig/cron/v3"
)
var crparser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
// TimeSlot this object give us ability to create periodic time intervals from some logic expresion
type TimeSlot struct {
ID string
//-------------------------------------
Scf string //Start Cron Format
Ecf string //End Cron Format
scs cron.Schedule //Start Cron Schedule
ecs cron.Schedule //End Cron Schedule
}
// NewTimeSlot create a periodic time slot from cron based expression for start and end time slot
func NewTimeSlot(id, startexpr, endexpr string) (*TimeSlot, error) {
var err error
ret := &TimeSlot{}
ret.ID = id
ret.Scf = startexpr
ret.Ecf = endexpr
//START
ret.scs, err = crparser.Parse(ret.Scf)
if err != nil {
return nil, fmt.Errorf("ERROR on parse Start cron expression : %s", err)
}
//END
ret.ecs, err = crparser.Parse(ret.Ecf)
if err != nil {
return nil, fmt.Errorf("ERROR on parse End cron expression : %s", err)
}
return ret, nil
}
// RefreshCronTZ reload cron based expressions adding timezone info
func (ts *TimeSlot) RefreshCronTZ(tz string) error {
var err error
cronTplStart := ts.Scf
cronTplEnd := ts.Ecf
if len(tz) > 0 {
cronTplStart = "CRON_TZ=" + tz + " " + ts.Scf
cronTplEnd = "CRON_TZ=" + tz + " " + ts.Ecf
}
//START
ts.scs, err = crparser.Parse(cronTplStart)
if err != nil {
return fmt.Errorf("ERROR on parse Start cron expression : %s", err)
}
//END
ts.ecs, err = crparser.Parse(cronTplEnd)
if err != nil {
return fmt.Errorf("ERROR on parse End cron expression : %s", err)
}
return nil
}
// GetPreviousCronTime get Previous scheduled time from cron Scheduler
// this method will check if exist any previous sched value beggining in the past
// with different intervals 1ms,1s,1m,1h,6h,24h,7d,30d,365d before
func GetPreviousCronTime(sch cron.Schedule, start time.Time) time.Time {
intervals := []time.Duration{
1 * time.Millisecond, //1ms
1 * time.Second, //1s
1 * time.Minute, //1m
1 * time.Hour, //1h
6 * time.Hour, //6h
24 * time.Hour, //24h
7 * 24 * time.Hour, //7d
30 * 24 * time.Hour, //1moth
365 * 24 * time.Hour, //1year
}
for _, i := range intervals {
ilog.Debugf(">>>>USTS DEBUG [GetPreviousCronTime]: GetPrevious from interval %s", i)
before := start.Add(-i)
count := 0
for {
ilog.Debugf(">>>>USTS DEBUG [GetPreviousCronTime]:Test before %s/%d", before, count)
iter := sch.Next(before)
if iter.After(start) && count > 0 {
return before
}
before = iter
count++
}
}
return time.Time{}
}
// GetClosestPreviousEvent helps to determine the default/first value
func (ts *TimeSlot) GetClosestPreviousEvent(start time.Time) (time.Time, bool) {
tStart := GetPreviousCronTime(ts.scs, start)
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetClosestPreviousEvent]: from Start Expression [%s] got on time [%s]", ts.Scf, tStart)
tEnd := GetPreviousCronTime(ts.ecs, start)
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetClosestPreviousEvent]: from End Expression [%s] got on time [%s]", ts.Ecf, tEnd)
switch {
case tStart.After(tEnd):
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetClosestPreviousEvent]: Start after end => (start expr wins)TRUE")
return tStart, true
case tStart.Before(tEnd):
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetClosestPreviousEvent]: Start before end => (end expr wins) FALSE")
return tEnd, false
case tStart.Equal(tEnd):
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetClosestPreviousEvent]: Start == end => Set to TRUE")
return tStart, true
}
ilog.Warnf(">>>>USTS WARN [TimeSlot:GetClosestPreviousEvent]: can not selected previous event")
return start, true
}
// GetTimeEvents get all initial(as true)/final(as false) slot events
func (ts *TimeSlot) GetTimeEvents(start, end time.Time, tz string) (*USTimeSerie, error) {
err := ts.RefreshCronTZ(tz)
if err != nil {
return nil, err
}
tok := NewUSTimeSerie(0)
tnok := NewUSTimeSerie(0)
//1 second below Needed to get start time if match
// with scheduled events with Next() function
tnext := start.Add(-1 * time.Second)
for {
t := ts.scs.Next(tnext)
if t.After(end) {
break
}
tok.Add(t, true)
tnext = t
}
tnext = start.Add(-1 * time.Second)
for {
t := ts.ecs.Next(tnext)
if t.After(end) {
break
}
tnok.Add(t, false)
tnext = t
}
tret, err := tok.Combine(tnok)
//if start has a value
if v, ok := tret.GetExact(start); ok {
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetTimeEvents] Set default value for t = %s in %t", start, v)
tret.SetDefault(v)
} else {
tinit, state := ts.GetClosestPreviousEvent(start)
ilog.Debugf(">>>>USTS DEBUG [TimeSlot:GetTimeEvents] Set default value for t = %s in %t", tinit, state)
tret.SetDefault(state)
}
return tret, err
}