@@ -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