github.com/zhongdalu/gf@v1.0.0/g/os/gcron/gcron_schedule.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 package gcron 8 9 import ( 10 "errors" 11 "fmt" 12 "github.com/zhongdalu/gf/g/text/gregex" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 // 运行时间管理对象 19 type cronSchedule struct { 20 create int64 // 创建时间戳(秒) 21 every int64 // 运行时间间隔(秒) 22 pattern string // 原始注册字符串 23 second map[int]struct{} 24 minute map[int]struct{} 25 hour map[int]struct{} 26 day map[int]struct{} 27 week map[int]struct{} 28 month map[int]struct{} 29 } 30 31 const ( 32 gREGEX_FOR_CRON = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)$` 33 ) 34 35 var ( 36 // 预定义的定时格式 37 predefinedPatternMap = map[string]string{ 38 "@yearly": "0 0 0 1 1 *", 39 "@annually": "0 0 0 1 1 *", 40 "@monthly": "0 0 0 1 * *", 41 "@weekly": "0 0 0 * * 0", 42 "@daily": "0 0 0 * * *", 43 "@midnight": "0 0 0 * * *", 44 "@hourly": "0 0 * * * *", 45 } 46 // 月份与数字对应表 47 monthMap = map[string]int{ 48 "jan": 1, 49 "feb": 2, 50 "mar": 3, 51 "apr": 4, 52 "may": 5, 53 "jun": 6, 54 "jul": 7, 55 "aug": 8, 56 "sep": 9, 57 "oct": 10, 58 "nov": 11, 59 "dec": 12, 60 } 61 // 星期与数字对应表 62 weekMap = map[string]int{ 63 "sun": 0, 64 "mon": 1, 65 "tue": 2, 66 "wed": 3, 67 "thu": 4, 68 "fri": 5, 69 "sat": 6, 70 } 71 ) 72 73 // 解析定时格式为cronSchedule对象 74 func newSchedule(pattern string) (*cronSchedule, error) { 75 // 处理预定义的定时格式 76 if match, _ := gregex.MatchString(`(@\w+)\s*(\w*)\s*`, pattern); len(match) > 0 { 77 key := strings.ToLower(match[1]) 78 if v, ok := predefinedPatternMap[key]; ok { 79 pattern = v 80 } else if strings.Compare(key, "@every") == 0 { 81 if d, err := time.ParseDuration(match[2]); err != nil { 82 return nil, err 83 } else { 84 return &cronSchedule{ 85 create: time.Now().Unix(), 86 every: int64(d.Seconds()), 87 pattern: pattern, 88 }, nil 89 } 90 } else { 91 return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern)) 92 } 93 } 94 // 处理通用的定时格式定义 95 if match, _ := gregex.MatchString(gREGEX_FOR_CRON, pattern); len(match) == 7 { 96 schedule := &cronSchedule{ 97 create: time.Now().Unix(), 98 every: 0, 99 pattern: pattern, 100 } 101 // 秒 102 if m, err := parseItem(match[1], 0, 59, false); err != nil { 103 return nil, err 104 } else { 105 schedule.second = m 106 } 107 // 分 108 if m, err := parseItem(match[2], 0, 59, false); err != nil { 109 return nil, err 110 } else { 111 schedule.minute = m 112 } 113 // 时 114 if m, err := parseItem(match[3], 0, 23, false); err != nil { 115 return nil, err 116 } else { 117 schedule.hour = m 118 } 119 // 天 120 if m, err := parseItem(match[4], 1, 31, true); err != nil { 121 return nil, err 122 } else { 123 schedule.day = m 124 } 125 // 月 126 if m, err := parseItem(match[5], 1, 12, false); err != nil { 127 return nil, err 128 } else { 129 schedule.month = m 130 } 131 // 周 132 if m, err := parseItem(match[6], 0, 6, true); err != nil { 133 return nil, err 134 } else { 135 schedule.week = m 136 } 137 return schedule, nil 138 } else { 139 return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern)) 140 } 141 } 142 143 // 解析定时格式中的每一项定时配置 144 func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) { 145 m := make(map[int]struct{}, max-min+1) 146 if item == "*" || (allowQuestionMark && item == "?") { 147 for i := min; i <= max; i++ { 148 m[i] = struct{}{} 149 } 150 } else { 151 for _, item := range strings.Split(item, ",") { 152 interval := 1 153 intervalArray := strings.Split(item, "/") 154 if len(intervalArray) == 2 { 155 if i, err := strconv.Atoi(intervalArray[1]); err != nil { 156 return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) 157 } else { 158 interval = i 159 } 160 } 161 rangeMin := min 162 rangeMax := max 163 rangeArray := strings.Split(intervalArray[0], "-") 164 valueType := byte(0) 165 switch max { 166 case 6: 167 valueType = 'w' 168 case 11: 169 valueType = 'm' 170 } 171 // 例如: */5 172 if rangeArray[0] != "*" { 173 if i, err := parseItemValue(rangeArray[0], valueType); err != nil { 174 return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) 175 } else { 176 rangeMin = i 177 rangeMax = i 178 } 179 } 180 if len(rangeArray) == 2 { 181 if i, err := parseItemValue(rangeArray[1], valueType); err != nil { 182 return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) 183 } else { 184 rangeMax = i 185 } 186 } 187 for i := rangeMin; i <= rangeMax; i += interval { 188 m[i] = struct{}{} 189 } 190 } 191 } 192 return m, nil 193 } 194 195 // 将配置项值转换为数字 196 func parseItemValue(value string, valueType byte) (int, error) { 197 if gregex.IsMatchString(`^\d+$`, value) { 198 // 纯数字 199 if i, err := strconv.Atoi(value); err == nil { 200 return i, nil 201 } 202 } else { 203 // 英文字母 204 switch valueType { 205 case 'm': 206 if i, ok := monthMap[strings.ToLower(value)]; ok { 207 return int(i), nil 208 } 209 case 'w': 210 if i, ok := weekMap[strings.ToLower(value)]; ok { 211 return int(i), nil 212 } 213 } 214 } 215 return 0, errors.New(fmt.Sprintf(`invalid pattern value: "%s"`, value)) 216 } 217 218 // 判断给定的时间是否满足schedule 219 func (s *cronSchedule) meet(t time.Time) bool { 220 if s.every != 0 { 221 diff := t.Unix() - s.create 222 if diff > 0 { 223 return diff%s.every == 0 224 } 225 return false 226 } else { 227 if _, ok := s.second[t.Second()]; !ok { 228 return false 229 } 230 if _, ok := s.minute[t.Minute()]; !ok { 231 return false 232 } 233 if _, ok := s.hour[t.Hour()]; !ok { 234 return false 235 } 236 if _, ok := s.day[t.Day()]; !ok { 237 return false 238 } 239 if _, ok := s.month[int(t.Month())]; !ok { 240 return false 241 } 242 if _, ok := s.week[int(t.Weekday())]; !ok { 243 return false 244 } 245 return true 246 } 247 }