Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cron job trigger on timezone based, daylightsaving taken care #40

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func task() {
fmt.Println("I am runnning task.")
}

func dayLightTask() {
fmt.Println("I am runnning task1.")
}

func taskWithParams(a int, b string) {
fmt.Println(a, b)
}
Expand All @@ -50,9 +54,13 @@ func main() {
gocron.Every(1).Monday().Do(task)
gocron.Every(1).Thursday().Do(task)

// function At() take a string like 'hour:min'
// function At() take a string like 'hour:min'
gocron.Every(1).Day().At("10:30").Do(task)
gocron.Every(1).Monday().At("18:30").Do(task)
// function At() take a string like 'hour:min:sec'
gocron.Every(1).Day().Zone("EST").At("12:34:37").Do(dayLightTask)
// Zone() will handle daylight savings if CST6CDT is used instead of CST.
gocron.Every(1).Day().Zone("CST6CDT").At("12:34:37").Do(dayLightTask)
gocron.Every(1).Monday().At("18:30:00").Do(task)

// remove, clear and next_run
_, time := gocron.NextRun()
Expand Down
12 changes: 10 additions & 2 deletions example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package main
import (
"fmt"

"github.com/jasonlvhit/gocron"
"github.com/laxmanvallandas/gocron"
)

func task() {
fmt.Println("I am runnning task.")
}

func dayLightTask() {
fmt.Println("I am runnning dayLightTask.")
}

func taskWithParams(a int, b string) {
fmt.Println(a, b)
}
Expand All @@ -34,7 +38,11 @@ func main() {

// function At() take a string like 'hour:min'
gocron.Every(1).Day().At("10:30").Do(task)
gocron.Every(1).Monday().At("18:30").Do(task)
// function At() take a string like 'hour:min:sec'
gocron.Every(1).Day().Zone("EST").At("12:34:37").Do(dayLightTask)
// Zone() will handle daylight savings if CST6CDT is used instead of CST.
gocron.Every(1).Monday().Zone("CST6CDT").At("12:34:37").Do(dayLightTask)
gocron.Every(1).Monday().At("18:30:00").Do(task)

// remove, clear and next_run
_, time := gocron.NextRun()
Expand Down
45 changes: 33 additions & 12 deletions gocron.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ type Job struct {

// Map for function and params of function
fparams map[string]([]interface{})

//diff time zone for each timer
timeZone *time.Location
}

// Create a new job with the time interval.
Expand All @@ -78,6 +81,7 @@ func NewJob(intervel uint64) *Job {
time.Sunday,
make(map[string]interface{}),
make(map[string]([]interface{})),
time.UTC,
}
}

Expand All @@ -99,7 +103,7 @@ func (j *Job) run() (result []reflect.Value, err error) {
in[k] = reflect.ValueOf(param)
}
result = f.Call(in)
j.lastRun = time.Now()
j.lastRun = time.Now().In(j.timeZone)
j.scheduleNextRun()
return
}
Expand All @@ -125,10 +129,10 @@ func (j *Job) Do(jobFun interface{}, params ...interface{}) {
j.scheduleNextRun()
}

func formatTime(t string) (hour, min int, err error) {
func formatTime(t string) (hour, min, sec int, err error) {
var er = errors.New("time format error")
ts := strings.Split(t, ":")
if len(ts) != 2 {
if len(ts) != 2 && len(ts) != 3 {
err = er
return
}
Expand All @@ -141,40 +145,47 @@ func formatTime(t string) (hour, min int, err error) {
if err != nil {
return
}

if hour < 0 || hour > 23 || min < 0 || min > 59 {
if len(ts) == 3 {
sec, err = strconv.Atoi(ts[2])
if err != nil {
return
}
} else {
sec = 0
}
if hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 59 {
err = er
return
}
return hour, min, nil
return hour, min, sec, nil
}

// s.Every(1).Day().At("10:30").Do(task)
// s.Every(1).Monday().At("10:30").Do(task)
func (j *Job) At(t string) *Job {
hour, min, err := formatTime(t)
hour, min, sec, err := formatTime(t)
if err != nil {
panic(err)
}

// time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
mock := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), int(hour), int(min), 0, 0, loc)
mock := time.Date(time.Now().In(j.timeZone).Year(), time.Now().In(j.timeZone).Month(), time.Now().In(j.timeZone).Day(), int(hour), int(min), int(sec), 0, j.timeZone)

if j.unit == "days" {
if time.Now().After(mock) {
j.lastRun = mock
} else {
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-1, hour, min, 0, 0, loc)
j.lastRun = time.Date(time.Now().In(j.timeZone).AddDate(0, 0, -1).Year(), time.Now().In(j.timeZone).AddDate(0, 0, -1).Month(), time.Now().In(j.timeZone).AddDate(0, 0, -1).Day(), hour, min, sec, 0, j.timeZone)
}
} else if j.unit == "weeks" {
if j.startDay != time.Now().Weekday() || (time.Now().After(mock) && j.startDay == time.Now().Weekday()) {
i := mock.Weekday() - j.startDay
if i < 0 {
i = 7 + i
}
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), hour, min, 0, 0, loc)
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), hour, min, sec, 0, j.timeZone)
} else {
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-7, hour, min, 0, 0, loc)
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-7, hour, min, sec, 0, j.timeZone)
}
}
return j
Expand All @@ -188,7 +199,7 @@ func (j *Job) scheduleNextRun() {
if i < 0 {
i = 7 + i
}
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), 0, 0, 0, 0, loc)
j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-int(i), 0, 0, 0, 0, j.timeZone)

} else {
j.lastRun = time.Now()
Expand Down Expand Up @@ -224,6 +235,16 @@ func (j *Job) NextScheduledTime() time.Time {
return j.nextRun
}

// Set timezone for timer
func (j *Job) Zone(timeZone string) *Job {
recivedTimeZone, err := time.LoadLocation(timeZone)
if err != nil {
panic("time zone format error.")
}
j.timeZone = recivedTimeZone
return j
}

// the follow functions set the job's unit with seconds,minutes,hours...

// Set the unit with second
Expand Down
51 changes: 35 additions & 16 deletions gocron_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,18 @@ func TestScheduler_WeekdaysTodayAfter(t *testing.T) {
scheduler := NewScheduler()

now := time.Now()
timeToSchedule := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-1, 0, 0, time.Local)
timeToSchedule := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-1, now.Second()-1, 0, time.UTC)

job := callTodaysWeekday(scheduler.Every(1)).At(fmt.Sprintf("%02d:%02d", timeToSchedule.Hour(), timeToSchedule.Minute()))
job := callTodaysWeekday(scheduler.Every(1)).At(fmt.Sprintf("%02d:%02d:%02d", timeToSchedule.Hour(), timeToSchedule.Minute(), timeToSchedule.Second()))
job.Do(task)
t.Logf("job is scheduled for %s", job.NextScheduledTime())
if job.NextScheduledTime().Weekday() != timeToSchedule.Weekday() {
t.Fail()
t.Logf("Job scheduled for current weekday for earlier time, should still be scheduled for current weekday (but next week)")
}
nextWeek := time.Date(now.Year(), now.Month(), now.Day()+7, now.Hour(), now.Minute()-1, 0, 0, time.Local)
nextWeek := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-1, now.Second()-1, 0, time.UTC)
if !job.NextScheduledTime().Equal(nextWeek) {
t.Fail()
t.Logf("Job should be scheduled for the correct time next week.")
t.Errorf("Job should be scheduled for the correct time next week.\nGot %+v, expected %+v", job.NextScheduledTime(), nextWeek)
}
}

Expand All @@ -67,9 +66,9 @@ func TestScheduler_WeekdaysTodayBefore(t *testing.T) {
scheduler := NewScheduler()

now := time.Now()
timeToSchedule := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()+1, 0, 0, time.Local)
timeToSchedule := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()+1, now.Second()+1, 0, time.UTC)

job := callTodaysWeekday(scheduler.Every(1)).At(fmt.Sprintf("%02d:%02d", timeToSchedule.Hour(), timeToSchedule.Minute()))
job := callTodaysWeekday(scheduler.Every(1)).Day().At(fmt.Sprintf("%02d:%02d:%02d", timeToSchedule.Hour(), timeToSchedule.Minute(), timeToSchedule.Second()))
job.Do(task)
t.Logf("job is scheduled for %s", job.NextScheduledTime())
if !job.NextScheduledTime().Equal(timeToSchedule) {
Expand All @@ -84,6 +83,7 @@ func Test_formatTime(t *testing.T) {
args string
wantHour int
wantMin int
wantSec int
wantErr bool
}{
{
Expand Down Expand Up @@ -115,10 +115,19 @@ func Test_formatTime(t *testing.T) {
wantErr: true,
},
{
name: "wrongformat",
name: "normal",
args: "19:18:17",
wantHour: 19,
wantMin: 18,
wantSec: 17,
wantErr: false,
},
{
name: "wrongformat",
args: "19:18:17:11",
wantHour: 0,
wantMin: 0,
wantSec: 0,
wantErr: true,
},
{
Expand All @@ -131,7 +140,7 @@ func Test_formatTime(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotHour, gotMin, err := formatTime(tt.args)
gotHour, gotMin, gotSec, err := formatTime(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("formatTime() error = %v, wantErr %v", err, tt.wantErr)
return
Expand All @@ -142,20 +151,30 @@ func Test_formatTime(t *testing.T) {
if gotMin != tt.wantMin {
t.Errorf("formatTime() gotMin = %v, want %v", gotMin, tt.wantMin)
}
if gotSec != tt.wantSec {
t.Errorf("formatTime() gotMin = %v, want %v", gotSec, tt.wantSec)
}
})
}
}

// utility function for testing the weekday functions *on* the current weekday.
func callTodaysWeekday(job *Job) *Job {
switch time.Now().Weekday() {
case 0: job.Sunday()
case 1: job.Monday()
case 2: job.Tuesday()
case 3: job.Wednesday()
case 4: job.Thursday()
case 5: job.Friday()
case 6: job.Saturday()
case 0:
job.Sunday()
case 1:
job.Monday()
case 2:
job.Tuesday()
case 3:
job.Wednesday()
case 4:
job.Thursday()
case 5:
job.Friday()
case 6:
job.Saturday()
}
return job
}
Expand Down