Fix recurrence rule parser (#3597)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-08-17 11:34:02 +06:00 committed by GitHub
parent ddd23e3bdf
commit 0ed468caba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -65,7 +65,7 @@ function generateDailyValues (
currentDate.setDate(currentDate.getDate() + (interval ?? 1))
if (count !== undefined && i === count) break
if (endDate !== undefined && currentDate.getTime() > endDate) break
if (endDate != null && currentDate.getTime() > endDate) break
if (currentDate.getTime() > to) break
}
}
@ -78,16 +78,20 @@ function generateWeeklyValues (
to: Timestamp
): void {
const { count, endDate, interval } = rule
const { byDay, wkst, bySetPos } = rule
let { byDay, wkst, bySetPos } = rule
let i = 0
if (byDay === undefined) {
byDay = [getWeekday(currentDate, wkst)]
}
while (true) {
const next = new Date(currentDate).setDate(currentDate.getDate() + (interval ?? 1) * 7)
const end = new Date(new Date(currentDate).setDate(currentDate.getDate() + 7))
let date = currentDate
while (date < end) {
if (
(byDay == null || byDay.includes(getWeekday(date, wkst))) &&
(byDay == null || matchesByDay(date, byDay, wkst)) &&
(bySetPos == null || bySetPos.includes(getSetPos(date)))
) {
const res = date.getTime()
@ -98,7 +102,7 @@ function generateWeeklyValues (
}
date = new Date(date.setDate(date.getDate() + 1))
if (count !== undefined && i === count) return
if (endDate !== undefined && date.getTime() > endDate) return
if (endDate != null && date.getTime() > endDate) return
if (date.getTime() > to) return
}
@ -106,6 +110,45 @@ function generateWeeklyValues (
}
}
function matchesByDay (date: Date, byDay: string[], wkst: string | undefined): boolean {
const weekday = getWeekday(date, wkst)
const dayOfMonth = Math.floor((date.getDate() - 1) / 7) + 1
for (const byDayItem of byDay) {
if (byDayItem === weekday) {
return true
}
const pos = parseInt(byDayItem)
if (isNaN(pos)) continue
if (pos > 0 && dayOfMonth === pos) {
return true
}
if (pos < 0 && dayOfMonth === getNegativePosition(date, weekday, pos)) {
return true
}
}
return false
}
function getNegativePosition (date: Date, weekday: string, pos: number): number | undefined {
const lastDayOfMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
const occurrences = []
for (let day = lastDayOfMonth; day >= 1; day--) {
const tempDate = new Date(date.getFullYear(), date.getMonth(), day)
if (getWeekday(tempDate) === weekday) {
occurrences.push(day)
}
if (occurrences.length === Math.abs(pos)) {
return occurrences.pop()
}
}
throw new Error(`Unable to calculate negative position ${pos}`)
}
function generateMonthlyValues (
rule: RecurringRule,
currentDate: Date,
@ -114,16 +157,20 @@ function generateMonthlyValues (
to: Timestamp
): void {
const { count, endDate, interval } = rule
const { byDay, byMonthDay, bySetPos, wkst } = rule
let { byDay, byMonthDay, bySetPos, wkst } = rule
let i = 0
if (byDay == null && byMonthDay == null) {
byMonthDay = [currentDate.getDate()]
}
while (true) {
const next = new Date(currentDate).setMonth(currentDate.getMonth() + (interval ?? 1))
const end = new Date(new Date(currentDate).setMonth(currentDate.getMonth() + 1))
let date = currentDate
while (date < end) {
if (
(byDay == null || byDay.includes(getWeekday(currentDate, wkst))) &&
(byDay == null || matchesByDay(date, byDay, wkst)) &&
(byMonthDay == null || byMonthDay.includes(new Date(currentDate).getDate())) &&
(bySetPos == null || bySetPos.includes(getSetPos(currentDate)))
) {
@ -136,7 +183,7 @@ function generateMonthlyValues (
date = new Date(date.setDate(date.getDate() + 1))
if (count !== undefined && i === count) return
if (endDate !== undefined && date.getTime() > endDate) return
if (endDate != null && date.getTime() > endDate) return
if (date.getTime() > to) return
}
currentDate = new Date(next)
@ -160,7 +207,7 @@ function generateYearlyValues (
let date = currentDate
while (date < end) {
if (
(byDay == null || byDay.includes(getWeekday(currentDate, wkst))) &&
(byDay == null || matchesByDay(date, byDay, wkst)) &&
(byMonthDay == null || byMonthDay.includes(currentDate.getDate())) &&
(byYearDay == null || byYearDay.includes(getYearDay(currentDate))) &&
(byWeekNo == null || byWeekNo.includes(getWeekNumber(currentDate))) &&
@ -175,7 +222,7 @@ function generateYearlyValues (
}
date = new Date(date.setDate(date.getDate() + 1))
if (count !== undefined && i === count) return
if (endDate !== undefined && date.getTime() > endDate) return
if (endDate != null && date.getTime() > endDate) return
if (date.getTime() > to) return
}
currentDate = new Date(next)