Skip to content

Commit

Permalink
month interval
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnRoesler committed Oct 27, 2023
1 parent e2bfcd8 commit 7535e08
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 52 deletions.
60 changes: 55 additions & 5 deletions job.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ func DailyJob(interval int, atTimes []time.Duration, task Task, options ...JobOp
return nil
}

var _ JobDefinition = (*weeklyJobDefinition)(nil)

type weeklyJobDefinition struct {
}

func (w weeklyJobDefinition) options() []JobOption {
//TODO implement me
panic("implement me")
}

func (w weeklyJobDefinition) setup(i *internalJob, location *time.Location) error {
//TODO implement me
panic("implement me")
}

func (w weeklyJobDefinition) task() Task {
//TODO implement me
panic("implement me")
}

func WeeklyJob(interval int, daysOfTheWeek []time.Weekday, atTimes []time.Duration, task Task, options ...JobOption) JobDefinition {
return nil
}
Expand All @@ -224,6 +244,7 @@ func (m monthlyJobDefinition) options() []JobOption {

func (m monthlyJobDefinition) setup(j *internalJob, location *time.Location) error {
var ms monthlyJob
ms.interval = m.interval

if m.daysOfTheMonth != nil {
var days, daysEnd []int
Expand All @@ -237,9 +258,12 @@ func (m monthlyJobDefinition) setup(j *internalJob, location *time.Location) err
daysEnd = append(daysEnd, day)
}
}
days = removeSliceDuplicatesInt(days)
slices.Sort(days)
slices.Sort(daysEnd)
ms.days = days

daysEnd = removeSliceDuplicatesInt(daysEnd)
slices.Sort(daysEnd)
ms.daysFromEnd = daysEnd
}

Expand Down Expand Up @@ -502,9 +526,22 @@ func (j *durationRandomJob) next(lastRun time.Time) time.Time {
return lastRun.Add(j.min + time.Duration(r))
}

var _ jobSchedule = (*weeklyJob)(nil)

type weeklyJob struct {
daysOfWeek []time.Weekday
atTimes []time.Time
}

func (w weeklyJob) next(lastRun time.Time) time.Time {
//TODO implement me
panic("implement me")
}

var _ jobSchedule = (*monthlyJob)(nil)

type monthlyJob struct {
interval uint
days []int
daysFromEnd []int
atTimes []time.Time
Expand All @@ -515,6 +552,9 @@ func (m monthlyJob) next(lastRun time.Time) time.Time {
copy(days, m.days)
firstDayNextMonth := time.Date(lastRun.Year(), lastRun.Month()+1, 1, 0, 0, 0, 0, lastRun.Location())
for _, daySub := range m.daysFromEnd {
// getting a combined list of all the days and the negative days
// which count backwards from the first day of the next month
// -1 == the last day of the month
day := firstDayNextMonth.AddDate(0, 0, daySub).Day()
days = append(days, day)
}
Expand All @@ -524,25 +564,35 @@ func (m monthlyJob) next(lastRun time.Time) time.Time {
if !next.IsZero() {
return next
}

from := time.Date(lastRun.Year(), lastRun.Month()+time.Month(m.interval), 1, 0, 0, 0, 0, lastRun.Location())
for next.IsZero() {
next = m.nextMonthDayAtTime(firstDayNextMonth, days)
firstDayNextMonth = firstDayNextMonth.AddDate(0, 1, 0)
next = m.nextMonthDayAtTime(from, days)
from = from.AddDate(0, int(m.interval), 0)
}

return next
}

func (m monthlyJob) nextMonthDayAtTime(lastRun time.Time, days []int) time.Time {
// find the next day in the month that should run and then check for an at time
for _, day := range days {
if day >= lastRun.Day() {
for _, at := range m.atTimes {
// sub the day, and the at time hour/min/sec onto the lastRun's values
// to use in checks to see if we've got our next run time
atDate := time.Date(lastRun.Year(), lastRun.Month(), day, at.Hour(), at.Minute(), at.Second(), lastRun.Nanosecond(), lastRun.Location())
// this check handles if we're setting a day not in the current month
// e.g. setting day 31 in Feb results in March 2nd

if atDate.Month() != lastRun.Month() {
// this check handles if we're setting a day not in the current month
// e.g. setting day 31 in Feb results in March 2nd
continue
}

if atDate.After(lastRun) {
// checking to see if it is after i.e. greater than,
// and not greater or equal as our lastRun day/time
// will be in the loop, and we don't want to select it again
return atDate
}
}
Expand Down
49 changes: 20 additions & 29 deletions job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,8 @@ import (
"github.com/stretchr/testify/require"
)

func TestDurationJob(t *testing.T) {
tests := []struct {
name string
duration time.Duration
expectedErr *string
}{
{"success", time.Second, nil},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, err := NewScheduler()
require.NoError(t, err)
func TestDurationJob_next(t *testing.T) {

_, err = s.NewJob(
DurationJob(
tt.duration,
NewTask(
func() {},
nil,
),
),
)
require.NoError(t, err)

s.Start()
err = s.Shutdown()
require.NoError(t, err)
})
}
}

func TestMonthlyJob_next(t *testing.T) {
Expand All @@ -46,6 +18,7 @@ func TestMonthlyJob_next(t *testing.T) {

tests := []struct {
name string
interval uint
days []int
daysFromEnd []int
atTimes []time.Time
Expand All @@ -55,6 +28,7 @@ func TestMonthlyJob_next(t *testing.T) {
}{
{
"same day - before at time",
1,
[]int{1},
nil,
[]time.Time{
Expand All @@ -66,6 +40,7 @@ func TestMonthlyJob_next(t *testing.T) {
},
{
"same day - after at time, runs next available date",
1,
[]int{1, 10},
nil,
[]time.Time{
Expand All @@ -77,6 +52,7 @@ func TestMonthlyJob_next(t *testing.T) {
},
{
"daylight savings time",
1,
[]int{5},
nil,
[]time.Time{
Expand All @@ -88,6 +64,7 @@ func TestMonthlyJob_next(t *testing.T) {
},
{
"negative days",
1,
nil,
[]int{-1, -3, -5},
[]time.Time{
Expand All @@ -99,6 +76,7 @@ func TestMonthlyJob_next(t *testing.T) {
},
{
"day not in current month, runs next month (leap year)",
1,
[]int{31},
nil,
[]time.Time{
Expand All @@ -108,11 +86,24 @@ func TestMonthlyJob_next(t *testing.T) {
time.Date(2000, 3, 31, 5, 30, 0, 0, time.UTC),
29*24*time.Hour + 31*24*time.Hour,
},
{
"multiple days not in order",
1,
[]int{10, 7, 19, 2},
nil,
[]time.Time{
time.Date(0, 0, 0, 5, 30, 0, 0, time.UTC),
},
time.Date(2000, 1, 2, 5, 30, 0, 0, time.UTC),
time.Date(2000, 1, 7, 5, 30, 0, 0, time.UTC),
5 * 24 * time.Hour,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := monthlyJob{
interval: tt.interval,
days: tt.days,
daysFromEnd: tt.daysFromEnd,
atTimes: tt.atTimes,
Expand Down
25 changes: 7 additions & 18 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,9 @@ import (
"reflect"

"github.com/google/uuid"
"golang.org/x/exp/maps"
)

//func callJobFunc(jobFunc interface{}) {
// if jobFunc == nil {
// return
// }
// f := reflect.ValueOf(jobFunc)
// if !f.IsZero() {
// f.Call([]reflect.Value{})
// }
//}

func callJobFuncWithParams(jobFunc interface{}, params ...interface{}) error {
if jobFunc == nil {
return nil
Expand Down Expand Up @@ -63,13 +54,11 @@ func requestJob(id uuid.UUID, ch chan jobOutRequest, ctx context.Context) *inter
return &j
}

func mapKeysContainAnySliceElement(m map[string]struct{}, sl []string) bool {
for x := range m {
for _, y := range sl {
if x == y {
return true
}
}
func removeSliceDuplicatesInt(in []int) []int {
m := make(map[int]struct{})

for _, i := range in {
m[i] = struct{}{}
}
return false
return maps.Keys(m)
}
35 changes: 35 additions & 0 deletions util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gocron

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRemoveSliceDuplicatesInt(t *testing.T) {
tests := []struct {
name string
input []int
expected []int
}{
{
"lots of duplicates",
[]int{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
},
[]int{1, 2, 3, 4, 5},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := removeSliceDuplicatesInt(tt.input)
assert.ElementsMatch(t, tt.expected, result)
})
}

}

0 comments on commit 7535e08

Please sign in to comment.