1 package cron
2
3 import "time"
4
5
6
7 type SpecSchedule struct {
8 Second, Minute, Hour, Dom, Month, Dow uint64
9
10
11 Location *time.Location
12 }
13
14
15 type bounds struct {
16 min, max uint
17 names map[string]uint
18 }
19
20
21 var (
22 seconds = bounds{0, 59, nil}
23 minutes = bounds{0, 59, nil}
24 hours = bounds{0, 23, nil}
25 dom = bounds{1, 31, nil}
26 months = bounds{1, 12, map[string]uint{
27 "jan": 1,
28 "feb": 2,
29 "mar": 3,
30 "apr": 4,
31 "may": 5,
32 "jun": 6,
33 "jul": 7,
34 "aug": 8,
35 "sep": 9,
36 "oct": 10,
37 "nov": 11,
38 "dec": 12,
39 }}
40 dow = bounds{0, 6, map[string]uint{
41 "sun": 0,
42 "mon": 1,
43 "tue": 2,
44 "wed": 3,
45 "thu": 4,
46 "fri": 5,
47 "sat": 6,
48 }}
49 )
50
51 const (
52
53 starBit = 1 << 63
54 )
55
56
57
58 func (s *SpecSchedule) Next(t time.Time) time.Time {
59
60
61
62
63
64
65
66
67
68
69
70
71
72 origLocation := t.Location()
73 loc := s.Location
74 if loc == time.Local {
75 loc = t.Location()
76 }
77 if s.Location != time.Local {
78 t = t.In(s.Location)
79 }
80
81
82 t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
83
84
85 added := false
86
87
88 yearLimit := t.Year() + 5
89
90 WRAP:
91 if t.Year() > yearLimit {
92 return time.Time{}
93 }
94
95
96
97 for 1<<uint(t.Month())&s.Month == 0 {
98
99 if !added {
100 added = true
101
102 t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
103 }
104 t = t.AddDate(0, 1, 0)
105
106
107 if t.Month() == time.January {
108 goto WRAP
109 }
110 }
111
112
113
114
115
116
117 for !dayMatches(s, t) {
118 if !added {
119 added = true
120 t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
121 }
122 t = t.AddDate(0, 0, 1)
123
124
125 if t.Hour() != 0 {
126 if t.Hour() > 12 {
127 t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
128 } else {
129 t = t.Add(time.Duration(-t.Hour()) * time.Hour)
130 }
131 }
132
133 if t.Day() == 1 {
134 goto WRAP
135 }
136 }
137
138 for 1<<uint(t.Hour())&s.Hour == 0 {
139 if !added {
140 added = true
141 t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
142 }
143 t = t.Add(1 * time.Hour)
144
145 if t.Hour() == 0 {
146 goto WRAP
147 }
148 }
149
150 for 1<<uint(t.Minute())&s.Minute == 0 {
151 if !added {
152 added = true
153 t = t.Truncate(time.Minute)
154 }
155 t = t.Add(1 * time.Minute)
156
157 if t.Minute() == 0 {
158 goto WRAP
159 }
160 }
161
162 for 1<<uint(t.Second())&s.Second == 0 {
163 if !added {
164 added = true
165 t = t.Truncate(time.Second)
166 }
167 t = t.Add(1 * time.Second)
168
169 if t.Second() == 0 {
170 goto WRAP
171 }
172 }
173
174 return t.In(origLocation)
175 }
176
177
178
179 func dayMatches(s *SpecSchedule, t time.Time) bool {
180 var (
181 domMatch bool = 1<<uint(t.Day())&s.Dom > 0
182 dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
183 )
184 if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
185 return domMatch && dowMatch
186 }
187 return domMatch || dowMatch
188 }
189
View as plain text