Compare commits

...

23 Commits

Author SHA1 Message Date
huangsimin
f83fd9f635 Merge branch 'master' of http://474420502.top/eson/crontabex 2019-01-04 18:25:18 +08:00
huangsimin
0f83cb5e66 调整正确的统计让第一次直接执行 2019-01-04 18:24:41 +08:00
9d57144934 修改返回的时间更客观 2019-01-02 03:03:28 +08:00
b54c0cf45c 添加时间 2019-01-02 02:03:58 +08:00
d148ea2445 add uid print 2019-01-02 01:59:34 +08:00
7342ef6605 暂停开发新表达式 2018-12-27 04:13:36 +08:00
huangsimin
cc9071bc70 save 2018-12-26 19:10:53 +08:00
5d7d9fc547 有错 2018-12-26 05:11:46 +08:00
huangsimin
d9a422dfec 添加新的条件表达式 2018-12-25 19:46:02 +08:00
huangsimin
40683dc466 添加注释, 和日志打印 2018-12-25 10:17:15 +08:00
b7f0209cc3 fix: crontab setstatus force, forget this: crontab.force = force 2018-12-22 04:35:16 +08:00
ed2dfe0809 修改条件表达式的表达方式 2018-12-22 04:04:12 +08:00
ed4ddb00ed 修改crontab控制的表达式字符 前缀 t, T -> s, S 代表Success, '?' -> '>' 代表 大于等于 2018-12-22 03:42:38 +08:00
f8e7d16241 fix: 少加两个base文件 2018-12-22 02:29:02 +08:00
74fddef18d 添加Force强制Sleep, 和 重新覆盖crontab计划.
把原来SetLastStaus改为一个多元化的设置方式
2018-12-22 02:09:31 +08:00
1140087c45 fix first Call NextTime 2018-12-21 23:30:21 +08:00
2904c8f38c 测试复杂的时间计划成功 2018-12-21 22:51:52 +08:00
0dce1192d2 TODO: 修复时间控制的bug 2018-12-21 04:18:40 +08:00
huangsimin
19a4034928 TODO: list insert error 2018-12-20 18:46:41 +08:00
huangsimin
539e764c08 TODO: bug list 2018-12-20 18:42:18 +08:00
huangsimin
cc37d62a2d test fail time 2018-12-20 14:37:37 +08:00
316d616419 fix 2018-12-07 04:42:53 +08:00
huangsimin
f3702f9df0 fix 2018-12-07 03:02:19 +08:00
6 changed files with 592 additions and 265 deletions

1
condition.go Normal file
View File

@ -0,0 +1 @@
package crontab

View File

@ -4,46 +4,49 @@ import (
"errors"
"fmt"
"log"
"reflect"
"regexp"
"strconv"
"strings"
"time"
clinked "474420502.top/eson/structure/circular_linked"
"github.com/satori/go.uuid"
"474420502.top/eson/structure/circular_linked"
"github.com/Pallinder/go-randomdata"
"github.com/davecgh/go-spew/spew"
)
type randLR struct {
left, right int
// StatusType 设置状态的类型
type StatusType int
const (
_ StatusType = iota
// SExecuteOK 设置这个次成功或者失败的状态
SExecuteOK
// SExecuteSleep 设置下次Sleep时间, 只影响一次
SExecuteSleep
// SExecuteCrontab 设置改写Crontab, 覆盖以前的Crontab
SExecuteCrontab
)
// Force 强制下次执行
type Force struct {
sleep time.Duration
}
type hInterval struct {
PlanFail []randLR
PlanNormal []randLR
Count int
ConstCount int
}
func (interval *hInterval) reset() {
interval.Count = interval.ConstCount
}
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)
// NextTime 下次执行时间
func (force *Force) NextTime() time.Time {
return time.Now().Add(force.sleep)
}
// Crontab 的string解析
type Crontab struct {
crontab string
uid uuid.UUID
force *Force
min []timePointer
hour []timePointer
day []timePointer
@ -55,35 +58,76 @@ type Crontab struct {
YearPlan *trieYear
interval *clinked.CircularLinked
lastStatus bool
nextTime time.Time
interval *clinked.CircularLinked
lastStatus bool
isCalculated bool
trueCount int
failCount int
nextTime time.Time
}
// NewCrontab create 一个crontab
func NewCrontab(crontab string) *Crontab {
cron := &Crontab{}
cron.FromString(crontab)
cron.crontab = strings.TrimSpace(crontab)
cron.FromString(cron.crontab)
return cron
}
// SetStatus 设置状态 接口定义
func (cron *Crontab) SetStatus(status interface{}) {
if cron.interval != nil {
cron.lastStatus = status.(bool)
// UnmarshalYAML 添加序列化接口 Marshal没实现, 需要的时候可以自己添加
func (cron *Crontab) UnmarshalYAML(unmarshal func(interface{}) error) error {
var buf string
err := unmarshal(&buf)
if err != nil {
return nil
}
}
// GetStatus 设置状态 接口定义
func (cron *Crontab) GetStatus() (status interface{}) {
if cron.interval != nil {
return cron.lastStatus
if err := cron.FromString(buf); err != nil {
return err
}
return nil
}
// TimeUp 是否时间快到
// SetStatus 设置上次状态 true false
func (cron *Crontab) SetStatus(statusType StatusType, statusValue ...interface{}) {
switch statusType {
case SExecuteOK:
if cron.interval != nil {
cron.lastStatus = statusValue[0].(bool)
}
case SExecuteSleep:
force := new(Force)
ivalue := statusValue[0]
switch value := ivalue.(type) {
case int:
force.sleep = time.Duration(value) * time.Second
case int64:
force.sleep = time.Duration(value) * time.Second
case time.Duration:
force.sleep = value
default:
panic(errors.New("statusValue type is error, the type is" + reflect.TypeOf(ivalue).String()))
}
cron.force = force
case SExecuteCrontab:
crontab := statusValue[0].(string)
cron.crontab = strings.TrimSpace(crontab)
cron.FromString(cron.crontab)
default:
panic(errors.New("StatusType is unknown, the type " + reflect.TypeOf(statusType).String()))
}
}
// // GetStatus 获取上次状态 true false
// func (cron *Crontab) GetStatus() (status bool) {
// return cron.lastStatus
// }
// TimeUp 是否时间快到, 时间间隔调用完TimeUp后必须调用NextTime, 为了精准控制时间
func (cron *Crontab) TimeUp() bool {
if cron.interval != nil {
@ -94,16 +138,27 @@ func (cron *Crontab) TimeUp() bool {
}
// NextTime 返回下次任务的时间
func (cron *Crontab) NextTime() *time.Time {
func (cron *Crontab) NextTime() time.Time {
if cron.force != nil {
nt := cron.force.NextTime()
cron.force = nil
return nt
}
if cron.interval != nil {
return &cron.nextTime
if !cron.isCalculated {
now := time.Now()
cron.intervalCalculateNextTime(now)
}
return cron.nextTime
}
if len(cron.WillPlans) > 0 {
return &cron.WillPlans[0]
return cron.WillPlans[0]
}
return nil
return time.Now().Add(time.Second * 2)
}
func (cron *Crontab) String() string {
@ -112,15 +167,36 @@ func (cron *Crontab) String() string {
// FromString 解析crontab 的 表达式
func (cron *Crontab) FromString(crontab string) error {
crontab = strings.TrimSpace(crontab)
crontab = cron.crontab
uid, err := uuid.NewV4()
if err != nil {
panic(err)
}
cron.uid = uid
cron.interval = nil
cron.min = nil
cron.hour = nil
cron.day = nil
cron.month = nil
cron.week = nil
cron.WillPlans = nil
cron.SkipPlans = nil
cron.YearPlan = nil
matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1)
mlen := len(matches)
switch mlen {
case 1:
// "f1-2|5-10x5,f1|10m,10-15,f1"
cron.nextTime = time.Now()
cron.lastStatus = true
cron.isCalculated = true
cron.nextTime = time.Now()
cron.interval = clinked.NewCircularLinked()
var intervalList []interface{}
intervalList = parseIntervalString(matches[0])
@ -134,6 +210,7 @@ func (cron *Crontab) FromString(crontab string) error {
cron.week = createTimePointer(matches[4], 0, 6, true)
cron.createYearPlan()
cron.TimeUp()
default:
return errors.New("mathches len != want, check crontab string")
}
@ -153,7 +230,7 @@ func (cron *Crontab) linuxTimeUp() bool {
createlen := 500
plen := len(cron.WillPlans)
if plen <= createlen {
if plen <= createlen { // 如果当前生成的计划表少于 限制的500. 就生成新的计划表
var lastplan time.Time
if plen == 0 {
lastplan = now
@ -171,9 +248,13 @@ func (cron *Crontab) linuxTimeUp() bool {
if len(cron.WillPlans) > 0 {
istimeup := false
for i := 0; i < maxlen; i++ {
// 统计过了多少计划任务时间表 i - 1
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]...)
@ -188,7 +269,7 @@ func (cron *Crontab) linuxTimeUp() bool {
return istimeup
}
}
// 如果所有计划表都不符合, 全部放到忽略的计划表上, 这个表方便打印查看, 因为程序执行超时过了多少计划任务
cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans...)
cron.WillPlans = nil
return istimeup
@ -198,215 +279,73 @@ func (cron *Crontab) linuxTimeUp() bool {
return false
}
// IntervalCalculateNextTime 计算时间间隔的下次时间
func (cron *Crontab) intervalCalculateNextTime(now time.Time) {
iv := cron.interval.Cursor().GetValue().(*hInterval)
isecond := 0
if iv.PlanFailCount.Size() == 0 && len(iv.PlanFail) == 0 {
cron.lastStatus = true
}
if cron.lastStatus {
cron.trueCount++
cron.failCount = 0
isecond = intervalPriorityListISecond(&iv.PlanTrueCount, cron.trueCount)
if isecond == -1 {
if len(iv.PlanTrue) > 0 {
idx := randomdata.Number(len(iv.PlanTrue))
lr := iv.PlanTrue[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
} else {
isecond = 0
}
}
fmt.Println(time.Now().Format("01-02 15:04:05"), cron.uid.String(), "success:", cron.trueCount, " wait:", isecond)
} else {
cron.failCount++
cron.trueCount = 0
isecond = intervalPriorityListISecond(&iv.PlanFailCount, cron.failCount)
if isecond == -1 {
if len(iv.PlanFail) > 0 {
idx := randomdata.Number(len(iv.PlanFail))
lr := iv.PlanFail[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
} else {
isecond = 0
}
}
fmt.Println(time.Now().Format("01-02 15:04:05"), cron.uid.String(), "fail:", cron.failCount, " wait:", isecond)
}
iv.Count--
if iv.Count <= 0 {
iv.reset()
cron.interval.MoveNext()
}
cron.isCalculated = true
cron.nextTime = now.Add(time.Duration(isecond) * time.Second)
}
func (cron *Crontab) intervalTimeUp() bool {
now := time.Now()
if now.Unix() >= cron.nextTime.Unix() {
iv := cron.interval.Cursor().GetValue().(hInterval)
isecond := 0
if cron.lastStatus == false && len(iv.PlanFail) > 0 {
idx := randomdata.Number(len(iv.PlanFail))
lr := iv.PlanFail[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
} else {
idx := randomdata.Number(len(iv.PlanNormal))
lr := iv.PlanNormal[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
if cron.isCalculated { // 需要调用nexttime()才能正常正常计算下次的时间
now := time.Now()
if now.Unix() >= cron.nextTime.Unix() {
cron.isCalculated = false
return true
}
iv.Count--
if iv.Count <= 0 {
iv.reset()
cron.interval.MoveNext()
}
cron.nextTime = now.Add(time.Duration(isecond) * time.Second)
return true
}
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
}
func parseIntervalString(crontab string) []interface{} {
var result []interface{}
values := strings.Split(crontab, ",")
for _, value := range values {
interval := hInterval{}
// 次数
valuesCounts := strings.Split(value, "x")
switch len(valuesCounts) {
case 1:
interval.ConstCount = 1
case 2:
count, err := strconv.Atoi(valuesCounts[1])
if err != nil {
panic(err)
}
interval.ConstCount = count
default:
panic("valuesCounts error, the len is not in range")
}
// 统计失败与普通间隔值的数组
failAndNormal := valuesCounts[0]
valuesFN := strings.Split(failAndNormal, "|")
for _, FN := range valuesFN {
if FN == "" {
continue
}
switch FN[0] {
case 'f', 'F':
fvalue := FN[1:]
interval.PlanFail = append(interval.PlanFail, parseRandLR(fvalue))
case 'n':
value := FN[1:]
interval.PlanNormal = append(interval.PlanNormal, parseRandLR(value))
default:
interval.PlanNormal = append(interval.PlanNormal, parseRandLR(FN))
}
}
interval.reset()
result = append(result, interval)
}
return result
}
func parseRandLR(value string) randLR {
vlen := len(value)
lastchar := value[vlen-1]
lr := strings.Split(value, "-")
switch len(lr) {
case 1:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[0], lastchar)}
return lr
case 2:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[1], lastchar)}
return lr
default:
panic("lr is error")
}
}
func getInt(v string) int {
vint, err := strconv.Atoi(v)
if err != nil {
panic(err)
}
return vint
}
func parseTimeValue(v string, lastchar byte) int {
vlen := len(v)
switch lastchar {
case 's':
return getInt(v[:vlen-1])
case 'm':
return getInt(v[:vlen-1]) * 60
case 'h':
return getInt(v[:vlen-1]) * 3600
case 'd':
return getInt(v[:vlen-1]) * 3600 * 24
default:
return getInt(v)
}
}

View File

@ -2,9 +2,12 @@ package crontab
import (
"fmt"
"log"
"runtime"
"testing"
"time"
"github.com/satori/go.uuid"
)
// type LRValue struct {
@ -26,8 +29,8 @@ func TestParseCrontab(t *testing.T) {
}
cron.createYearPlan()
if !cron.TimeUp() {
t.Error("timeup error")
if cron.TimeUp() {
t.Error("timeup error", cron.WillPlans[0:2])
}
PrintMemUsage()
@ -36,13 +39,13 @@ func TestParseCrontab(t *testing.T) {
func TestParseInterval(t *testing.T) {
//crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围
//crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f1"
crontab := "2"
//crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f22?15-12"
crontab := "2s"
cron := NewCrontab(crontab)
now := time.Now()
for i := 0; i <= 6; i++ {
for i := 0; i <= 3; i++ {
log.Println(cron.NextTime())
if cron.TimeUp() {
sec := time.Since(now).Seconds()
if i != 0 {
if 2.0 <= sec && sec <= 2.1 {
@ -58,13 +61,85 @@ func TestParseInterval(t *testing.T) {
}
}
func isInRangeTime(sec, t float64) error {
if sec >= t-0.05 && sec <= t+0.05 {
return nil
}
return fmt.Errorf("计算错误 范围 %f-%f interval time is %f", t-0.05, t+0.05, sec)
}
func TestParseIntervalOnline1(t *testing.T) {
uid, _ := uuid.NewV4()
log.Println(string(uid.Bytes()))
crontab := "f[c > 2]=3|s[c <= 2]"
log.Println(crontab)
}
func TestParseIntervalPlus(t *testing.T) {
// crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围
// crondata := NewCrontab("*22 * * * *")
//crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围
//crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f1"
//crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f1" ^代表往后算 ^5 2月份就代表 28-4=24 f代表失败后1-2秒设置 由SetStatus控制 t 相反默认不写前缀为t
log.SetFlags(log.Llongfile)
crontab := "f=2|f2>=3|f3>=4|1|s4>=5"
cron := NewCrontab(crontab)
i := 0
now := time.Now()
lasttime := now
for j := 0; j <= 2500; j++ {
nexttime := cron.NextTime()
if j >= 5 && !nexttime.Equal(lasttime) {
lasttime = nexttime
}
if cron.TimeUp() {
sec := time.Since(now).Seconds()
switch i {
case 0:
case 1:
if err := isInRangeTime(sec, 2.0); err != nil {
t.Error(err.Error(), "status = ", cron.lastStatus)
}
case 2:
if err := isInRangeTime(sec, 3.0); err != nil {
t.Error(err.Error(), "status = ", cron.lastStatus)
}
case 3:
if err := isInRangeTime(sec, 4.0); err != nil {
t.Error(err.Error(), "status = ", cron.lastStatus)
}
case 5:
if err := isInRangeTime(sec, 1); err != nil {
t.Error(err.Error(), "status = ", cron.lastStatus)
}
default:
if i >= 10 {
if err := isInRangeTime(sec, 5.0); err != nil {
t.Error(err.Error(), "status = ", cron.lastStatus)
}
}
}
if i <= 3 {
cron.SetStatus(SExecuteOK, false)
} else {
cron.SetStatus(SExecuteOK, true)
}
now = time.Now()
i++
}
time.Sleep(time.Millisecond * 10)
}
}
func PrintMemUsage() {

205
interval_base.go Normal file
View File

@ -0,0 +1,205 @@
package crontab
import (
"errors"
"strconv"
"strings"
"474420502.top/eson/structure/priority_list"
randomdata "github.com/Pallinder/go-randomdata"
)
type randLR struct {
left, right int
}
// NodeCount 用于priority_list
type NodeCount struct {
plist.Node
randLR
}
type hInterval struct {
PlanFailCount plist.PriorityList
PlanTrueCount plist.PriorityList
PlanFail []randLR
PlanTrue []randLR
Count int
ConstCount int
}
func (interval *hInterval) reset() {
interval.Count = interval.ConstCount
}
// Compare NodeCount比较函数
func (rlr *NodeCount) Compare(v plist.INode) bool {
return rlr.GetValue().(int) > v.GetValue().(int)
}
func parseIntervalFail(interval *hInterval, FN string) {
scharIndex := strings.Index(FN, ">")
if scharIndex != -1 {
if FN[scharIndex+1] != '=' {
panic(errors.New("= is not exist"))
}
fc := FN[0:scharIndex]
flr := FN[scharIndex+2:]
node := new(NodeCount)
node.SetValue(getInt(fc[1:]))
node.randLR = parseRandLR(flr)
interval.PlanFailCount.Insert(node)
} else {
if FN[1] != '=' {
panic(errors.New("= is not exist"))
}
fvalue := FN[2:]
interval.PlanFail = append(interval.PlanFail, parseRandLR(fvalue))
}
}
func parseIntervalSuccess(interval *hInterval, FN string) {
scharIndex := strings.Index(FN, ">")
if scharIndex != -1 {
if FN[scharIndex+1] != '=' {
panic(errors.New("= is not exist"))
}
tc := FN[0:scharIndex]
tlr := FN[scharIndex+2:]
node := new(NodeCount)
node.SetValue(getInt(tc[1:]))
node.randLR = parseRandLR(tlr)
interval.PlanTrueCount.Insert(node)
} else {
if FN[1] != '=' {
panic(errors.New("= is not exist"))
}
tvalue := FN[2:]
interval.PlanTrue = append(interval.PlanTrue, parseRandLR(tvalue))
}
}
func parseIntervalString(crontab string) []interface{} {
var result []interface{}
values := strings.Split(crontab, ",")
for _, value := range values {
interval := &hInterval{}
// 次数
valuesCounts := strings.Split(value, "x")
switch len(valuesCounts) {
case 1:
interval.ConstCount = 1
case 2:
count, err := strconv.Atoi(valuesCounts[1])
if err != nil {
panic(err)
}
interval.ConstCount = count
default:
panic("valuesCounts error, the len is not in range")
}
// 统计失败与普通间隔值的数组
failAndNormal := valuesCounts[0]
valuesFN := strings.Split(failAndNormal, "|")
for _, FN := range valuesFN {
if FN == "" {
continue
}
switch FN[0] {
case 'f', 'F':
parseIntervalFail(interval, FN)
case 's', 'S':
parseIntervalSuccess(interval, FN)
default:
FN = "s=" + FN
parseIntervalSuccess(interval, FN)
}
}
interval.reset()
result = append(result, interval)
}
return result
}
func parseRandLR(lrvalue string) randLR {
vlen := len(lrvalue)
lastchar := lrvalue[vlen-1]
lr := strings.Split(lrvalue, "-")
switch len(lr) {
case 1:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[0], lastchar)}
return lr
case 2:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[1], lastchar)}
return lr
default:
panic("lr is error")
}
}
// intervalPriorityListISecond 获取优先链表比较的值
func intervalPriorityListISecond(planlist *plist.PriorityList, count int) int {
if planlist.Size() > 0 {
node := new(NodeCount)
node.SetValue(count)
iwantNode := planlist.GetCompare(node)
if iwantNode != nil {
wantNode := iwantNode.(*NodeCount)
lr := wantNode.randLR
return randomdata.Number(lr.left, lr.right+1)
}
}
return -1
}
func getInt(v string) int {
vint, err := strconv.Atoi(v)
if err != nil {
panic(err)
}
return vint
}
func parseTimeValue(v string, lastchar byte) int {
vlen := len(v)
switch lastchar {
case 's':
return getInt(v[:vlen-1])
case 'm':
return getInt(v[:vlen-1]) * 60
case 'h':
return getInt(v[:vlen-1]) * 3600
case 'd':
return getInt(v[:vlen-1]) * 3600 * 24
default:
return getInt(v)
}
}

View File

@ -123,7 +123,7 @@ func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint)
for j := 1; j <= 31; j++ {
if nmonth == i {
if j < nday {
if j < nday { // 获取当天的计划表
continue
}
}
@ -132,7 +132,7 @@ func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint)
if day != nil {
if day.IsClear {
insertHour(cron, day)
insertHour(cron, day) // 生成小时的计划表
}
for k := 0; k <= 23; k++ {
@ -144,7 +144,7 @@ func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint)
}
hour := day.Hour[k]
// 遍历24小时 生成需要的计划表
if hour != nil {
for n := 0; n <= 59; n++ {
@ -159,7 +159,7 @@ func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint)
curTime := time.Date(ty.Year, time.Month(i), j, k, n, 0, 0, time.Local)
result = append(result, curTime)
count--
count-- // 统计完当前需要的计划任务后就结束
if count <= 0 {
ty.clearHour()
return result
@ -168,8 +168,8 @@ func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint)
}
}
}
}
}
}
}

107
yearplan_base.go Normal file
View File

@ -0,0 +1,107 @@
package crontab
import (
"errors"
"fmt"
"strconv"
"strings"
)
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)
}
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
if leftfixed == tp.leftlimit && rightfixed == tp.rightlimit && tp.per == 1 {
tp.isAll = true
}
result = append(result, tp)
}
return result
}