package scheduler import ( "errors" "reflect" "time" ) const MAXJOBNUM = 10000 var _location = time.Local type Job interface { Do(jobFun interface{}, params ...interface{}) At(t string) Job NextScheduledTime() time.Time Second() (job Job) Seconds() (job Job) Minute() (job Job) Minutes() (job Job) Hour() (job Job) Hours() (job Job) Day() (job Job) Days() (job Job) Monday() (job Job) Tuesday() (job Job) Wednesday() (job Job) Thursday() (job Job) Friday() (job Job) Saturday() (job Job) Sunday() (job Job) Weeks() (job Job) shouldRun() bool run() (result []reflect.Value, err error) scheduleNextRun() nextRun() time.Time jobFunc() string } func NewJob(intervel uint64) Job { return &innerJob{ intervel, "", "", "", time.Unix(0, 0), time.Unix(0, 0), 0, time.Sunday, make(map[string]interface{}), make(map[string]([]interface{})), } } type innerJob struct { // pause interval * unit bettween runs interval uint64 // the job _jobFunc to run, func[_jobFunc] _jobFunc string // time units, ,e.g. 'minutes', 'hours'... unit string // optional time at which this job runs atTime string // datetime of last run lastRun time.Time // datetime of next run _nextRun time.Time // cache the period between last an next run period time.Duration // Specific day of the week to start on startDay time.Weekday // Map for the function task store funcs map[string]interface{} // Map for function and params of function fparams map[string]([]interface{}) } // True if the job should be run now func (j *innerJob) shouldRun() bool { return time.Now().After(j._nextRun) } // True if the job should be run now func (j *innerJob) nextRun() time.Time { return j._nextRun } // True if the job should be run now func (j *innerJob) jobFunc() string { return j._jobFunc } //Run the job and immediately reschedule it func (j *innerJob) run() (result []reflect.Value, err error) { f := reflect.ValueOf(j.funcs[j._jobFunc]) params := j.fparams[j._jobFunc] if len(params) != f.Type().NumIn() { err = errors.New("the number of param is not adapted") return } in := make([]reflect.Value, len(params)) for k, param := range params { in[k] = reflect.ValueOf(param) } result = f.Call(in) j.lastRun = time.Now() j.scheduleNextRun() return } func (j *innerJob) Do(jobFun interface{}, params ...interface{}) { typ := reflect.TypeOf(jobFun) if typ.Kind() != reflect.Func { panic("only function can be schedule into the job queue.") } fname := getFunctionName(jobFun) j.funcs[fname] = jobFun j.fparams[fname] = params j._jobFunc = fname //schedule the next run j.scheduleNextRun() } // s.Every(1).Day().At("10:30").Do(task) // s.Every(1).Monday().At("10:30").Do(task) func (j *innerJob) At(t string) Job { hour, min, 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, _location) 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, _location) } } 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, _location) } else { j.lastRun = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day()-7, hour, min, 0, 0, _location) } } return j } //Compute the instant when this job should run next func (j *innerJob) scheduleNextRun() { if j.lastRun == time.Unix(0, 0) { if j.unit == "weeks" { i := time.Now().Weekday() - j.startDay 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, _location) } else { j.lastRun = time.Now() } } if j.period != 0 { // translate all the units to the Seconds j._nextRun = j.lastRun.Add(j.period * time.Second) } else { switch j.unit { case "minutes": j.period = time.Duration(j.interval * 60) break case "hours": j.period = time.Duration(j.interval * 60 * 60) break case "days": j.period = time.Duration(j.interval * 60 * 60 * 24) break case "weeks": j.period = time.Duration(j.interval * 60 * 60 * 24 * 7) break case "seconds": j.period = time.Duration(j.interval) } j._nextRun = j.lastRun.Add(j.period * time.Second) } } // NextScheduledTime returns the time of when this job is to run next func (j *innerJob) NextScheduledTime() time.Time { return j._nextRun } // the follow functions set the job's unit with seconds,minutes,hours... // Set the unit with second func (j *innerJob) Second() (job Job) { if j.interval != 1 { panic("") } job = j.Seconds() return } // Set the unit with seconds func (j *innerJob) Seconds() (job Job) { j.unit = "seconds" return j } // Set the unit with minute, which interval is 1 func (j *innerJob) Minute() (job Job) { if j.interval != 1 { panic("") } job = j.Minutes() return } //set the unit with minute func (j *innerJob) Minutes() (job Job) { j.unit = "minutes" return j } //set the unit with hour, which interval is 1 func (j *innerJob) Hour() (job Job) { if j.interval != 1 { panic("") } job = j.Hours() return } // Set the unit with hours func (j *innerJob) Hours() (job Job) { j.unit = "hours" return j } // Set the job's unit with day, which interval is 1 func (j *innerJob) Day() (job Job) { if j.interval != 1 { panic("") } job = j.Days() return } // Set the job's unit with days func (j *innerJob) Days() Job { j.unit = "days" return j } // s.Every(1).Monday().Do(task) // Set the start day with Monday func (j *innerJob) Monday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 1 job = j.Weeks() return } // Set the start day with Tuesday func (j *innerJob) Tuesday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 2 job = j.Weeks() return } // Set the start day woth Wednesday func (j *innerJob) Wednesday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 3 job = j.Weeks() return } // Set the start day with thursday func (j *innerJob) Thursday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 4 job = j.Weeks() return } // Set the start day with friday func (j *innerJob) Friday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 5 job = j.Weeks() return } // Set the start day with saturday func (j *innerJob) Saturday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 6 job = j.Weeks() return } // Set the start day with sunday func (j *innerJob) Sunday() (job Job) { if j.interval != 1 { panic("") } j.startDay = 0 job = j.Weeks() return } //Set the units as weeks func (j *innerJob) Weeks() Job { j.unit = "weeks" return j }