-
Notifications
You must be signed in to change notification settings - Fork 1
/
cron.go
125 lines (110 loc) · 2.52 KB
/
cron.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// SPDX-FileCopyrightText: 2018-2024 caixw
//
// SPDX-License-Identifier: MIT
// Package cron 实现了 [cron] 表达式的 [schedulers.Scheduler] 接口
//
// [cron]: https://zh.wikipedia.org/wiki/Cron
package cron
import (
"errors"
"strings"
"time"
"github.com/issue9/scheduled/schedulers"
"github.com/issue9/scheduled/schedulers/at"
)
// 表示 cron.data 中各个元素的索引值
const (
secondIndex = iota
minuteIndex
hourIndex
dayIndex
monthIndex
weekIndex
indexSize
)
// 常用的便捷指令
var direct = map[string]string{
"@yearly": "0 0 0 1 1 *",
"@annually": "0 0 0 1 1 *",
"@monthly": "0 0 0 1 * *",
"@weekly": "0 0 0 * * 0",
"@daily": "0 0 0 * * *",
"@midnight": "0 0 0 * * *",
"@hourly": "0 0 * * * *",
}
type cron struct {
// 依次保存着 cron 语法中各个字段解析后的内容
data []fields
loc *time.Location
}
// Parse 根据 spec 初始化 [schedulers.Scheduler]
//
// spec 表示 crontab 的格式
//
// 区分大小写,支持秒,其格式如下:
//
// ! * * * * * *
// | | | | | |
// | | | | | --- 星期
// | | | | ----- 月
// | | | ------- 日
// | | --------- 小时
// | ----------- 分
// ------------- 秒
//
// 星期与日若同时存在,则以或的形式组合。!用于使 go fmt 不会自动格式化内容,无实际意义。
//
// 支持以下符号:
// - - 表示范围
// - , 表示和
//
// 同时支持以下便捷指令:
//
// @reboot: 启动时执行一次
// @yearly: 0 0 0 1 1 *
// @annually: 0 0 0 1 1 *
// @monthly: 0 0 0 1 * *
// @weekly: 0 0 0 * * 0
// @daily: 0 0 0 * * *
// @midnight: 0 0 0 * * *
// @hourly: 0 0 * * * *
func Parse(spec string, loc *time.Location) (schedulers.Scheduler, error) {
switch {
case spec == "":
return nil, errors.New("参数 spec 不能为空")
case spec == "@reboot":
return at.At(time.Now()), nil
case spec[0] == '@':
d, found := direct[spec]
if !found {
return nil, errors.New("未找到指令:" + spec)
}
spec = d
}
fs := strings.Fields(spec)
if len(fs) != indexSize {
return nil, errors.New("长度不正确")
}
c := &cron{
data: make([]fields, indexSize),
loc: loc,
}
allAny := true // 是否所有字段都是 asterisk
for i, field := range fs {
vals, err := parseField(i, field)
if err != nil {
return nil, err
}
if allAny && vals != asterisk {
allAny = false
}
if !allAny && vals == asterisk {
vals = step
}
c.data[i] = vals
}
if allAny { // 所有项都为 *
return nil, errors.New("所有项都为 *")
}
return c, nil
}