| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 | package cronimport (	"time")// SpecSchedule specifies a duty cycle (to the second granularity), based on a// traditional crontab specification. It is computed initially and stored as bit sets.type SpecSchedule struct {	Second, Minute, Hour, Dom, Month, Dow uint64}// bounds provides a range of acceptable values (plus a map of name to value).type bounds struct {	min, max uint	names    map[string]uint}// 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)// 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 *SpecSchedule) Next(t time.Time) time.Time {	// 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)	// 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() + 5WRAP:	if t.Year() > yearLimit {		return time.Time{}	}	// Find the first applicable month.	// If it's this month, then do nothing.	for 1<<uint(t.Month())&s.Month == 0 {		// If we have to add a month, reset the other parts to 0.		if !added {			added = true			// Otherwise, set the date at the beginning (since the current time is irrelevant).			t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location())		}		t = t.AddDate(0, 1, 0)		// Wrapped around.		if t.Month() == time.January {			goto WRAP		}	}	// Now get a day in that month.	for !dayMatches(s, t) {		if !added {			added = true			t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())		}		t = t.AddDate(0, 0, 1)		if t.Day() == 1 {			goto WRAP		}	}	for 1<<uint(t.Hour())&s.Hour == 0 {		if !added {			added = true			t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location())		}		t = t.Add(1 * time.Hour)		if t.Hour() == 0 {			goto WRAP		}	}	for 1<<uint(t.Minute())&s.Minute == 0 {		if !added {			added = true			t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, t.Location())		}		t = t.Add(1 * time.Minute)		if t.Minute() == 0 {			goto WRAP		}	}	for 1<<uint(t.Second())&s.Second == 0 {		if !added {			added = true			t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, t.Location())		}		t = t.Add(1 * time.Second)		if t.Second() == 0 {			goto WRAP		}	}	return t}// dayMatches returns true if the schedule's day-of-week and day-of-month// restrictions are satisfied by the given time.func dayMatches(s *SpecSchedule, t time.Time) bool {	var (		domMatch bool = 1<<uint(t.Day())&s.Dom > 0		dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0	)	if s.Dom&starBit > 0 || s.Dow&starBit > 0 {		return domMatch && dowMatch	}	return domMatch || dowMatch}
 |