improve package gcron

This commit is contained in:
Jack 2020-07-29 23:03:31 +08:00
parent c4b28b0bc4
commit 2f0e5a45c5
2 changed files with 53 additions and 44 deletions

View File

@ -19,7 +19,6 @@ const (
STATUS_RUNNING = gtimer.STATUS_RUNNING
STATUS_STOPPED = gtimer.STATUS_STOPPED
STATUS_CLOSED = gtimer.STATUS_CLOSED
gDEFAULT_TIMES = math.MaxInt32
)

View File

@ -9,6 +9,7 @@ package gcron
import (
"errors"
"fmt"
"github.com/gogf/gf/os/gtime"
"strconv"
"strings"
"time"
@ -16,25 +17,26 @@ import (
"github.com/gogf/gf/text/gregex"
)
// 运行时间管理对象
// cronSchedule is the schedule for cron job.
type cronSchedule struct {
create int64 // 创建时间戳(秒)
every int64 // 运行时间间隔(秒)
pattern string // 原始注册字符串
second map[int]struct{}
minute map[int]struct{}
hour map[int]struct{}
day map[int]struct{}
week map[int]struct{}
month map[int]struct{}
create int64 // Created timestamp.
every int64 // Running interval in seconds.
pattern string // The raw cron pattern string.
second map[int]struct{} // Job can run in these second numbers.
minute map[int]struct{} // Job can run in these minute numbers.
hour map[int]struct{} // Job can run in these hour numbers.
day map[int]struct{} // Job can run in these day numbers.
week map[int]struct{} // Job can run in these week numbers.
month map[int]struct{} // Job can run in these moth numbers.
}
const (
gREGEX_FOR_CRON = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)$`
// regular expression for cron pattern, which contains 6 parts of time units.
gREGEX_FOR_CRON = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,A-Za-z]+)\s+([\-/\d\*\?,A-Za-z]+)$`
)
var (
// 预定义的定时格式
// Predefined pattern map.
predefinedPatternMap = map[string]string{
"@yearly": "0 0 0 1 1 *",
"@annually": "0 0 0 1 1 *",
@ -44,7 +46,7 @@ var (
"@midnight": "0 0 0 * * *",
"@hourly": "0 0 * * * *",
}
// 月份与数字对应表
// Short month name to its number.
monthMap = map[string]int{
"jan": 1,
"feb": 2,
@ -59,7 +61,7 @@ var (
"nov": 11,
"dec": 12,
}
// 星期与数字对应表
// Short week name to its number.
weekMap = map[string]int{
"sun": 0,
"mon": 1,
@ -71,15 +73,15 @@ var (
}
)
// 解析定时格式为cronSchedule对象
// newSchedule creates and returns a schedule object for given cron pattern.
func newSchedule(pattern string) (*cronSchedule, error) {
// 处理预定义的定时格式
// Check if the predefined patterns.
if match, _ := gregex.MatchString(`(@\w+)\s*(\w*)\s*`, pattern); len(match) > 0 {
key := strings.ToLower(match[1])
if v, ok := predefinedPatternMap[key]; ok {
pattern = v
} else if strings.Compare(key, "@every") == 0 {
if d, err := time.ParseDuration(match[2]); err != nil {
if d, err := gtime.ParseDuration(match[2]); err != nil {
return nil, err
} else {
return &cronSchedule{
@ -92,44 +94,45 @@ func newSchedule(pattern string) (*cronSchedule, error) {
return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern))
}
}
// 处理通用的定时格式定义
// Handle the common cron pattern, like:
// 0 0 0 1 1 2
if match, _ := gregex.MatchString(gREGEX_FOR_CRON, pattern); len(match) == 7 {
schedule := &cronSchedule{
create: time.Now().Unix(),
every: 0,
pattern: pattern,
}
//
// Second.
if m, err := parseItem(match[1], 0, 59, false); err != nil {
return nil, err
} else {
schedule.second = m
}
//
// Minute.
if m, err := parseItem(match[2], 0, 59, false); err != nil {
return nil, err
} else {
schedule.minute = m
}
//
// Hour.
if m, err := parseItem(match[3], 0, 23, false); err != nil {
return nil, err
} else {
schedule.hour = m
}
//
// Day.
if m, err := parseItem(match[4], 1, 31, true); err != nil {
return nil, err
} else {
schedule.day = m
}
//
// Month.
if m, err := parseItem(match[5], 1, 12, false); err != nil {
return nil, err
} else {
schedule.month = m
}
//
// Week.
if m, err := parseItem(match[6], 0, 6, true); err != nil {
return nil, err
} else {
@ -141,7 +144,7 @@ func newSchedule(pattern string) (*cronSchedule, error) {
}
}
// 解析定时格式中的每一项定时配置
// parseItem parses every item in the pattern and returns the result as map.
func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) {
m := make(map[int]struct{}, max-min+1)
if item == "*" || (allowQuestionMark && item == "?") {
@ -159,19 +162,23 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
interval = i
}
}
rangeMin := min
rangeMax := max
rangeArray := strings.Split(intervalArray[0], "-")
valueType := byte(0)
var (
rangeMin = min
rangeMax = max
fieldType = byte(0)
rangeArray = strings.Split(intervalArray[0], "-") // Like: 1-30, JAN-DEC
)
switch max {
case 6:
valueType = 'w'
case 11:
valueType = 'm'
// It's checking week field.
fieldType = 'w'
case 12:
// It's checking month field.
fieldType = 'm'
}
// 例如: */5
// Eg: */5
if rangeArray[0] != "*" {
if i, err := parseItemValue(rangeArray[0], valueType); err != nil {
if i, err := parseItemValue(rangeArray[0], fieldType); err != nil {
return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item))
} else {
rangeMin = i
@ -179,7 +186,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
}
}
if len(rangeArray) == 2 {
if i, err := parseItemValue(rangeArray[1], valueType); err != nil {
if i, err := parseItemValue(rangeArray[1], fieldType); err != nil {
return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item))
} else {
rangeMax = i
@ -193,38 +200,41 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
return m, nil
}
// 将配置项值转换为数字
func parseItemValue(value string, valueType byte) (int, error) {
// parseItemValue parses the field value to a number according to its field type.
func parseItemValue(value string, fieldType byte) (int, error) {
if gregex.IsMatchString(`^\d+$`, value) {
// 纯数字
// Pure number.
if i, err := strconv.Atoi(value); err == nil {
return i, nil
}
} else {
// 英文字母
switch valueType {
// Check if contains letter,
// it converts the value to number according to predefined map.
switch fieldType {
case 'm':
if i, ok := monthMap[strings.ToLower(value)]; ok {
return int(i), nil
return i, nil
}
case 'w':
if i, ok := weekMap[strings.ToLower(value)]; ok {
return int(i), nil
return i, nil
}
}
}
return 0, errors.New(fmt.Sprintf(`invalid pattern value: "%s"`, value))
}
// 判断给定的时间是否满足schedule
// meet checks if the given time <t> meets the runnable point for the job.
func (s *cronSchedule) meet(t time.Time) bool {
if s.every != 0 {
// It checks using interval.
diff := t.Unix() - s.create
if diff > 0 {
return diff%s.every == 0
}
return false
} else {
// It checks using normal cron pattern.
if _, ok := s.second[t.Second()]; !ok {
return false
}