-
Notifications
You must be signed in to change notification settings - Fork 1
/
fields.go
166 lines (139 loc) · 3.75 KB
/
fields.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// SPDX-License-Identifier: MIT
package cron
import (
"fmt"
"math/bits"
"sort"
"strconv"
"strings"
)
// 表示 cron 语法中每一个字段的数据
//
// 最长的秒数,最多 60 位,正好可以使用一个 uint64 保存,
// 其它类型也各自占一个字段长度。
//
// 其中每个字段中,从 0 位到高位,每一位表示一个值,比如在秒字段中,
// 0,1,7 表示为 ...10000011
// 如果是月份这种从 1 开始的,则其第一位永远是 0
type fields uint64
const (
// any 和 step 是两个特殊的标记位,需要大于 60(所有字段中,秒数最大,
// 但不会超过 60)
// any 表示当前字段可以是任意值,即对值不做任意要求,
// 甚至可以一直是相同的值,也不会做累加。
any fields = 1 << 61
// step 表示当前字段是允许范围内的所有值。
// 每次计算时,按其当前值加 1 即可。
step fields = 1 << 62
)
var bounds = []bound{
{min: 0, max: 59}, // secondIndex
{min: 0, max: 59}, // minuteIndex
{min: 0, max: 23}, // hourIndex
{min: 1, max: 31}, // dayIndex
{min: 1, max: 12}, // monthIndex
{min: 0, max: 7}, // weekIndex
}
type bound struct{ min, max int }
func (b bound) valid(v int) bool { return v >= b.min && v <= b.max }
// 获取 fields 中与 curr 最近的下一个值
//
// curr 当前的时间值;
// typ 字段类型;
// greater 是否必须要大于 curr 这个值;
// val 返回计算后的最近一个时间值;
// c 是否需要下一个值进位。
func (fs fields) next(curr int, b bound, greater bool) (val int, c bool) {
if fs == any { // any 表示对当前值没有要求,不需要增加值。
return curr, greater
} else if fs == step {
if greater {
curr++
}
if curr > b.max {
return b.min, true
}
return curr, false
}
for i := curr; i <= b.max; i++ {
if ((uint64(1) << uint64(i)) & uint64(fs)) <= 0 { // 该位未被设置为 1
continue
}
if i > curr {
return i, false
} else if i == curr && !greater {
return i, false
}
}
// 大于当前列表的最大值,则返回列表中的最小值,并设置进位标记
return bits.TrailingZeros64(uint64(fs)), true
}
// 分析单个数字域内容
//
// field 可以是以下格式:
// *
// n1-n2
// n1,n2
// n1-n2,n3-n4,n5
func parseField(typ int, field string) (fields, error) {
if field == "*" {
return any, nil
}
fs := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
list := make([]uint64, 0, len(fs))
b := bounds[typ]
for _, v := range fs {
if len(v) <= 2 { // 少于 3 个字符,说明不可能有特殊字符。
n, err := strconv.Atoi(v)
if err != nil {
return 0, err
}
if !b.valid(n) {
return 0, fmt.Errorf("值 %d 超出范围:[%d,%d]", n, b.min, b.max)
}
// 星期中的 7 替换成 0
if typ == weekIndex && n == b.max {
n = b.min
}
list = append(list, uint64(n))
continue
}
index := strings.IndexByte(v, '-')
if index >= 0 {
n1, err := strconv.Atoi(v[:index])
if err != nil {
return 0, err
}
n2, err := strconv.Atoi(v[index+1:])
if err != nil {
return 0, err
}
if !b.valid(n1) {
return 0, fmt.Errorf("值 %d 超出范围:[%d,%d]", n1, b.min, b.max)
}
if !b.valid(n2) {
return 0, fmt.Errorf("值 %d 超出范围:[%d,%d]", n2, b.min, b.max)
}
for i := n1; i <= n2; i++ {
if typ == weekIndex && i == b.max {
list = append(list, uint64(b.min))
} else {
list = append(list, uint64(i))
}
}
}
}
sort.SliceStable(list, func(i, j int) bool {
return list[i] < list[j]
})
for i := 1; i < len(list); i++ {
if list[i] == list[i-1] {
return 0, fmt.Errorf("重复的值 %d", list[i])
}
}
var ret fields
for _, v := range list {
ret |= (1 << v)
}
return ret, nil
}