summaryrefslogtreecommitdiffstats
path: root/modules/cron/spec.go
diff options
context:
space:
mode:
authorUnknown <joe2010xtmf@163.com>2014-06-13 13:01:52 -0400
committerUnknown <joe2010xtmf@163.com>2014-06-13 13:01:52 -0400
commitd952b4200c7c024ff5a809a3885bc79be9e4cce9 (patch)
treeb106d81f51a10144749e20c923713fd2c78643e4 /modules/cron/spec.go
parente07674bff19dcc321a1611a3598d69c418ac8642 (diff)
downloadgitea-d952b4200c7c024ff5a809a3885bc79be9e4cce9.tar.gz
gitea-d952b4200c7c024ff5a809a3885bc79be9e4cce9.zip
Add corn rask monitor panel
Diffstat (limited to 'modules/cron/spec.go')
-rw-r--r--modules/cron/spec.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/modules/cron/spec.go b/modules/cron/spec.go
new file mode 100644
index 0000000000..cb3743325d
--- /dev/null
+++ b/modules/cron/spec.go
@@ -0,0 +1,161 @@
+package cron
+
+import (
+ "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() + 5
+
+WRAP:
+ 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
+}