@@ -111,7 +111,7 @@ func newCronFieldN(values []int, n int) *cronField {
111111 return & cronField {values : values , n : n }
112112}
113113
114- // add increments each element of the underlying values array by the given delta.
114+ // add increments each element of the underlying values slice by the given delta.
115115func (cf * cronField ) add (delta int ) {
116116 for i := range cf .values {
117117 cf .values [i ] += delta
@@ -123,6 +123,12 @@ func (cf *cronField) String() string {
123123 return strings .Trim (strings .Join (strings .Fields (fmt .Sprint (cf .values )), "," ), "[]" )
124124}
125125
126+ // boundary represents inclusive range boundaries for cron field values.
127+ type boundary struct {
128+ lower int
129+ upper int
130+ }
131+
126132var (
127133 months = []string {"0" , "JAN" , "FEB" , "MAR" , "APR" , "MAY" , "JUN" , "JUL" , "AUG" , "SEP" , "OCT" , "NOV" , "DEC" }
128134 days = []string {"0" , "SUN" , "MON" , "TUE" , "WED" , "THU" , "FRI" , "SAT" }
@@ -181,98 +187,83 @@ func buildCronField(tokens []string) ([]*cronField, error) {
181187 var err error
182188 fields := make ([]* cronField , 7 )
183189 // second field
184- fields [0 ], err = parseField (tokens [0 ], 0 , 59 )
190+ fields [0 ], err = parseField (tokens [0 ], boundary { 0 , 59 }, nil )
185191 if err != nil {
186192 return nil , err
187193 }
188194 // minute field
189- fields [1 ], err = parseField (tokens [1 ], 0 , 59 )
195+ fields [1 ], err = parseField (tokens [1 ], boundary { 0 , 59 }, nil )
190196 if err != nil {
191197 return nil , err
192198 }
193199 // hour field
194- fields [2 ], err = parseField (tokens [2 ], 0 , 23 )
200+ fields [2 ], err = parseField (tokens [2 ], boundary { 0 , 23 }, nil )
195201 if err != nil {
196202 return nil , err
197203 }
198204 // day-of-month field
199- fields [3 ], err = parseDayOfMonthField (tokens [3 ], 1 , 31 )
205+ fields [3 ], err = parseDayOfMonthField (tokens [3 ], boundary { 1 , 31 }, nil )
200206 if err != nil {
201207 return nil , err
202208 }
203209 // month field
204- fields [4 ], err = parseField (tokens [4 ], 1 , 12 , months )
210+ fields [4 ], err = parseField (tokens [4 ], boundary { 1 , 12 } , months )
205211 if err != nil {
206212 return nil , err
207213 }
208214 // day-of-week field
209- fields [5 ], err = parseDayOfWeekField (tokens [5 ], 1 , 7 , days )
215+ fields [5 ], err = parseDayOfWeekField (tokens [5 ], boundary { 1 , 7 } , days )
210216 if err != nil {
211217 return nil , err
212218 }
213219 fields [5 ].add (- 1 )
214220 // year field
215- fields [6 ], err = parseField (tokens [6 ], 1970 , 1970 * 2 )
221+ fields [6 ], err = parseField (tokens [6 ], boundary { 1970 , 1970 * 2 }, nil )
216222 if err != nil {
217223 return nil , err
218224 }
219225
220226 return fields , nil
221227}
222228
223- func parseField (field string , min , max int , translate ... []string ) (* cronField , error ) {
224- var glossary []string
225- if len (translate ) > 0 {
226- glossary = translate [0 ]
227- }
229+ func parseField (field string , bound boundary , names []string ) (* cronField , error ) {
228230 // any value
229231 if field == "*" || field == "?" {
230232 return newCronField ([]int {}), nil
231233 }
232- // simple value
233- i , err := strconv .Atoi (field )
234- if err == nil {
235- if inScope (i , min , max ) {
236- return newCronField ([]int {i }), nil
237- }
238- return nil , newInvalidCronFieldError ("simple" , field )
239- }
240234 // list values
241235 if strings .ContainsRune (field , listRune ) {
242- return parseListField (field , min , max , glossary )
236+ return parseListField (field , bound , names )
243237 }
244238 // step values
245239 if strings .ContainsRune (field , stepRune ) {
246- return parseStepField (field , min , max , glossary )
240+ return parseStepField (field , bound , names )
247241 }
248242 // range values
249243 if strings .ContainsRune (field , rangeRune ) {
250- return parseRangeField (field , min , max , glossary )
244+ return parseRangeField (field , bound , names )
251245 }
252- // simple literal value
253- if glossary != nil {
254- intVal , err := translateLiteral (glossary , field )
255- if err != nil {
256- return nil , err
257- }
258- if inScope (intVal , min , max ) {
259- return newCronField ([]int {intVal }), nil
260- }
261- return nil , newInvalidCronFieldError ("literal" , field )
246+ // simple value
247+ numeric , err := normalize (field , names )
248+ if err != nil {
249+ return nil , err
250+ }
251+ if inScope (numeric , bound .lower , bound .upper ) {
252+ return newCronField ([]int {numeric }), nil
262253 }
263254
264- return nil , newCronParseError ( fmt . Sprintf ( "invalid field %s " , field ) )
255+ return nil , newInvalidCronFieldError ( "numeric " , field )
265256}
266257
267258var (
268259 cronLastMonthDayRegex = regexp .MustCompile (`^L(-[0-9]+)?$` )
269260 cronWeekdayRegex = regexp .MustCompile (`^[0-9]+W$` )
270261
271- cronLastWeekdayRegex = regexp .MustCompile (`^[0 -9]*L$` )
272- cronHashRegex = regexp .MustCompile (`^[0 -9]+#[0-9]+$` )
262+ cronLastWeekdayRegex = regexp .MustCompile (`^[a-zA-Z0 -9]*L$` )
263+ cronHashRegex = regexp .MustCompile (`^[a-zA-Z0 -9]+#[0-9]+$` )
273264)
274265
275- func parseDayOfMonthField (field string , min , max int , translate ... []string ) (* cronField , error ) {
266+ func parseDayOfMonthField (field string , bound boundary , names []string ) (* cronField , error ) {
276267 if strings .ContainsRune (field , lastRune ) && cronLastMonthDayRegex .MatchString (field ) {
277268 if field == string (lastRune ) {
278269 return newCronFieldN ([]int {}, cronLastDayOfMonthN ), nil
@@ -282,7 +273,7 @@ func parseDayOfMonthField(field string, min, max int, translate ...[]string) (*c
282273 return nil , newInvalidCronFieldError ("last" , field )
283274 }
284275 n , err := strconv .Atoi (values [1 ])
285- if err != nil || ! inScope (n , 1 , 30 ) {
276+ if err != nil || ! inScope (n , bound . lower , bound . upper ) {
286277 return nil , newInvalidCronFieldError ("last" , field )
287278 }
288279 return newCronFieldN ([]int {}, - n ), nil
@@ -299,24 +290,24 @@ func parseDayOfMonthField(field string, min, max int, translate ...[]string) (*c
299290 return nil , newInvalidCronFieldError ("weekday" , field )
300291 }
301292 dayOfMonth , err := strconv .Atoi (day )
302- if err != nil || ! inScope (dayOfMonth , min , max ) {
293+ if err != nil || ! inScope (dayOfMonth , bound . lower , bound . upper ) {
303294 return nil , newInvalidCronFieldError ("weekday" , field )
304295 }
305296 return newCronFieldN ([]int {dayOfMonth }, cronWeekdayN ), nil
306297 }
307298 }
308299
309- return parseField (field , min , max , translate ... )
300+ return parseField (field , bound , names )
310301}
311302
312- func parseDayOfWeekField (field string , min , max int , translate ... []string ) (* cronField , error ) {
303+ func parseDayOfWeekField (field string , bound boundary , names []string ) (* cronField , error ) {
313304 if strings .ContainsRune (field , lastRune ) && cronLastWeekdayRegex .MatchString (field ) {
314305 day := strings .TrimSuffix (field , string (lastRune ))
315306 if day == "" { // Saturday
316307 return newCronFieldN ([]int {7 }, - 1 ), nil
317308 }
318- dayOfWeek , err := strconv . Atoi (day )
319- if err != nil || ! inScope (dayOfWeek , min , max ) {
309+ dayOfWeek , err := normalize (day , names )
310+ if err != nil || ! inScope (dayOfWeek , bound . lower , bound . upper ) {
320311 return nil , newInvalidCronFieldError ("last" , field )
321312 }
322313 return newCronFieldN ([]int {dayOfWeek }, - 1 ), nil
@@ -327,8 +318,8 @@ func parseDayOfWeekField(field string, min, max int, translate ...[]string) (*cr
327318 if len (values ) != 2 {
328319 return nil , newInvalidCronFieldError ("hash" , field )
329320 }
330- dayOfWeek , err := strconv . Atoi (values [0 ])
331- if err != nil || ! inScope (dayOfWeek , min , max ) {
321+ dayOfWeek , err := normalize (values [0 ], names )
322+ if err != nil || ! inScope (dayOfWeek , bound . lower , bound . upper ) {
332323 return nil , newInvalidCronFieldError ("hash" , field )
333324 }
334325 n , err := strconv .Atoi (values [1 ])
@@ -338,26 +329,26 @@ func parseDayOfWeekField(field string, min, max int, translate ...[]string) (*cr
338329 return newCronFieldN ([]int {dayOfWeek }, n ), nil
339330 }
340331
341- return parseField (field , min , max , translate ... )
332+ return parseField (field , bound , names )
342333}
343334
344- func parseListField (field string , min , max int , glossary []string ) (* cronField , error ) {
335+ func parseListField (field string , bound boundary , names []string ) (* cronField , error ) {
345336 t := strings .Split (field , string (listRune ))
346337 values , stepValues := extractStepValues (t )
347338 values , rangeValues := extractRangeValues (values )
348- listValues , err := translateLiterals (glossary , values )
339+ listValues , err := translateLiterals (names , values )
349340 if err != nil {
350341 return nil , err
351342 }
352343 for _ , v := range stepValues {
353- stepField , err := parseStepField (v , min , max , glossary )
344+ stepField , err := parseStepField (v , bound , names )
354345 if err != nil {
355346 return nil , err
356347 }
357348 listValues = append (listValues , stepField .values ... )
358349 }
359350 for _ , v := range rangeValues {
360- rangeField , err := parseRangeField (v , min , max , glossary )
351+ rangeField , err := parseRangeField (v , bound , names )
361352 if err != nil {
362353 return nil , err
363354 }
@@ -368,20 +359,20 @@ func parseListField(field string, min, max int, glossary []string) (*cronField,
368359 return newCronField (listValues ), nil
369360}
370361
371- func parseRangeField (field string , min , max int , glossary []string ) (* cronField , error ) {
362+ func parseRangeField (field string , bound boundary , names []string ) (* cronField , error ) {
372363 t := strings .Split (field , string (rangeRune ))
373364 if len (t ) != 2 {
374365 return nil , newInvalidCronFieldError ("range" , field )
375366 }
376- from , err := normalize (t [0 ], glossary )
367+ from , err := normalize (t [0 ], names )
377368 if err != nil {
378369 return nil , err
379370 }
380- to , err := normalize (t [1 ], glossary )
371+ to , err := normalize (t [1 ], names )
381372 if err != nil {
382373 return nil , err
383374 }
384- if ! inScope (from , min , max ) || ! inScope (to , min , max ) {
375+ if ! inScope (from , bound . lower , bound . upper ) || ! inScope (to , bound . lower , bound . upper ) {
385376 return nil , newInvalidCronFieldError ("range" , field )
386377 }
387378 rangeValues , err := fillRangeValues (from , to )
@@ -392,45 +383,48 @@ func parseRangeField(field string, min, max int, glossary []string) (*cronField,
392383 return newCronField (rangeValues ), nil
393384}
394385
395- func parseStepField (field string , min , max int , glossary []string ) (* cronField , error ) {
386+ func parseStepField (field string , bound boundary , names []string ) (* cronField , error ) {
396387 t := strings .Split (field , string (stepRune ))
397388 if len (t ) != 2 {
398389 return nil , newInvalidCronFieldError ("step" , field )
399390 }
400- to := max
391+ to := bound . upper
401392 var (
402393 from int
403394 err error
404395 )
405396 switch {
406397 case t [0 ] == "*" :
407- from = min
398+ from = bound . lower
408399 case strings .ContainsRune (t [0 ], rangeRune ):
409400 trange := strings .Split (t [0 ], string (rangeRune ))
410401 if len (trange ) != 2 {
411402 return nil , newInvalidCronFieldError ("step" , field )
412403 }
413- from , err = normalize (trange [0 ], glossary )
404+ from , err = normalize (trange [0 ], names )
414405 if err != nil {
415406 return nil , err
416407 }
417- to , err = normalize (trange [1 ], glossary )
408+ to , err = normalize (trange [1 ], names )
418409 if err != nil {
419410 return nil , err
420411 }
421412 default :
422- from , err = normalize (t [0 ], glossary )
413+ from , err = normalize (t [0 ], names )
423414 if err != nil {
424415 return nil , err
425416 }
426417 }
418+
427419 step , err := strconv .Atoi (t [1 ])
428420 if err != nil {
429421 return nil , newInvalidCronFieldError ("step" , field )
430422 }
431- if ! inScope (from , min , max ) || ! inScope (step , 1 , max ) || ! inScope (to , min , max ) {
423+ if ! inScope (from , bound .lower , bound .upper ) || ! inScope (step , 1 , bound .upper ) ||
424+ ! inScope (to , bound .lower , bound .upper ) {
432425 return nil , newInvalidCronFieldError ("step" , field )
433426 }
427+
434428 stepValues , err := fillStepValues (from , step , to )
435429 if err != nil {
436430 return nil , err
0 commit comments