Repository: go-co-op/gocron
Pull request #221 (2021-08-30 14:12:51)

only days 1 through 28 are allowed for monthly schedules
### What does this do?
only days 1 through 28 are allowed for monthly schedules

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->
#141 

### 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/gocron.go b/gocron.go
index 31e7241..a734d39 100644
--- a/gocron.go
+++ b/gocron.go
@@ -27,6 +27,7 @@ var (
 
 	ErrAtTimeNotSupported     = errors.New("the At() method is not supported for this time unit")
 	ErrWeekdayNotSupported    = errors.New("weekday is not supported for time unit")
+	ErrInvalidDayOfMonthEntry = errors.New("only days 1 through 28 are allowed for monthly schedules")
 	ErrTagsUnique             = func(tag string) error { return fmt.Errorf("a non-unique tag was set on the job: %s", tag) }
 	ErrWrongParams            = errors.New("wrong list of params")
 	ErrUpdateCalledWithoutJob = errors.New("a call to Scheduler.Update() requires a call to Scheduler.Job() first")
diff --git a/scheduler.go b/scheduler.go
index a158ec0..80a11c6 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -856,13 +856,20 @@ func (s *Scheduler) Weeks() *Scheduler {
 }
 
 // Month sets the unit with months
+// Note: Only days 1 through 28 are allowed for monthly schedules
 func (s *Scheduler) Month(dayOfTheMonth int) *Scheduler {
 	return s.Months(dayOfTheMonth)
 }
 
 // Months sets the unit with months
+// Note: Only days 1 through 28 are allowed for monthly schedules
 func (s *Scheduler) Months(dayOfTheMonth int) *Scheduler {
 	job := s.getCurrentJob()
+
+	if dayOfTheMonth < 1 || dayOfTheMonth > 28 {
+		job.error = wrapOrError(job.error, ErrInvalidDayOfMonthEntry)
+	}
+
 	job.dayOfTheMonth = dayOfTheMonth
 	job.startsImmediately = false
 	s.setUnit(months)


Test patch:
diff --git a/scheduler_test.go b/scheduler_test.go
index 46b0d94..e9c0326 100644
--- a/scheduler_test.go
+++ b/scheduler_test.go
@@ -907,23 +907,28 @@ func TestRunJobsWithLimit(t *testing.T) {
 	})
 }
 
-func TestCalculateMonths(t *testing.T) {
-	ft := fakeTime{onNow: func(l *time.Location) time.Time {
-		return time.Date(1970, 1, 1, 12, 0, 0, 0, l)
-	}}
-	s := NewScheduler(time.UTC)
-	s.time = ft
-	s.StartAsync()
-	job, err := s.Every(1).Month(1).At("10:00").Do(func() {
-		fmt.Println("hello task")
-	})
-	require.NoError(t, err)
-	s.Stop()
+func TestCalculateMonthsError(t *testing.T) {
+	testCases := []struct {
+		desc       string
+		dayOfMonth int
+	}{
+		{"invalid -1", -1},
+		{"invalid 29", 29},
+	}
 
-	assert.Equal(t, s.time.Now(s.location).AddDate(0, 1, 0).Month(), job.nextRun.Month())
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			s := NewScheduler(time.UTC)
+			job, err := s.Every(1).Month(tc.dayOfMonth).Do(func() {
+				fmt.Println("hello task")
+			})
+			require.Error(t, err)
+			require.Nil(t, job)
+		})
+	}
 }
 
-func TestCalculateMonths2(t *testing.T) {
+func TestCalculateMonths(t *testing.T) {
 	maySecond2021At0200 := time.Date(2021, 5, 2, 2, 0, 0, 0, time.UTC)
 
 	maySecond2021At0800 := time.Date(2021, 5, 2, 8, 0, 0, 0, time.UTC)