curl2info/crontab.go
2018-12-05 14:51:36 +08:00

217 lines
4.6 KiB
Go

package curl2info
import (
"errors"
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"
"github.com/davecgh/go-spew/spew"
)
type timePointer struct {
left, right int
leftlimit, rightlimit int
per int
isAll bool
}
func (tp *timePointer) String() string {
return fmt.Sprintf("left: %d, right: %d, leftlimit: %d, rightlimit: %d, per: %d", tp.left, tp.right, tp.leftlimit, tp.rightlimit, tp.per)
}
// Crontab 的string解析
type Crontab struct {
Min []timePointer
Hour []timePointer
Day []timePointer
Month []timePointer
Week []timePointer
WillPlans []time.Time
SkipPlans []time.Time
YearPlan *TrieYear
}
// NewCrontab create 一个crontab
func NewCrontab(crontab string) *Crontab {
cron := &Crontab{}
cron.FromString(crontab)
return cron
}
func (cron *Crontab) String() string {
return fmt.Sprintf("min:%s\nhour:%s\nday:%s\nmonth:%s\nweek:%s\n", spew.Sdump(cron.Min), spew.Sdump(cron.Hour), spew.Sdump(cron.Day), spew.Sdump(cron.Month), spew.Sdump(cron.Week))
}
// FromString 解析crontab 的 表达式
func (cron *Crontab) FromString(crontab string) error {
crontab = strings.TrimSpace(crontab)
matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1)
mlen := len(matches)
switch mlen {
case 5:
cron.Min = createTimePointer(matches[0], 0, 59, true)
cron.Hour = createTimePointer(matches[1], 0, 23, true)
cron.Day = createTimePointer(matches[2], 1, 31, false)
cron.Month = createTimePointer(matches[3], 1, 12, true)
cron.Week = createTimePointer(matches[4], 0, 6, true)
cron.CreateYearPlan()
default:
return errors.New("mathches len != want, check crontab string")
}
return nil
}
// CreateYearPlan 创建年度计划
func (cron *Crontab) CreateYearPlan() {
cron.YearPlan = newTrieYear()
cron.YearPlan.FromCrontab(cron)
}
// TimeUp 是否时间快到
func (cron *Crontab) TimeUp(now time.Time) bool {
maxlen := 1000
createlen := 500
plen := len(cron.WillPlans)
if plen <= createlen {
var lastplan time.Time
if plen == 0 {
lastplan = now
} else {
lastplan = cron.WillPlans[plen-1].Add(time.Minute)
}
if !cron.YearPlan.CheckYear() {
cron.CreateYearPlan()
}
timeplans := cron.YearPlan.GetPlanTime(cron, lastplan, uint(maxlen-plen))
cron.WillPlans = append(cron.WillPlans, timeplans...)
}
if len(cron.WillPlans) > 0 {
istimeup := false
for i := 0; i < maxlen; i++ {
if now.Unix() >= cron.WillPlans[i].Unix() {
istimeup = true
} else {
if istimeup {
if i-1 > 0 {
cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans[0:i-1]...)
if len(cron.SkipPlans) >= maxlen+200 {
cron.SkipPlans = cron.SkipPlans[200:]
}
}
cron.WillPlans = cron.WillPlans[i:]
return istimeup
}
return istimeup
}
}
cron.SkipPlans = cron.WillPlans
cron.WillPlans = nil
return istimeup
}
log.Panicln("error willplans range")
return false
}
func createTimePointer(min string, llimit, rlimit int, fixedLeftRight bool) []timePointer {
var result []timePointer
exelist := strings.Split(min, ",")
for _, exe := range exelist {
tp := timePointer{}
takeper := strings.Split(exe, "/") // per
var rangevalue, per string
if len(takeper) == 1 {
rangevalue = exe
per = "1"
} else {
rangevalue = takeper[0]
per = takeper[1]
}
// takeRange
be := strings.Split(rangevalue, "-")
var left, rigth string
switch len(be) {
case 1:
left = be[0]
rigth = be[0]
case 2:
left = be[0]
rigth = be[1]
default:
panic(errors.New("range value is > 2"))
}
if left == "*" {
tp.left = llimit
} else {
ileft, err := strconv.Atoi(strings.Replace(left, "^", "-", -1))
if err != nil {
panic(err)
}
tp.left = ileft
}
if rigth == "*" {
tp.right = rlimit
} else {
iright, err := strconv.Atoi(strings.Replace(rigth, "^", "-", -1))
if err != nil {
panic(err)
}
tp.right = iright
}
iper, err := strconv.Atoi(per)
if err != nil {
panic(err)
}
tp.per = iper
tp.leftlimit = llimit
tp.rightlimit = rlimit
// 修正左值
leftfixed := tp.left
if leftfixed < 0 {
leftfixed += tp.rightlimit + 1
if fixedLeftRight {
tp.left = leftfixed
}
}
rightfixed := tp.right
if rightfixed < 0 {
rightfixed += tp.rightlimit + 1
if fixedLeftRight {
tp.right = rightfixed
}
}
// 全部符合 当左等于左 且 右等于右最大 并且 per == 1 TODO: 如果加入时间间隔 就需要 加多一个 附加值
if leftfixed == tp.leftlimit && rightfixed == tp.rightlimit && tp.per == 1 {
tp.isAll = true
}
result = append(result, tp)
}
return result
}