Skip to content

Commit fca6bba

Browse files
committed
fix: every n-th segments #49
1 parent 77b8ef0 commit fca6bba

File tree

2 files changed

+41
-20
lines changed

2 files changed

+41
-20
lines changed

core/src/__tests__/cron.spec.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@ describe('segments', () => {
1616
expect(cronToArray('*', r(1, 3))).toEqual([])
1717
expect(cronToArray('1,3,5', r(0, 24))).toEqual([1, 3, 5])
1818
expect(cronToArray('*/5', r(0, 11))).toEqual([0, 5, 10])
19-
expect(cronToArray('*/5', r(1, 11))).toEqual([5, 10])
19+
expect(cronToArray('*/5', r(1, 11))).toEqual([1, 6, 11])
2020
expect(cronToArray('10-15', r(0, 59))).toEqual([10, 11, 12, 13, 14, 15])
2121
expect(cronToArray('10-11,20-22,30-33', r(0, 59))).toEqual([10, 11, 20, 21, 22, 30, 31, 32, 33])
2222
expect(cronToArray('5,7-8', r(0, 59))).toEqual([5, 7, 8])
23+
expect(cronToArray('*/10', r(1, 10))).toEqual([1])
24+
expect(cronToArray('2-6/2', r(1, 10))).toEqual([2, 4, 6])
25+
expect(cronToArray('3-5/10', r(1, 10))).toEqual([3])
2326

2427
expect(cronToArray('x', r(0, 59))).toBe(null)
2528
expect(cronToArray('1-60', r(0, 59))).toBe(null)
2629
expect(cronToArray('0-10', r(1, 59))).toBe(null)
2730
expect(cronToArray('60', r(0, 59))).toBe(null)
2831
expect(cronToArray('0', r(1, 10))).toBe(null)
2932
expect(cronToArray('*/90', r(1, 10))).toBe(null)
33+
expect(cronToArray('*/11', r(1, 10))).toBe(null)
34+
expect(cronToArray('2-6/11', r(1, 10))).toBe(null)
3035
})
3136

3237
it('arrayToSegment', () => {
@@ -40,7 +45,7 @@ describe('segments', () => {
4045
expect(arrayToCron([], r(1, 3))).toEqual('*')
4146
expect(arrayToCron([1, 2, 3], r(1, 3))).toEqual('*')
4247
expect(arrayToCron([0, 5, 10], r(0, 10))).toEqual('*/5')
43-
expect(arrayToCron([7, 14, 21, 28], r(5, 30))).toEqual('*/7')
48+
expect(arrayToCron([5, 12, 19, 26], r(5, 30))).toEqual('*/7')
4449
expect(arrayToCron([0, 5, 10], r(0, 20))).toEqual('0,5,10')
4550
expect(arrayToCron([1, 2, 5, 8, 9, 10], r(1, 10))).toEqual('1-2,5,8-10')
4651
})

core/src/cron.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -129,40 +129,45 @@ class RangeSegment implements CronSegment {
129129
}
130130

131131
const _every = (n: number, min: number, max: number) => {
132-
const start = n * Math.floor(min / n)
133132
const res = []
134-
for (let i = start; i <= max; i += n) {
135-
if (i >= min) {
136-
res.push(i)
137-
}
133+
for (let i = min; i <= max; i += n) {
134+
res.push(i)
138135
}
139136
return res
140137
}
141138

142139
class EverySegment implements CronSegment {
143-
static re = /^\*\/\d+$/
140+
static re = /^(\*|\d+-\d+)\/\d+$/
144141

145142
field: FieldWrapper
146143
type: CronType = CronType.EveryX
147144
every: number
145+
start: number
146+
end: number
148147

149-
constructor(field: FieldWrapper, every: number) {
148+
constructor(field: FieldWrapper, every: number, start?: number, end?: number) {
150149
this.field = field
151150
this.every = every
151+
this.start = start ?? field.min
152+
this.end = end ?? field.max
152153
}
153154

154155
toCron() {
155-
return `*/${this.every}`
156+
if (this.start == this.field.min && this.end == this.field.max) {
157+
return `*/${this.every}`
158+
}
159+
return `${this.start}-${this.end}/${this.every}`
156160
}
157161

158162
toArray() {
159-
const { min, max } = this.field
160-
return _every(this.every, min, max)
163+
return _every(this.every, this.start, this.end)
161164
}
162165

163166
get items() {
164167
return {
165168
every: this.field.itemMap[this.every],
169+
start: this.field.itemMap[this.start],
170+
end: this.field.itemMap[this.end],
166171
}
167172
}
168173

@@ -171,15 +176,22 @@ class EverySegment implements CronSegment {
171176
return null
172177
}
173178

174-
const [, everyStr] = str.split('/')
179+
const [rangeStr, everyStr] = str.split('/')
175180
const every = parseInt(everyStr)
176-
const { min, max } = field
181+
182+
if (every > field.items.length) {
183+
return null
184+
}
185+
186+
const range = str.split('-').map((s) => parseInt(s))
187+
const min = rangeStr == '*' ? field.min : range[0]
188+
const max = rangeStr == '*' ? field.max : range[1]
177189

178190
if (_every(every, min, max).length == 0) {
179191
return null
180192
}
181193

182-
return new EverySegment(field, every)
194+
return new EverySegment(field, every, min, max)
183195
}
184196

185197
static fromArray(arr: number[], field: FieldWrapper) {
@@ -194,18 +206,22 @@ class EverySegment implements CronSegment {
194206
return null
195207
}
196208

197-
const first = min % step === 0 ? min : (Math.floor(min / step) + 1) * step
198-
if (arr.length !== Math.floor((max - first) / step) + 1) {
209+
// prevent a-b/x segments until localization is ready
210+
if (arr[0] != min) {
211+
return null
212+
}
213+
const end = arr[arr.length - 1]
214+
if (max - end >= step) {
199215
return null
200216
}
201217

202-
for (const value of arr) {
203-
if (value % step !== 0) {
218+
for (let i = 2; i < arr.length; i++) {
219+
if (arr[i] - arr[i - 1] != step) {
204220
return null
205221
}
206222
}
207223

208-
return new EverySegment(field, step)
224+
return new EverySegment(field, step, min, max)
209225
}
210226
}
211227

0 commit comments

Comments
 (0)