Skip to content

Commit a516f2d

Browse files
committed
Filter out negative components after normalizing. Components that are too big remain, and date matching will take care of them
1 parent 31bfa63 commit a516f2d

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

Sources/FoundationEssentials/Calendar/Calendar_Recurrence.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -658,37 +658,43 @@ extension Calendar {
658658
/// Normalized months so that all months are positive
659659
func _normalizedMonths(_ months: [Calendar.RecurrenceRule.Month], for anchor: Date) -> [Calendar.RecurrenceRule.Month] {
660660
lazy var monthRange = self.range(of: .month, in: .year, for: anchor)
661-
return months.map { month in
661+
return months.compactMap { month in
662662
if month.index > 0 {
663663
return month
664-
} else {
664+
} else if month.index > -monthRange!.upperBound {
665665
let newIndex = monthRange!.upperBound + month.index
666666
// The upper bound is the last month plus one. Subtracting 1 we get the last month
667667
return Calendar.RecurrenceRule.Month(newIndex, isLeap: month.isLeap)
668+
} else {
669+
return nil
668670
}
669671
}
670672
}
671673

672674
/// Normalized days in a month so that all days are positive
673675
internal func _normalizedDaysOfMonth(_ days: [Int], for anchor: Date) -> [Int] {
674676
lazy var dayRange = self.range(of: .day, in: .month, for: anchor)
675-
return days.map { day in
677+
return days.compactMap { day in
676678
if day > 0 {
677679
day
678-
} else {
680+
} else if day > -dayRange!.upperBound {
679681
dayRange!.upperBound + day
682+
} else {
683+
nil
680684
}
681685
}
682686
}
683687

684688
/// Normalized days in a year so that all days are positive
685689
internal func _normalizedDaysOfYear(_ days: [Int], for anchor: Date) -> [Int] {
686690
lazy var dayRange = self.range(of: .day, in: .year, for: anchor)
687-
return days.map { day in
691+
return days.compactMap { day in
688692
if day > 0 {
689693
day
690-
} else {
694+
} else if day > -dayRange!.upperBound {
691695
dayRange!.upperBound + day
696+
} else {
697+
nil
692698
}
693699
}
694700
}
@@ -709,11 +715,13 @@ extension Calendar {
709715
weekRange.upperBound
710716
}
711717

712-
return weeksOfYear.map { weekIdx in
718+
return weeksOfYear.compactMap { weekIdx in
713719
if weekIdx > 0 {
714720
weekIdx
715-
} else {
721+
} else if weekIdx > -lastWeekIdx {
716722
lastWeekIdx + weekIdx
723+
} else {
724+
nil
717725
}
718726
}
719727
}

Tests/FoundationEssentialsTests/GregorianCalendarRecurrenceRuleTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,4 +663,17 @@ final class GregorianCalendarRecurrenceRuleTests: XCTestCase {
663663
}
664664
// If we get here, there isn't an infinite loop
665665
}
666+
667+
func testOutOfRangeComponents() {
668+
let start = Date(timeIntervalSince1970: 1695304800.0) // 2023-09-21T14:00:00-0000
669+
let end = Date(timeIntervalSince1970: 1729519200.0) // 2024-10-21T14:00:00-0000
670+
671+
var rule = Calendar.RecurrenceRule(calendar: gregorian, frequency: .monthly)
672+
rule.daysOfTheMonth = [32, -32]
673+
674+
let eventStart = Date(timeIntervalSince1970: 1285077600.0) // 2010-09-21T14:00:00-0000
675+
let results = Array(rule.recurrences(of: eventStart, in: start..<end))
676+
677+
XCTAssertEqual(results, [])
678+
}
666679
}

0 commit comments

Comments
 (0)