From 6211da4789ed7f687e3a316c68aac044226dd8b1 Mon Sep 17 00:00:00 2001 From: John Roesler Date: Wed, 25 Nov 2020 22:49:21 -0600 Subject: [PATCH 1/3] start jobs immediately by default --- README.md | 8 ++--- example_test.go | 8 +---- job.go | 29 ++++++++--------- scheduler.go | 23 ++++++------- scheduler_test.go | 82 +++++++++++++++++++++++------------------------ 5 files changed, 68 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index ce2ca896..d42f9c5a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ See also these two great articles: If you want to chat, you can find us at Slack! [](https://gophers.slack.com/archives/CQ7T0T1FW) -# Examples: +## Examples: ```go package main @@ -71,7 +71,6 @@ func main() { tag1 := []string{"tag1"} tag2 := []string{"tag2"} - s2.Every(1).Week().SetTag(tag1).Do(task) s2.Every(1).Week().SetTag(tag2).Do(task) @@ -89,14 +88,15 @@ func main() { s2.Every(1).Wednesday().At("1:01").Do(task) // Begin job at a specific date/time. - // Attention: scheduler timezone has precedence over job's timezone! t := time.Date(2019, time.November, 10, 15, 0, 0, 0, time.UTC) s2.Every(1).Hour().StartAt(t).Do(task) + // Delay start of job + s2.Every(1).Hour().StartAt(time.Now().Add(time.Duration(1 * time.Hour)).Do(task) + // use .StartImmediately() to run job upon scheduler start s2.Every(1).Hour().StartImmediately().Do(task) - // NextRun gets the next running time _, time := s2.NextRun() fmt.Println(time) diff --git a/example_test.go b/example_test.go index 5d2e7c9e..1efa4c29 100644 --- a/example_test.go +++ b/example_test.go @@ -20,13 +20,7 @@ func ExampleScheduler_StartBlocking() { func ExampleScheduler_StartAsync() { s := gocron.NewScheduler(time.UTC) _, _ = s.Every(3).Seconds().Do(task) - <-s.StartAsync() -} - -func ExampleScheduler_StartImmediately() { - s := gocron.NewScheduler(time.UTC) - _, _ = s.Every(1).Hour().StartImmediately().Do(task) - s.StartBlocking() + s.StartAsync() } func ExampleScheduler_StartAt() { diff --git a/job.go b/job.go index 2d0b52d7..9961ffa5 100644 --- a/job.go +++ b/job.go @@ -11,21 +11,20 @@ type jobInterval uint64 // Job struct stores the information necessary to run a Job type Job struct { sync.RWMutex - interval jobInterval // pause interval * unit between runs - unit timeUnit // time units, ,e.g. 'minutes', 'hours'... - startsImmediately bool // if the Job should run upon scheduler start - jobFunc string // the Job jobFunc to run, func[jobFunc] - atTime time.Duration // optional time at which this Job runs - err error // error related to Job - lastRun time.Time // datetime of last run - nextRun time.Time // datetime of next run - scheduledWeekday *time.Weekday // Specific day of the week to start on - dayOfTheMonth int // Specific day of the month to run the job - funcs map[string]interface{} // Map for the function task store - fparams map[string][]interface{} // Map for function and params of function - tags []string // allow the user to tag Jobs with certain labels - runConfig runConfig // configuration for how many times to run the job - runCount int // number of time the job ran + interval jobInterval // pause interval * unit between runs + unit timeUnit // time units, ,e.g. 'minutes', 'hours'... + jobFunc string // the Job jobFunc to run, func[jobFunc] + atTime time.Duration // optional time at which this Job runs + err error // error related to Job + lastRun time.Time // datetime of last run + nextRun time.Time // datetime of next run + scheduledWeekday *time.Weekday // Specific day of the week to start on + dayOfTheMonth int // Specific day of the month to run the job + funcs map[string]interface{} // Map for the function task store + fparams map[string][]interface{} // Map for function and params of function + tags []string // allow the user to tag Jobs with certain labels + runConfig runConfig // configuration for how many times to run the job + runCount int // number of time the job ran } type runConfig struct { diff --git a/scheduler.go b/scheduler.go index f79646c1..9864b413 100644 --- a/scheduler.go +++ b/scheduler.go @@ -91,15 +91,17 @@ func (s *Scheduler) scheduleNextRun(job *Job) { defer job.Unlock() now := s.time.Now(s.loc) - if job.startsImmediately { - job.nextRun = now - job.startsImmediately = false - return + if job.neverRan() { + if !job.nextRun.IsZero() { + return // scheduled for future run and should skip scheduling + } + // default is for jobs to start immediately unless scheduled at a specific time or day + if job.atTime == 0 && job.scheduledWeekday == nil && job.dayOfTheMonth == 0 { + job.nextRun = now + return + } } - if job.neverRan() && !job.nextRun.IsZero() { - return // scheduled for future run and should skip scheduling - } job.lastRun = now durationToNextRun := s.durationToNextRun(job) @@ -438,13 +440,6 @@ func (s *Scheduler) StartAt(t time.Time) *Scheduler { return s } -// StartImmediately sets the Jobs next run as soon as the scheduler starts -func (s *Scheduler) StartImmediately() *Scheduler { - job := s.getCurrentJob() - job.startsImmediately = true - return s -} - // shouldRun returns true if the Job should be run now func (s *Scheduler) shouldRun(j *Job) bool { return j.shouldRun() && s.time.Now(s.loc).Unix() >= j.nextRun.Unix() diff --git a/scheduler_test.go b/scheduler_test.go index c4840a36..a7d4fb42 100644 --- a/scheduler_test.go +++ b/scheduler_test.go @@ -53,7 +53,7 @@ func TestExecutionSeconds(t *testing.T) { var ( executions []int64 interval uint64 = 2 - expectedExecutions = 3 + expectedExecutions = 4 ) runTime := time.Duration(6 * time.Second) @@ -95,20 +95,6 @@ func TestScheduledWithTag(t *testing.T) { } } -func TestStartImmediately(t *testing.T) { - sched := NewScheduler(time.UTC) - now := time.Now().UTC() - - job, _ := sched.Every(1).Hour().StartImmediately().Do(task) - sched.scheduleAllJobs() - next := job.ScheduledTime() - - nextRounded := time.Date(next.Year(), next.Month(), next.Day(), next.Hour(), next.Minute(), next.Second(), 0, time.UTC) - expected := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), 0, time.UTC) - - assert.Exactly(t, expected, nextRounded) -} - func TestAtFuture(t *testing.T) { s := NewScheduler(time.UTC) now := time.Now().UTC() @@ -135,22 +121,50 @@ func TestAtFuture(t *testing.T) { assert.Equal(t, false, shouldBeFalse, "Day job was not expected to run as it was in the future") } -func schedulerForNextWeekdayEveryNTimes(weekday time.Weekday, n uint64, s *Scheduler) *Scheduler { +func schedulerForNextOrPreviousWeekdayEveryNTimes(weekday time.Weekday, next bool, n uint64, s *Scheduler) *Scheduler { switch weekday { case time.Monday: - s = s.Every(n).Tuesday() + if next { + s = s.Every(n).Tuesday() + } else { + s = s.Every(n).Sunday() + } case time.Tuesday: - s = s.Every(n).Wednesday() + if next { + s = s.Every(n).Wednesday() + } else { + s = s.Every(n).Monday() + } case time.Wednesday: - s = s.Every(n).Thursday() + if next { + s = s.Every(n).Thursday() + } else { + s = s.Every(n).Tuesday() + } case time.Thursday: - s = s.Every(n).Friday() + if next { + s = s.Every(n).Friday() + } else { + s = s.Every(n).Wednesday() + } case time.Friday: - s = s.Every(n).Saturday() + if next { + s = s.Every(n).Saturday() + } else { + s = s.Every(n).Thursday() + } case time.Saturday: - s = s.Every(n).Sunday() + if next { + s = s.Every(n).Sunday() + } else { + s = s.Every(n).Friday() + } case time.Sunday: - s = s.Every(n).Monday() + if next { + s = s.Every(n).Monday() + } else { + s = s.Every(n).Saturday() + } } return s } @@ -159,23 +173,7 @@ func TestWeekdayBeforeToday(t *testing.T) { now := time.Now().In(time.UTC) s := NewScheduler(time.UTC) - // Schedule job at day before - switch now.Weekday() { - case time.Monday: - s = s.Every(1).Sunday() - case time.Tuesday: - s = s.Every(1).Monday() - case time.Wednesday: - s = s.Every(1).Tuesday() - case time.Thursday: - s = s.Every(1).Wednesday() - case time.Friday: - s = s.Every(1).Thursday() - case time.Saturday: - s = s.Every(1).Friday() - case time.Sunday: - s = s.Every(1).Saturday() - } + s = schedulerForNextOrPreviousWeekdayEveryNTimes(now.Weekday(), false, 1, s) weekJob, _ := s.Do(task) s.scheduleNextRun(weekJob) sixDaysFromNow := now.AddDate(0, 0, 6) @@ -188,7 +186,7 @@ func TestWeekdayAt(t *testing.T) { t.Run("asserts weekday scheduling starts at the current week", func(t *testing.T) { s := NewScheduler(time.UTC) now := time.Now().UTC() - s = schedulerForNextWeekdayEveryNTimes(now.Weekday(), 1, s) + s = schedulerForNextOrPreviousWeekdayEveryNTimes(now.Weekday(), true, 1, s) weekdayJob, _ := s.Do(task) s.scheduleNextRun(weekdayJob) @@ -386,7 +384,7 @@ func TestScheduler_StartAt(t *testing.T) { job, _ = scheduler.Every(3).Seconds().Do(func() {}) scheduler.scheduleNextRun(job) _, nextRun = scheduler.NextRun() - assert.Equal(t, now.Add(time.Second*3).Second(), nextRun.Second()) + assert.Equal(t, now.Second(), nextRun.Second()) } func TestScheduler_CalculateNextRun(t *testing.T) { From e22112f8c3012fee1a9fb500b7c755898bf75d5b Mon Sep 17 00:00:00 2001 From: John Roesler Date: Fri, 27 Nov 2020 14:41:04 -0600 Subject: [PATCH 2/3] set startsimmediately as default in constructor, false in date/time places --- README.md | 3 ++- example_test.go | 7 +++++++ job.go | 42 ++++++++++++++++++++++-------------------- scheduler.go | 23 +++++++++++++++++++---- scheduler_test.go | 14 ++++++++++++++ 5 files changed, 64 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d42f9c5a..7015f170 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,8 @@ func main() { // Delay start of job s2.Every(1).Hour().StartAt(time.Now().Add(time.Duration(1 * time.Hour)).Do(task) - // use .StartImmediately() to run job upon scheduler start + // Deprecated: Jobs start immediately by default + // use StartImmediately() to run job upon scheduler start s2.Every(1).Hour().StartImmediately().Do(task) // NextRun gets the next running time diff --git a/example_test.go b/example_test.go index 1efa4c29..681622ec 100644 --- a/example_test.go +++ b/example_test.go @@ -23,6 +23,13 @@ func ExampleScheduler_StartAsync() { s.StartAsync() } +// Deprecated: All jobs start immediately by default unless set to a specific date or time +func ExampleScheduler_StartImmediately() { + s := gocron.NewScheduler(time.UTC) + _, _ = s.Every(1).Hour().StartImmediately().Do(task) + s.StartBlocking() +} + func ExampleScheduler_StartAt() { s := gocron.NewScheduler(time.UTC) specificTime := time.Date(2019, time.November, 10, 15, 0, 0, 0, time.UTC) diff --git a/job.go b/job.go index 9961ffa5..ade04cf2 100644 --- a/job.go +++ b/job.go @@ -11,20 +11,21 @@ type jobInterval uint64 // Job struct stores the information necessary to run a Job type Job struct { sync.RWMutex - interval jobInterval // pause interval * unit between runs - unit timeUnit // time units, ,e.g. 'minutes', 'hours'... - jobFunc string // the Job jobFunc to run, func[jobFunc] - atTime time.Duration // optional time at which this Job runs - err error // error related to Job - lastRun time.Time // datetime of last run - nextRun time.Time // datetime of next run - scheduledWeekday *time.Weekday // Specific day of the week to start on - dayOfTheMonth int // Specific day of the month to run the job - funcs map[string]interface{} // Map for the function task store - fparams map[string][]interface{} // Map for function and params of function - tags []string // allow the user to tag Jobs with certain labels - runConfig runConfig // configuration for how many times to run the job - runCount int // number of time the job ran + interval jobInterval // pause interval * unit between runs + unit timeUnit // time units, ,e.g. 'minutes', 'hours'... + startsImmediately bool // if the Job should run upon scheduler start + jobFunc string // the Job jobFunc to run, func[jobFunc] + atTime time.Duration // optional time at which this Job runs + err error // error related to Job + lastRun time.Time // datetime of last run + nextRun time.Time // datetime of next run + scheduledWeekday *time.Weekday // Specific day of the week to start on + dayOfTheMonth int // Specific day of the month to run the job + funcs map[string]interface{} // Map for the function task store + fparams map[string][]interface{} // Map for function and params of function + tags []string // allow the user to tag Jobs with certain labels + runConfig runConfig // configuration for how many times to run the job + runCount int // number of time the job ran } type runConfig struct { @@ -35,12 +36,13 @@ type runConfig struct { // NewJob creates a new Job with the provided interval func NewJob(interval uint64) *Job { return &Job{ - interval: jobInterval(interval), - lastRun: time.Time{}, - nextRun: time.Time{}, - funcs: make(map[string]interface{}), - fparams: make(map[string][]interface{}), - tags: []string{}, + interval: jobInterval(interval), + lastRun: time.Time{}, + nextRun: time.Time{}, + funcs: make(map[string]interface{}), + fparams: make(map[string][]interface{}), + tags: []string{}, + startsImmediately: true, } } diff --git a/scheduler.go b/scheduler.go index 9864b413..738e53de 100644 --- a/scheduler.go +++ b/scheduler.go @@ -96,7 +96,7 @@ func (s *Scheduler) scheduleNextRun(job *Job) { return // scheduled for future run and should skip scheduling } // default is for jobs to start immediately unless scheduled at a specific time or day - if job.atTime == 0 && job.scheduledWeekday == nil && job.dayOfTheMonth == 0 { + if job.startsImmediately { job.nextRun = now return } @@ -424,6 +424,7 @@ func (s *Scheduler) At(t string) *Scheduler { } // save atTime start as duration from midnight j.atTime = time.Duration(hour)*time.Hour + time.Duration(min)*time.Minute + time.Duration(sec)*time.Second + j.startsImmediately = false return s } @@ -436,7 +437,17 @@ func (s *Scheduler) SetTag(t []string) *Scheduler { // StartAt schedules the next run of the Job func (s *Scheduler) StartAt(t time.Time) *Scheduler { - s.getCurrentJob().nextRun = t + job := s.getCurrentJob() + job.nextRun = t + job.startsImmediately = false + return s +} + +// Deprecated: Jobs start immediately by default unless a specific start day or time is set +// StartImmediately sets the Jobs next run as soon as the scheduler starts +func (s *Scheduler) StartImmediately() *Scheduler { + job := s.getCurrentJob() + job.startsImmediately = true return s } @@ -515,7 +526,9 @@ func (s *Scheduler) Month(dayOfTheMonth int) *Scheduler { // Months sets the unit with months func (s *Scheduler) Months(dayOfTheMonth int) *Scheduler { - s.getCurrentJob().dayOfTheMonth = dayOfTheMonth + job := s.getCurrentJob() + job.dayOfTheMonth = dayOfTheMonth + job.startsImmediately = false s.setUnit(months) return s } @@ -527,7 +540,9 @@ func (s *Scheduler) Months(dayOfTheMonth int) *Scheduler { // Weekday sets the start with a specific weekday weekday func (s *Scheduler) Weekday(startDay time.Weekday) *Scheduler { - s.getCurrentJob().scheduledWeekday = &startDay + job := s.getCurrentJob() + job.scheduledWeekday = &startDay + job.startsImmediately = false s.setUnit(weeks) return s } diff --git a/scheduler_test.go b/scheduler_test.go index a7d4fb42..52188f6b 100644 --- a/scheduler_test.go +++ b/scheduler_test.go @@ -95,6 +95,20 @@ func TestScheduledWithTag(t *testing.T) { } } +func TestStartImmediately(t *testing.T) { + sched := NewScheduler(time.UTC) + now := time.Now().UTC() + + job, _ := sched.Every(1).Hour().StartImmediately().Do(task) + sched.scheduleAllJobs() + next := job.ScheduledTime() + + nextRounded := time.Date(next.Year(), next.Month(), next.Day(), next.Hour(), next.Minute(), next.Second(), 0, time.UTC) + expected := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), 0, time.UTC) + + assert.Exactly(t, expected, nextRounded) +} + func TestAtFuture(t *testing.T) { s := NewScheduler(time.UTC) now := time.Now().UTC() From 34b88f955a061b6a5426b6f0091af94db67ba5df Mon Sep 17 00:00:00 2001 From: John Roesler Date: Fri, 27 Nov 2020 14:43:41 -0600 Subject: [PATCH 3/3] golint --- scheduler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler.go b/scheduler.go index 738e53de..b7b02eeb 100644 --- a/scheduler.go +++ b/scheduler.go @@ -443,8 +443,8 @@ func (s *Scheduler) StartAt(t time.Time) *Scheduler { return s } -// Deprecated: Jobs start immediately by default unless a specific start day or time is set // StartImmediately sets the Jobs next run as soon as the scheduler starts +// Deprecated: Jobs start immediately by default unless a specific start day or time is set func (s *Scheduler) StartImmediately() *Scheduler { job := s.getCurrentJob() job.startsImmediately = true