@@ -71,27 +71,63 @@ extension Calendar {
71
71
let start : Date
72
72
/// The recurrenece rule
73
73
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 ) ?
76
80
77
81
init ( start: Date , recurrence: RecurrenceRule , range: Range < Date > ? ) {
78
82
self . start = start
79
83
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 )
81
119
}
82
120
83
121
struct Iterator : Sendable , IteratorProtocol {
84
122
/// The starting date for the recurrence
85
123
let start : Date
86
124
/// The recurrence rule that should be used for enumeration
87
125
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 ) ?
95
131
96
132
/// The start date's nanoseconds component
97
133
let startDateNanoseconds : TimeInterval
@@ -105,6 +141,10 @@ extension Calendar {
105
141
/// date, by the interval specified by the recurrence rule frequency
106
142
/// This does not include the start date itself.
107
143
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
+
108
148
109
149
/// How many elements we have consumed from `baseRecurrence`
110
150
var iterations : Int = 0
@@ -123,7 +163,8 @@ extension Calendar {
123
163
124
164
internal init ( start: Date ,
125
165
matching recurrence: RecurrenceRule ,
126
- range: Range < Date > ? ) {
166
+ lowerBound: Date ? ,
167
+ upperBound: ( bound: Date , inclusive: Bool ) ? ) {
127
168
// Copy the calendar if it's autoupdating
128
169
var recurrence = recurrence
129
170
if recurrence. calendar == . autoupdatingCurrent {
@@ -132,7 +173,6 @@ extension Calendar {
132
173
self . recurrence = recurrence
133
174
134
175
self . start = start
135
- self . range = range
136
176
137
177
let frequency = recurrence. frequency
138
178
@@ -215,10 +255,12 @@ extension Calendar {
215
255
secondAction = . expand
216
256
}
217
257
218
- if let range {
219
- rangeLowerBound = recurrence. calendar. dateInterval ( of: frequency. component, for: range. lowerBound) ? . start
258
+ self . lowerBound = lowerBound
259
+ self . upperBound = upperBound
260
+ if let lowerBound {
261
+ baseRecurrenceLowerBound = recurrence. calendar. dateInterval ( of: frequency. component, for: lowerBound) ? . start
220
262
} else {
221
- rangeLowerBound = nil
263
+ baseRecurrenceLowerBound = nil
222
264
}
223
265
224
266
// Create date components that enumerate recurrences without any
@@ -330,7 +372,7 @@ extension Calendar {
330
372
}
331
373
// If a range has been specified, we should skip a few extra
332
374
// occurrences until we reach the start date
333
- if let rangeLowerBound , nextDate < rangeLowerBound {
375
+ if let baseRecurrenceLowerBound , nextDate < baseRecurrenceLowerBound {
334
376
continue
335
377
}
336
378
anchor = nextDate
@@ -476,11 +518,18 @@ extension Calendar {
476
518
finished = true
477
519
return nil
478
520
}
479
- if let range = self . range {
480
- if date >= range. upperBound {
521
+ if let upperBound = self . upperBound {
522
+ let outOfRange = switch upperBound. inclusive {
523
+ case true : date > upperBound. bound
524
+ case false : date >= upperBound. bound
525
+ }
526
+ if outOfRange {
481
527
finished = true
482
528
return nil
483
- } else if date < range. lowerBound {
529
+ }
530
+ }
531
+ if let lowerBound = self . lowerBound {
532
+ if date < lowerBound {
484
533
continue
485
534
}
486
535
}
@@ -503,7 +552,7 @@ extension Calendar {
503
552
}
504
553
505
554
public func makeIterator( ) -> Iterator {
506
- return Iterator ( start: start, matching: recurrence, range : range )
555
+ return Iterator ( start: start, matching: recurrence, lowerBound : lowerBound , upperBound : upperBound )
507
556
}
508
557
}
509
558
}
0 commit comments