@@ -24,6 +24,7 @@ type internalJob struct {
2424 id uuid.UUID
2525 name string
2626 tags []string
27+ cron Cron
2728 jobSchedule
2829
2930 // as some jobs may queue up, it's possible to
@@ -104,6 +105,20 @@ type limitRunsTo struct {
104105 runCount uint
105106}
106107
108+ // -----------------------------------------------
109+ // -----------------------------------------------
110+ // --------------- Custom Cron -------------------
111+ // -----------------------------------------------
112+ // -----------------------------------------------
113+
114+ // Cron defines the interface that must be
115+ // implemented to provide a custom cron implementation for
116+ // the job. Pass in the implementation using the JobOption WithCronImplementation.
117+ type Cron interface {
118+ IsValid (crontab string , location * time.Location , now time.Time ) error
119+ Next (lastRun time.Time ) time.Time
120+ }
121+
107122// -----------------------------------------------
108123// -----------------------------------------------
109124// --------------- Job Variants ------------------
@@ -116,29 +131,37 @@ type JobDefinition interface {
116131 setup (j * internalJob , l * time.Location , now time.Time ) error
117132}
118133
119- var _ JobDefinition = ( * cronJobDefinition )( nil )
134+ // Default cron implementation
120135
121- type cronJobDefinition struct {
122- crontab string
123- withSeconds bool
136+ func newDefaultCronImplementation (withSeconds bool ) Cron {
137+ return & defaultCron {
138+ withSeconds : withSeconds ,
139+ }
124140}
125141
126- func (c cronJobDefinition ) setup (j * internalJob , location * time.Location , now time.Time ) error {
142+ var _ Cron = (* defaultCron )(nil )
143+
144+ type defaultCron struct {
145+ cronSchedule cron.Schedule
146+ withSeconds bool
147+ }
148+
149+ func (r * defaultCron ) IsValid (crontab string , location * time.Location , now time.Time ) error {
127150 var withLocation string
128- if strings .HasPrefix (c . crontab , "TZ=" ) || strings .HasPrefix (c . crontab , "CRON_TZ=" ) {
129- withLocation = c . crontab
151+ if strings .HasPrefix (crontab , "TZ=" ) || strings .HasPrefix (crontab , "CRON_TZ=" ) {
152+ withLocation = crontab
130153 } else {
131154 // since the user didn't provide a timezone default to the location
132155 // passed in by the scheduler. Default: time.Local
133- withLocation = fmt .Sprintf ("CRON_TZ=%s %s" , location .String (), c . crontab )
156+ withLocation = fmt .Sprintf ("CRON_TZ=%s %s" , location .String (), crontab )
134157 }
135158
136159 var (
137160 cronSchedule cron.Schedule
138161 err error
139162 )
140163
141- if c .withSeconds {
164+ if r .withSeconds {
142165 p := cron .NewParser (cron .SecondOptional | cron .Minute | cron .Hour | cron .Dom | cron .Month | cron .Dow | cron .Descriptor )
143166 cronSchedule , err = p .Parse (withLocation )
144167 } else {
@@ -150,8 +173,32 @@ func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now ti
150173 if cronSchedule .Next (now ).IsZero () {
151174 return ErrCronJobInvalid
152175 }
176+ r .cronSchedule = cronSchedule
177+ return nil
178+ }
153179
154- j .jobSchedule = & cronJob {cronSchedule : cronSchedule }
180+ func (r * defaultCron ) Next (lastRun time.Time ) time.Time {
181+ return r .cronSchedule .Next (lastRun )
182+ }
183+
184+ // default cron job implimentation
185+ var _ JobDefinition = (* cronJobDefinition )(nil )
186+
187+ type cronJobDefinition struct {
188+ crontab string
189+ cron Cron
190+ }
191+
192+ func (c cronJobDefinition ) setup (j * internalJob , location * time.Location , now time.Time ) error {
193+ if j .cron != nil {
194+ c .cron = j .cron
195+ }
196+
197+ if err := c .cron .IsValid (c .crontab , location , now ); err != nil {
198+ return err
199+ }
200+
201+ j .jobSchedule = & cronJob {crontab : c .crontab , cronSchedule : c .cron }
155202 return nil
156203}
157204
@@ -163,8 +210,8 @@ func (c cronJobDefinition) setup(j *internalJob, location *time.Location, now ti
163210// `CRON_TZ=America/Chicago * * * * *`
164211func CronJob (crontab string , withSeconds bool ) JobDefinition {
165212 return cronJobDefinition {
166- crontab : crontab ,
167- withSeconds : withSeconds ,
213+ crontab : crontab ,
214+ cron : newDefaultCronImplementation ( withSeconds ) ,
168215 }
169216}
170217
@@ -608,6 +655,15 @@ func WithName(name string) JobOption {
608655 }
609656}
610657
658+ // WithCronImplementation sets the custom Cron implementation for the job.
659+ // This is only utilized for the CronJob type.
660+ func WithCronImplementation (c Cron ) JobOption {
661+ return func (j * internalJob , _ time.Time ) error {
662+ j .cron = c
663+ return nil
664+ }
665+ }
666+
611667// WithSingletonMode keeps the job from running again if it is already running.
612668// This is useful for jobs that should not overlap, and that occasionally
613669// (but not consistently) run longer than the interval between job runs.
@@ -818,7 +874,8 @@ type jobSchedule interface {
818874var _ jobSchedule = (* cronJob )(nil )
819875
820876type cronJob struct {
821- cronSchedule cron.Schedule
877+ crontab string
878+ cronSchedule Cron
822879}
823880
824881func (j * cronJob ) next (lastRun time.Time ) time.Time {
0 commit comments