package cronimport// SpecSchedule specifies a duty cycle (to the second granularity), based on a// traditional crontab specification. It is computed initially and stored as bit sets.typeSpecSchedulestruct { Second, Minute, Hour, Dom, Month, Dow uint64// 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}// 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 ( *SpecSchedule) ( 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)// 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. := .Location() := .Locationif == time.Local { = .Location() }if .Location != time.Local { = .In(.Location) }// Start at the earliest possible time (the upcoming second). = .Add(1*time.Second - time.Duration(.Nanosecond())*time.Nanosecond)// This flag indicates whether a field has been incremented. := false// If no time is found within five years, return zero. := .Year() + 5:if .Year() > {returntime.Time{} }// Find the first applicable month. // If it's this month, then do nothing.for1<<uint(.Month())&.Month == 0 {// If we have to add a month, reset the other parts to 0.if ! { = true// Otherwise, set the date at the beginning (since the current time is irrelevant). = time.Date(.Year(), .Month(), 1, 0, 0, 0, 0, ) } = .AddDate(0, 1, 0)// Wrapped around.if .Month() == time.January {goto } }// Now get a day in that month. // // NOTE: This causes issues for daylight savings regimes where midnight does // not exist. For example: Sao Paulo has DST that transforms midnight on // 11/3 into 1am. Handle that by noticing when the Hour ends up != 0.for !dayMatches(, ) {if ! { = true = time.Date(.Year(), .Month(), .Day(), 0, 0, 0, 0, ) } = .AddDate(0, 0, 1)// Notice if the hour is no longer midnight due to DST. // Add an hour if it's 23, subtract an hour if it's 1.if .Hour() != 0 {if .Hour() > 12 { = .Add(time.Duration(24-.Hour()) * time.Hour) } else { = .Add(time.Duration(-.Hour()) * time.Hour) } }if .Day() == 1 {goto } }for1<<uint(.Hour())&.Hour == 0 {if ! { = true = time.Date(.Year(), .Month(), .Day(), .Hour(), 0, 0, 0, ) } = .Add(1 * time.Hour)if .Hour() == 0 {goto } }for1<<uint(.Minute())&.Minute == 0 {if ! { = true = .Truncate(time.Minute) } = .Add(1 * time.Minute)if .Minute() == 0 {goto } }for1<<uint(.Second())&.Second == 0 {if ! { = true = .Truncate(time.Second) } = .Add(1 * time.Second)if .Second() == 0 {goto } }return .In()}// dayMatches returns true if the schedule's day-of-week and day-of-month// restrictions are satisfied by the given time.func dayMatches( *SpecSchedule, time.Time) bool {var (bool = 1<<uint(.Day())&.Dom > 0bool = 1<<uint(.Weekday())&.Dow > 0 )if .Dom&starBit > 0 || .Dow&starBit > 0 {return && }return || }
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.