@@ -71,27 +71,63 @@ extension Calendar {
7171 let start : Date
7272 /// The recurrenece rule
7373 let recurrence : RecurrenceRule
74- /// Range in which the search should occur. If `nil`, return all results
75- let range : Range < Date > ?
74+ /// The lower end of the search range. If `nil`, the search is unbounded
75+ /// in the past.
76+ let lowerBound : Date ?
77+ /// The upper end of the search range. If `nil`, the search is unbounded
78+ /// in the future. If `inclusive` is true, `bound` is a valid result
79+ let upperBound : ( bound: Date , inclusive: Bool ) ?
7680
7781 init ( start: Date , recurrence: RecurrenceRule , range: Range < Date > ? ) {
7882 self . start = start
7983 self . recurrence = recurrence
80- self . range = range
84+ if let range {
85+ self . lowerBound = range. lowerBound
86+ self . upperBound = ( range. upperBound, false )
87+ } else {
88+ self . lowerBound = nil
89+ self . upperBound = nil
90+ }
91+ }
92+
93+ init ( start: Date , recurrence: RecurrenceRule , range: ClosedRange < Date > ) {
94+ self . start = start
95+ self . recurrence = recurrence
96+ self . lowerBound = range. lowerBound
97+ self . upperBound = ( range. upperBound, true )
98+ }
99+
100+ init ( start: Date , recurrence: RecurrenceRule , range: PartialRangeFrom < Date > ) {
101+ self . start = start
102+ self . recurrence = recurrence
103+ self . lowerBound = range. lowerBound
104+ self . upperBound = nil
105+ }
106+
107+ init ( start: Date , recurrence: RecurrenceRule , range: PartialRangeThrough < Date > ) {
108+ self . start = start
109+ self . recurrence = recurrence
110+ self . lowerBound = nil
111+ self . upperBound = ( range. upperBound, true )
112+ }
113+
114+ init ( start: Date , recurrence: RecurrenceRule , range: PartialRangeUpTo < Date > ) {
115+ self . start = start
116+ self . recurrence = recurrence
117+ self . lowerBound = nil
118+ self . upperBound = ( range. upperBound, false )
81119 }
82120
83121 struct Iterator : Sendable , IteratorProtocol {
84122 /// The starting date for the recurrence
85123 let start : Date
86124 /// The recurrence rule that should be used for enumeration
87125 let recurrence : RecurrenceRule
88- /// The range in which the sequence should produce results
89- let range : Range < Date > ?
90-
91- /// The lower bound of `range`, adjusted so that date expansions may
92- /// still fit in range even if this value is outside the range. This
93- /// value is used as a lower bound for ``nextBaseRecurrenceDate()``.
94- let rangeLowerBound : Date ?
126+
127+ /// The lower bound for iteration results, inclusive
128+ let lowerBound : Date ?
129+ /// The upper bound for iteration results and whether it's inclusive
130+ let upperBound : ( bound: Date , inclusive: Bool ) ?
95131
96132 /// The start date's fractional seconds component
97133 let fractionalSeconds : TimeInterval
@@ -105,6 +141,10 @@ extension Calendar {
105141 /// date, by the interval specified by the recurrence rule frequency
106142 /// This does not include the start date itself.
107143 var baseRecurrence : Calendar . DatesByMatching . Iterator
144+ /// The lower bound for `baseRecurrence`. Note that this date can be
145+ /// lower than `lowerBound`
146+ let baseRecurrenceLowerBound : Date ?
147+
108148
109149 /// How many elements we have consumed from `baseRecurrence`
110150 var iterations : Int = 0
@@ -123,7 +163,8 @@ extension Calendar {
123163
124164 internal init ( start: Date ,
125165 matching recurrence: RecurrenceRule ,
126- range: Range < Date > ? ) {
166+ lowerBound: Date ? ,
167+ upperBound: ( bound: Date , inclusive: Bool ) ? ) {
127168 // Copy the calendar if it's autoupdating
128169 var recurrence = recurrence
129170 if recurrence. calendar == . autoupdatingCurrent {
@@ -135,7 +176,6 @@ extension Calendar {
135176 let wholeSeconds = start. _time. floor ( )
136177 fractionalSeconds = ( start. _time - wholeSeconds) . head
137178 self . start = Date ( wholeSeconds)
138- self . range = range
139179
140180 let frequency = recurrence. frequency
141181
@@ -218,10 +258,12 @@ extension Calendar {
218258 secondAction = . expand
219259 }
220260
221- if let range {
222- rangeLowerBound = recurrence. calendar. dateInterval ( of: frequency. component, for: range. lowerBound) ? . start
261+ self . lowerBound = lowerBound
262+ self . upperBound = upperBound
263+ if let lowerBound {
264+ baseRecurrenceLowerBound = recurrence. calendar. dateInterval ( of: frequency. component, for: lowerBound) ? . start
223265 } else {
224- rangeLowerBound = nil
266+ baseRecurrenceLowerBound = nil
225267 }
226268
227269 // Create date components that enumerate recurrences without any
@@ -331,7 +373,7 @@ extension Calendar {
331373 }
332374 // If a range has been specified, we should skip a few extra
333375 // occurrences until we reach the start date
334- if let rangeLowerBound , nextDate < rangeLowerBound {
376+ if let baseRecurrenceLowerBound , nextDate < baseRecurrenceLowerBound {
335377 continue
336378 }
337379 anchor = nextDate
@@ -477,11 +519,18 @@ extension Calendar {
477519 finished = true
478520 return nil
479521 }
480- if let range = self . range {
481- if date >= range. upperBound {
522+ if let upperBound = self . upperBound {
523+ let outOfRange = switch upperBound. inclusive {
524+ case true : date > upperBound. bound
525+ case false : date >= upperBound. bound
526+ }
527+ if outOfRange {
482528 finished = true
483529 return nil
484- } else if date < range. lowerBound {
530+ }
531+ }
532+ if let lowerBound = self . lowerBound {
533+ if date < lowerBound {
485534 continue
486535 }
487536 }
@@ -504,7 +553,7 @@ extension Calendar {
504553 }
505554
506555 public func makeIterator( ) -> Iterator {
507- return Iterator ( start: start, matching: recurrence, range : range )
556+ return Iterator ( start: start, matching: recurrence, lowerBound : lowerBound , upperBound : upperBound )
508557 }
509558 }
510559}
0 commit comments