Repository: go-co-op/gocron
Pull request #82 (2020-11-26 04:52:22)

start jobs immediately by default
### What does this do?
The default behavior will now be that jobs start immediately unless scheduled at a specific time, for a specific weekday or on a specific day of the month. 

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->


### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/README.md b/README.md
index ce2ca89..7015f17 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! [<img src="https://img.shields.io/badge/gophers-gocron-brightgreen?logo=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,13 +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)
 
-    // use .StartImmediately() to run job upon scheduler start
-    s2.Every(1).Hour().StartImmediately().Do(task)
+    // Delay start of job
+    s2.Every(1).Hour().StartAt(time.Now().Add(time.Duration(1 * time.Hour)).Do(task)
 
+    // 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
     _, time := s2.NextRun()
diff --git a/job.go b/job.go
index 2d0b52d..ade04cf 100644
--- a/job.go
+++ b/job.go
@@ -36,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 f79646c..b7b02ee 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.startsImmediately {
+			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)
@@ -422,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
 }
 
@@ -434,11 +437,14 @@ 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
 }
 
 // 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
@@ -520,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
 }
@@ -532,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
 }


Test patch:
diff --git a/example_test.go b/example_test.go
index 5d2e7c9..681622e 100644
--- a/example_test.go
+++ b/example_test.go
@@ -20,9 +20,10 @@ func ExampleScheduler_StartBlocking() {
 func ExampleScheduler_StartAsync() {
 	s := gocron.NewScheduler(time.UTC)
 	_, _ = s.Every(3).Seconds().Do(task)
-	<-s.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)
diff --git a/scheduler_test.go b/scheduler_test.go
index c4840a3..52188f6 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)
@@ -135,22 +135,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 +187,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 +200,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 +398,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) {