Skip to content

Commit 3cf8d2f

Browse files
committed
fix: DateTime.add() hangs when interval generates sequences never overlap byhour
1 parent 9f2061f commit 3cf8d2f

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed

src/datetime.ts

+31-7
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,12 @@ export class DateTime extends Time {
139139
this.fixDay()
140140
}
141141

142-
public addHours(hours: number, filtered: boolean, byhour: number[]) {
142+
public addHours(
143+
hours: number,
144+
filtered: boolean,
145+
byhour: number[],
146+
until?: Date
147+
) {
143148
if (filtered) {
144149
// Jump to one iteration before next day
145150
this.hour += Math.floor((23 - this.hour) / hours) * hours
@@ -154,14 +159,21 @@ export class DateTime extends Time {
154159
}
155160

156161
if (empty(byhour) || includes(byhour, this.hour)) break
162+
163+
if (this.exceedUntilOrMaxYear(until)) break
157164
}
158165
}
159166

167+
private exceedUntilOrMaxYear(until?: Date): boolean {
168+
return (until && this.getTime() > until.getTime()) || this.year > MAXYEAR
169+
}
170+
160171
public addMinutes(
161172
minutes: number,
162173
filtered: boolean,
163174
byhour: number[],
164-
byminute: number[]
175+
byminute: number[],
176+
until?: Date
165177
) {
166178
if (filtered) {
167179
// Jump to one iteration before next day
@@ -183,6 +195,8 @@ export class DateTime extends Time {
183195
) {
184196
break
185197
}
198+
199+
if (this.exceedUntilOrMaxYear(until)) break
186200
}
187201
}
188202

@@ -191,7 +205,8 @@ export class DateTime extends Time {
191205
filtered: boolean,
192206
byhour: number[],
193207
byminute: number[],
194-
bysecond: number[]
208+
bysecond: number[],
209+
until?: Date
195210
) {
196211
if (filtered) {
197212
// Jump to one iteration before next day
@@ -217,6 +232,8 @@ export class DateTime extends Time {
217232
) {
218233
break
219234
}
235+
236+
if (this.exceedUntilOrMaxYear(until)) break
220237
}
221238
}
222239

@@ -246,7 +263,7 @@ export class DateTime extends Time {
246263
}
247264

248265
public add(options: ParsedOptions, filtered: boolean) {
249-
const { freq, interval, wkst, byhour, byminute, bysecond } = options
266+
const { freq, interval, wkst, byhour, byminute, bysecond, until } = options
250267

251268
switch (freq) {
252269
case Frequency.YEARLY:
@@ -258,11 +275,18 @@ export class DateTime extends Time {
258275
case Frequency.DAILY:
259276
return this.addDaily(interval)
260277
case Frequency.HOURLY:
261-
return this.addHours(interval, filtered, byhour)
278+
return this.addHours(interval, filtered, byhour, until)
262279
case Frequency.MINUTELY:
263-
return this.addMinutes(interval, filtered, byhour, byminute)
280+
return this.addMinutes(interval, filtered, byhour, byminute, until)
264281
case Frequency.SECONDLY:
265-
return this.addSeconds(interval, filtered, byhour, byminute, bysecond)
282+
return this.addSeconds(
283+
interval,
284+
filtered,
285+
byhour,
286+
byminute,
287+
bysecond,
288+
until
289+
)
266290
}
267291
}
268292
}

test/datetime.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { DateTime } from '../src/datetime'
2+
import { Frequency } from '../src'
3+
import { ParsedOptions } from '../src/types'
4+
5+
describe('datetime', () => {
6+
describe('DateTime', () => {
7+
const defaultOption: ParsedOptions = {
8+
freq: Frequency.HOURLY,
9+
dtstart: null,
10+
interval: 1,
11+
wkst: 0,
12+
count: 1,
13+
until: null,
14+
tzid: null,
15+
bysetpos: null,
16+
bymonth: null,
17+
bymonthday: [],
18+
bynmonthday: [],
19+
byyearday: null,
20+
byweekno: null,
21+
byweekday: null,
22+
bynweekday: null,
23+
byhour: [0],
24+
byminute: [0],
25+
bysecond: [0],
26+
byeaster: null,
27+
}
28+
29+
it('should not hang when add HOURLY with odd "byhour" and even "interval"', () => {
30+
const byhour = [1]
31+
const interval = 2
32+
const dtstart = new Date(2024, 2, 26)
33+
const until = new Date(2024, 2, 27)
34+
const dt = DateTime.fromDate(dtstart)
35+
dt.add(
36+
{
37+
...defaultOption,
38+
byhour,
39+
interval,
40+
freq: Frequency.HOURLY,
41+
dtstart,
42+
until,
43+
},
44+
false
45+
)
46+
47+
expect(dt.getTime()).toBeGreaterThan(until.getTime())
48+
})
49+
50+
it('should not hang when add MINUTELY with odd "byminute" and even "interval"', () => {
51+
const byminute = [1]
52+
const interval = 2
53+
const dtstart = new Date(2024, 2, 26)
54+
const until = new Date(2024, 2, 27)
55+
const dt = DateTime.fromDate(dtstart)
56+
dt.add(
57+
{
58+
...defaultOption,
59+
byminute,
60+
interval,
61+
freq: Frequency.MINUTELY,
62+
dtstart,
63+
until,
64+
},
65+
false
66+
)
67+
68+
expect(dt.getTime()).toBeGreaterThan(until.getTime())
69+
})
70+
71+
it('should not hang when add SECONDLY with odd "bysecond" and even "interval"', () => {
72+
const bysecond = [1]
73+
const interval = 2
74+
const dtstart = new Date(2024, 2, 26)
75+
const until = new Date(2024, 2, 27)
76+
const dt = DateTime.fromDate(dtstart)
77+
dt.add(
78+
{
79+
...defaultOption,
80+
bysecond,
81+
interval,
82+
freq: Frequency.SECONDLY,
83+
dtstart,
84+
until,
85+
},
86+
false
87+
)
88+
89+
expect(dt.getTime()).toBeGreaterThan(until.getTime())
90+
})
91+
})
92+
})

0 commit comments

Comments
 (0)