@@ -71,6 +71,29 @@ func Daily(raw string) (*Schedule, error) {
71
71
return parse (raw )
72
72
}
73
73
74
+ // TimeRange parses a Schedule from a cron specification interpreted as a continuous time range.
75
+ //
76
+ // For example, the expression "* 9-18 * * 1-5" represents a continuous time span
77
+ // from 09:00:00 to 18:59:59, Monday through Friday.
78
+ //
79
+ // The specification consists of space-delimited fields in the following order:
80
+ // - (Optional) Timezone, e.g., CRON_TZ=US/Central
81
+ // - Minutes: must be "*" to represent the full range within each hour
82
+ // - Hour of day: e.g., 9-18 (required)
83
+ // - Day of month: e.g., * or 1-15 (required)
84
+ // - Month: e.g., * or 1-6 (required)
85
+ // - Day of week: e.g., * or 1-5 (required)
86
+ //
87
+ // Unlike standard cron, this function interprets the input as a continuous active period
88
+ // rather than discrete scheduled times.
89
+ func TimeRange (raw string ) (* Schedule , error ) {
90
+ if err := validateTimeRangeSpec (raw ); err != nil {
91
+ return
CDEC
nil , xerrors .Errorf ("validate time range schedule: %w" , err )
92
+ }
93
+
94
+ return parse (raw )
95
+ }
96
+
74
97
func parse (raw string ) (* Schedule , error ) {
75
98
// If schedule does not specify a timezone, default to UTC. Otherwise,
76
99
// the library will default to time.Local which we want to avoid.
@@ -281,3 +304,18 @@ func validateDailySpec(spec string) error {
281
304
}
282
305
return nil
283
306
}
307
+
308
+ // validateTimeRangeSpec ensures that the minutes field is set to *
309
+ func validateTimeRangeSpec (spec string ) error {
310
+ parts := strings .Fields (spec )
311
+ if len (parts ) < 5 {
312
+ return xerrors .Errorf ("expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix" )
313
+ }
314
+ if len (parts ) == 6 {
315
+ parts = parts [1 :]
316
+ }
317
+ if parts [0 ] != "*" {
318
+ return xerrors .Errorf ("expected minutes to be *" )
319
+ }
320
+ return nil
321
+ }
0 commit comments