support cron
This commit is contained in:
parent
c1295d9e26
commit
d41b1134c9
180
cron.go
180
cron.go
@ -2,10 +2,80 @@ package startimer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func parseCron(cron string) (StarTimer, error) {
|
||||
var rpmonths = map[string]int{
|
||||
"JAN": 1,
|
||||
"FEB": 2,
|
||||
"MAR": 3,
|
||||
"APR": 4,
|
||||
"MAY": 5,
|
||||
"JUN": 6,
|
||||
"JUL": 7,
|
||||
"AUG": 8,
|
||||
"SEP": 9,
|
||||
"OCT": 10,
|
||||
"NOV": 11,
|
||||
"DEC": 12,
|
||||
}
|
||||
|
||||
var rpweekdays = map[string]int{
|
||||
"SUN": 0,
|
||||
"MON": 1,
|
||||
"TUE": 2,
|
||||
"WED": 3,
|
||||
"THU": 4,
|
||||
"FRI": 5,
|
||||
"SAT": 6,
|
||||
}
|
||||
|
||||
func NewTimerWithCron(cron ...string) (StarTimer, error) {
|
||||
tmr := StarTimer{
|
||||
base: time.Now(),
|
||||
nextDate: time.Time{},
|
||||
mu: new(sync.RWMutex),
|
||||
}
|
||||
for _, c := range cron {
|
||||
c = "0 " + c
|
||||
rpt, err := parseCron(c)
|
||||
if err != nil {
|
||||
return tmr, err
|
||||
}
|
||||
tmr.repeat = append(tmr.repeat, rpt)
|
||||
}
|
||||
return tmr, nil
|
||||
}
|
||||
|
||||
func NewTimerWithSecCron(cron ...string) (StarTimer, error) {
|
||||
tmr := StarTimer{
|
||||
base: time.Now(),
|
||||
nextDate: time.Time{},
|
||||
mu: new(sync.RWMutex),
|
||||
}
|
||||
for _, c := range cron {
|
||||
rpt, err := parseCron(c)
|
||||
if err != nil {
|
||||
return tmr, err
|
||||
}
|
||||
tmr.repeat = append(tmr.repeat, rpt)
|
||||
}
|
||||
return tmr, nil
|
||||
}
|
||||
|
||||
func parseCron(cron string) (*Repeats, error) {
|
||||
{
|
||||
cron = strings.ToUpper(cron)
|
||||
for k, v := range rpmonths {
|
||||
cron = strings.ReplaceAll(cron, k, strconv.Itoa(v))
|
||||
}
|
||||
for k, v := range rpweekdays {
|
||||
cron = strings.ReplaceAll(cron, k, strconv.Itoa(v))
|
||||
}
|
||||
}
|
||||
for {
|
||||
oldLen := len(cron)
|
||||
cron = strings.ReplaceAll(strings.TrimSpace(cron), " ", " ")
|
||||
@ -15,8 +85,110 @@ func parseCron(cron string) (StarTimer, error) {
|
||||
}
|
||||
ct := strings.Split(cron, " ")
|
||||
if len(ct) != 6 {
|
||||
return StarTimer{}, errors.New("Invalid cron,argument not enough")
|
||||
return nil, errors.New("Invalid cron,argument not enough")
|
||||
}
|
||||
|
||||
return StarTimer{}, nil
|
||||
|
||||
foundFirstAll := false
|
||||
var myMap = make(map[Unit]map[uint32]Repeat)
|
||||
for idx, c := range ct {
|
||||
if _, ok := myMap[Unit(idx)]; !ok {
|
||||
myMap[Unit(idx)] = make(map[uint32]Repeat)
|
||||
}
|
||||
var minVal, maxVal int
|
||||
switch Unit(idx) {
|
||||
case STAR_MINUTE, STAR_SECOND:
|
||||
minVal = 0
|
||||
maxVal = 60
|
||||
case STAR_HOUR:
|
||||
minVal = 0
|
||||
maxVal = 24
|
||||
case STAR_DAY:
|
||||
minVal = 1
|
||||
maxVal = 32
|
||||
case STAR_MONTH:
|
||||
minVal = 1
|
||||
maxVal = 13
|
||||
case STAR_WEEK:
|
||||
minVal = 0
|
||||
maxVal = 7
|
||||
}
|
||||
|
||||
cdt := strings.Split(c, ",")
|
||||
for _, dtl := range cdt {
|
||||
dtl = strings.TrimSpace(dtl)
|
||||
if dtl == "*" {
|
||||
if !foundFirstAll {
|
||||
foundFirstAll = true
|
||||
if maxVal > 0 {
|
||||
for i := minVal; i < maxVal; i++ {
|
||||
val := uint32(i)
|
||||
myMap[Unit(idx)][val] = Repeat{
|
||||
Unit: Unit(idx),
|
||||
Value: val,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.Contains(dtl, "*/") {
|
||||
num, err := strconv.Atoi(strings.TrimPrefix(dtl, "*/"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := minVal; i < maxVal; i++ {
|
||||
val := i
|
||||
if Unit(idx) == STAR_DAY || Unit(idx) == STAR_MONTH {
|
||||
val--
|
||||
}
|
||||
if val%num == 0 {
|
||||
myMap[Unit(idx)][uint32(val)] = Repeat{
|
||||
Unit: Unit(idx),
|
||||
Value: uint32(i),
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.Contains(dtl, "-") {
|
||||
numbers := strings.Split(dtl, "-")
|
||||
if len(numbers) != 2 {
|
||||
return nil, errors.New("Invalid Cron")
|
||||
}
|
||||
startNum, err := strconv.Atoi(numbers[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endNum, err := strconv.Atoi(numbers[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if startNum < minVal && endNum >= maxVal {
|
||||
return nil, errors.New("Invalid Cron")
|
||||
}
|
||||
for i := startNum; i <= endNum; i++ {
|
||||
myMap[Unit(idx)][uint32(i)] = Repeat{
|
||||
Unit: Unit(idx),
|
||||
Value: uint32(i),
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
number, err := strconv.Atoi(dtl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
myMap[Unit(idx)][uint32(number)] = Repeat{
|
||||
Unit: Unit(idx),
|
||||
Value: uint32(number),
|
||||
}
|
||||
}
|
||||
}
|
||||
var repeat Repeats
|
||||
for _, v := range myMap {
|
||||
for _, rpt := range v {
|
||||
repeat.Repeat = append(repeat.Repeat, rpt)
|
||||
}
|
||||
}
|
||||
return &repeat, nil
|
||||
}
|
||||
|
44
time_test.go
44
time_test.go
@ -151,3 +151,47 @@ func TestPrepareCronSimple(t *testing.T) {
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidCron(t *testing.T) {
|
||||
tk := StarTimer{
|
||||
base: time.Now(),
|
||||
repeat: []*Repeats{
|
||||
{
|
||||
Every: false,
|
||||
Repeat: []Repeat{
|
||||
{Unit: STAR_MINUTE, Value: 0},
|
||||
{Unit: STAR_HOUR, Value: 2},
|
||||
{Unit: STAR_DAY, Value: 29}, {Unit: STAR_DAY, Value: 30}, {Unit: STAR_DAY, Value: 31},
|
||||
{Unit: STAR_MONTH, Value: 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
base := tk.base
|
||||
for i := 0; i < 10; i++ {
|
||||
base = tk.parseNextDate(base, true)
|
||||
fmt.Println(base)
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCronMax(t *testing.T) {
|
||||
tk, err := NewTimerWithSecCron("*/5 40-59 * * * *")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk.AddTask(func() {
|
||||
fmt.Println("现在是", time.Now())
|
||||
})
|
||||
for i := 0; i < 20; i++ {
|
||||
fmt.Println()
|
||||
}
|
||||
/*
|
||||
err = tk.Run()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second * 300)
|
||||
|
||||
*/
|
||||
}
|
||||
|
28
timer.go
28
timer.go
@ -134,6 +134,9 @@ func (t *StarTimer) ImportRepeats(r string) error {
|
||||
func (t *StarTimer) BaseDate() time.Time {
|
||||
return t.base
|
||||
}
|
||||
func (t *StarTimer) SetBaseDate(date time.Time) {
|
||||
t.base = date
|
||||
}
|
||||
|
||||
func (t *StarTimer) ResetWithRepeat(base time.Time, repeat []*Repeats) error {
|
||||
t.Stop()
|
||||
@ -142,6 +145,10 @@ func (t *StarTimer) ResetWithRepeat(base time.Time, repeat []*Repeats) error {
|
||||
return t.Run()
|
||||
}
|
||||
|
||||
func (t *StarTimer) Repeats() []*Repeats {
|
||||
return t.repeat
|
||||
}
|
||||
|
||||
func (t *StarTimer) Run() error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
@ -192,7 +199,7 @@ func (t *StarTimer) parseNextDate(base time.Time, isMock bool) time.Time {
|
||||
if d.Every {
|
||||
dates = append(dates, t.parseEveryNextDate(base, d, isMock)...)
|
||||
} else {
|
||||
dates = append(dates, t.parseStaticNextDate(base, d))
|
||||
dates = append(dates, t.parseStaticNextDate(base, d, false))
|
||||
}
|
||||
}
|
||||
sort.SliceStable(dates, func(i, j int) bool {
|
||||
@ -215,7 +222,8 @@ func (t *StarTimer) parseNextDate(base time.Time, isMock bool) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats, recall bool) time.Time {
|
||||
base = time.Date(base.Year(), base.Month(), base.Day(), base.Hour(), base.Minute(), base.Second(), 0, base.Location())
|
||||
var targets []time.Time
|
||||
var uniqueRepeat [][]Repeat
|
||||
selectMap := make(map[Unit][]Repeat)
|
||||
@ -249,7 +257,8 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
return task[i].Unit < task[j].Unit
|
||||
})
|
||||
target := base
|
||||
if !r.Every { //固定日期
|
||||
veryDay, veryMonth := 0, 0 //验证日期
|
||||
if !r.Every { //固定日期
|
||||
for _, d := range task {
|
||||
switch d.Unit {
|
||||
case STAR_SECOND:
|
||||
@ -271,6 +280,7 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
}
|
||||
target = target.Add(time.Hour * time.Duration(sub))
|
||||
case STAR_DAY:
|
||||
veryDay = int(d.Value)
|
||||
sub := int(d.Value) - target.Day()
|
||||
if sub >= 0 {
|
||||
target = target.Add(time.Hour * 24 * time.Duration(sub))
|
||||
@ -278,6 +288,7 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
}
|
||||
target = time.Date(target.Year(), target.Month()+1, int(d.Value), target.Hour(), target.Minute(), target.Second(), 0, target.Location())
|
||||
case STAR_MONTH:
|
||||
veryMonth = int(d.Value)
|
||||
sub := int(d.Value) - int(target.Month())
|
||||
if sub < 0 {
|
||||
sub += 12
|
||||
@ -292,8 +303,10 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if target == base {
|
||||
if (veryDay != 0 && target.Day() != veryDay) || (veryMonth != 0 && int(target.Month()) != veryMonth) {
|
||||
continue
|
||||
}
|
||||
if target == base && !recall {
|
||||
continue
|
||||
}
|
||||
targets = append(targets, target)
|
||||
@ -303,7 +316,7 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
return targets[i].UnixNano() < targets[j].UnixNano()
|
||||
})
|
||||
for k, v := range targets {
|
||||
if v.After(base) {
|
||||
if v.UnixNano() > base.UnixNano() || (recall && v.UnixNano() == base.UnixNano()) {
|
||||
targets = targets[k:]
|
||||
break
|
||||
}
|
||||
@ -315,7 +328,8 @@ func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
return targets[0]
|
||||
}
|
||||
}
|
||||
return t.parseStaticNextDate(targets[0].Add(time.Hour*24), r)
|
||||
nextBase := time.Date(targets[0].Year(), targets[0].Month(), targets[0].Day(), 0, 0, 0, 0, targets[0].Location())
|
||||
return t.parseStaticNextDate(nextBase.Add(time.Hour*24), r, true)
|
||||
}
|
||||
}
|
||||
if len(targets) > 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user