diff --git a/src/datetime.ts b/src/datetime.ts index a542047c..2fea9710 100644 --- a/src/datetime.ts +++ b/src/datetime.ts @@ -139,7 +139,12 @@ export class DateTime extends Time { this.fixDay() } - public addHours(hours: number, filtered: boolean, byhour: number[]) { + public addHours( + hours: number, + filtered: boolean, + byhour: number[], + until?: Date + ) { if (filtered) { // Jump to one iteration before next day this.hour += Math.floor((23 - this.hour) / hours) * hours @@ -154,14 +159,21 @@ export class DateTime extends Time { } if (empty(byhour) || includes(byhour, this.hour)) break + + if (this.exceedUntilOrMaxYear(until)) break } } + private exceedUntilOrMaxYear(until?: Date): boolean { + return (until && this.getTime() > until.getTime()) || this.year > MAXYEAR + } + public addMinutes( minutes: number, filtered: boolean, byhour: number[], - byminute: number[] + byminute: number[], + until?: Date ) { if (filtered) { // Jump to one iteration before next day @@ -183,6 +195,8 @@ export class DateTime extends Time { ) { break } + + if (this.exceedUntilOrMaxYear(until)) break } } @@ -191,7 +205,8 @@ export class DateTime extends Time { filtered: boolean, byhour: number[], byminute: number[], - bysecond: number[] + bysecond: number[], + until?: Date ) { if (filtered) { // Jump to one iteration before next day @@ -217,6 +232,8 @@ export class DateTime extends Time { ) { break } + + if (this.exceedUntilOrMaxYear(until)) break } } @@ -246,7 +263,7 @@ export class DateTime extends Time { } public add(options: ParsedOptions, filtered: boolean) { - const { freq, interval, wkst, byhour, byminute, bysecond } = options + const { freq, interval, wkst, byhour, byminute, bysecond, until } = options switch (freq) { case Frequency.YEARLY: @@ -258,11 +275,18 @@ export class DateTime extends Time { case Frequency.DAILY: return this.addDaily(interval) case Frequency.HOURLY: - return this.addHours(interval, filtered, byhour) + return this.addHours(interval, filtered, byhour, until) case Frequency.MINUTELY: - return this.addMinutes(interval, filtered, byhour, byminute) + return this.addMinutes(interval, filtered, byhour, byminute, until) case Frequency.SECONDLY: - return this.addSeconds(interval, filtered, byhour, byminute, bysecond) + return this.addSeconds( + interval, + filtered, + byhour, + byminute, + bysecond, + until + ) } } } diff --git a/test/datetime.test.ts b/test/datetime.test.ts new file mode 100644 index 00000000..cf8deb0d --- /dev/null +++ b/test/datetime.test.ts @@ -0,0 +1,92 @@ +import { DateTime } from '../src/datetime' +import { Frequency } from '../src' +import { ParsedOptions } from '../src/types' + +describe('datetime', () => { + describe('DateTime', () => { + const defaultOption: ParsedOptions = { + freq: Frequency.HOURLY, + dtstart: null, + interval: 1, + wkst: 0, + count: 1, + until: null, + tzid: null, + bysetpos: null, + bymonth: null, + bymonthday: [], + bynmonthday: [], + byyearday: null, + byweekno: null, + byweekday: null, + bynweekday: null, + byhour: [0], + byminute: [0], + bysecond: [0], + byeaster: null, + } + + it('should not hang when add HOURLY with odd "byhour" and even "interval"', () => { + const byhour = [1] + const interval = 2 + const dtstart = new Date(2024, 2, 26) + const until = new Date(2024, 2, 27) + const dt = DateTime.fromDate(dtstart) + dt.add( + { + ...defaultOption, + byhour, + interval, + freq: Frequency.HOURLY, + dtstart, + until, + }, + false + ) + + expect(dt.getTime()).toBeGreaterThan(until.getTime()) + }) + + it('should not hang when add MINUTELY with odd "byminute" and even "interval"', () => { + const byminute = [1] + const interval = 2 + const dtstart = new Date(2024, 2, 26) + const until = new Date(2024, 2, 27) + const dt = DateTime.fromDate(dtstart) + dt.add( + { + ...defaultOption, + byminute, + interval, + freq: Frequency.MINUTELY, + dtstart, + until, + }, + false + ) + + expect(dt.getTime()).toBeGreaterThan(until.getTime()) + }) + + it('should not hang when add SECONDLY with odd "bysecond" and even "interval"', () => { + const bysecond = [1] + const interval = 2 + const dtstart = new Date(2024, 2, 26) + const until = new Date(2024, 2, 27) + const dt = DateTime.fromDate(dtstart) + dt.add( + { + ...defaultOption, + bysecond, + interval, + freq: Frequency.SECONDLY, + dtstart, + until, + }, + false + ) + + expect(dt.getTime()).toBeGreaterThan(until.getTime()) + }) + }) +})