package jobs import ( "time" ) // JobSchedule specifies a duty cycle (to the second granularity), based on a // traditional crontab specification. It is computed initially and stored as bit sets. type JobSchedule struct { Second, Minute, Hour, Dom, Month, Dow uint64 *RuntimeJobStu // Override location for this schedule. Location *time.Location } // bounds provides a range of acceptable values (plus a map of name to value). type bounds struct { min, max uint names map[string]uint } const aDay = 24 * 60 * 60 // The bounds for each field. var ( seconds = bounds{0, 59, nil} minutes = bounds{0, 59, nil} hours = bounds{0, 23, nil} dom = bounds{1, 31, nil} months = bounds{1, 12, map[string]uint{ "jan": 1, "feb": 2, "mar": 3, "apr": 4, "may": 5, "jun": 6, "jul": 7, "aug": 8, "sep": 9, "oct": 10, "nov": 11, "dec": 12, }} dow = bounds{0, 6, map[string]uint{ "sun": 0, "mon": 1, "tue": 2, "wed": 3, "thu": 4, "fri": 5, "sat": 6, }} ) const ( // Set the top bit if a star was included in the expression. starBit = 1 << 63 ) //var _ Schedule = (*JobSchedule)(nil) // Next returns the next time this schedule is activated, greater than the given // time. If no time can be found to satisfy the schedule, return the zero time. func (s *JobSchedule) Next(t time.Time) (time.Time, bool) { //switch s.TimeData.Type { //case constants.DelayJob: // return s.delayNext(t) //case constants.CronJob: // return s.cronNext(t) //case constants.RepeatJob: // return s.repeatNext(t) //default: // return time.Time{}, true //} return s.cronNext(t) } func (s *JobSchedule) cronNext(t time.Time) (time.Time, bool) { // General approach // // For Month, Day, Hour, Minute, Second: // Check if the time value matches. If yes, continue to the next field. // If the field doesn't match the schedule, then increment the field until it matches. // While incrementing the field, a wrap-around brings it back to the beginning // of the field list (since it is necessary to re-verify previous field // values) // Convert the given time into the schedule's timezone, if one is specified. // Save the original timezone so we can convert back after we find a time. // Note that schedules without a time zone specified (time.Local) are treated // as local to the time provided. origLocation := t.Location() loc := s.Location if loc == time.Local { loc = t.Location() } if s.Location != time.Local { t = t.In(s.Location) } // Start at the earliest possible time (the upcoming second). t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) // This flag indicates whether a field has been incremented. added := false // If no time is found within five years, return zero. yearLimit := t.Year() + 5 WRAP: if t.Year() > yearLimit { return time.Time{}, true } // Find the first applicable month. // If it's this month, then do nothing. for 1< 12 { t = t.Add(time.Duration(24-t.Hour()) * time.Hour) } else { t = t.Add(time.Duration(-t.Hour()) * time.Hour) } } if t.Day() == 1 { goto WRAP } } for 1< exp.FinishTime { // return true // } // return false // case 2: // if s.Runtimes >= exp.Retries { // return true // } // return false // case 3: // return false // default: // return false // } // case constants.SampleJob: // exp, ok := s.TimeData.Expression.(SampleExp) // if !ok { // return true // } // switch exp.RunType { // case 1: // if time.Now().Unix()+exp.IntervalTime > exp.FinishTime { // return true // } // return false // case 2: // if s.Runtimes >= exp.Retries { // return true // } // return false // case 3: // return false // default: // return false // } // default: // return true // } //} func (s *JobSchedule) ScheduleAdd1() int64 { s.Runtimes++ return s.Runtimes } // dayMatches returns true if the schedule's day-of-week and day-of-month // restrictions are satisfied by the given time. func dayMatches(s *JobSchedule, t time.Time) bool { var ( domMatch bool = 1< 0 dowMatch bool = 1< 0 ) if s.Dom&starBit > 0 || s.Dow&starBit > 0 { return domMatch && dowMatch } return domMatch || dowMatch }