Skip to content

Commit ace817c

Browse files
authored
[Proposal] SF-NNNN Search for recurrence in partial ranges (#1457)
1 parent 702ff86 commit ace817c

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Search for recurrence in partial ranges
2+
3+
* Proposal: SF-NNNN
4+
* Author: Hristo Staykov <https://github.com/hristost>
5+
* Implementation: [#1456](https://github.com/swiftlang/swift-foundation/pull/1456)
6+
* Status: **Draft**
7+
8+
## Revision history
9+
10+
* **v1** Initial version
11+
12+
## Introduction
13+
14+
In [SF-0009](0009-calendar-recurrence-rule.md) we introduced `Calendar.RecurrenceRule`. With this API, we can find occurences of a recurring event in a given range:
15+
16+
```swift
17+
let birthday = Date(timeIntervalSince1970: 813283200.0) // 1995-10-10T00:00:00-0000
18+
let rangeStart = Date(timeIntervalSince1970: 946684800.0) // 2000-01-01T00:00:00-0000
19+
let rangeEnd = Date(timeIntervalSince1970: 1293840000.0) // 2011-01-01T00:00:00-0000
20+
21+
let recurrence = Calendar.RecurrenceRule(calendar: .current, frequency: .yearly)
22+
for date in recurrence.recurrences(of: birthday, in: rangeStart..<rangeEnd) {
23+
// All occurrences of `birthday` between 2000 and 2010
24+
}
25+
```
26+
27+
However, enumerating recurrences in a partial range is not supported: the user has to enumerate over a larger range, and discard results that are not necessary:
28+
29+
```swift
30+
for date in recurrence.recurrences(of: birthday) where date >= rangeStart {
31+
// All occurrences of `birthday` after 2000
32+
}
33+
```
34+
35+
or specify a range that stretches to `Date.distantPast` or `Date.distantFuture`:
36+
37+
```swift
38+
for date in recurrence.recurrences(of: birthday, in: rangeStart..<Date.distantFuture) {
39+
// All occurrences of `birthday` after 2000
40+
}
41+
```
42+
43+
This proposal adds a method similar to `recurrences` that allows specifying partial ranges.
44+
45+
46+
## Detailed design
47+
48+
```swift
49+
public extension Calendar.RecurrenceRule.End {
50+
@available(FoundationPreview 6.3, *)
51+
public func recurrences(of start: Date,
52+
in range: PartialRangeThrough<Date>
53+
) -> some (Sequence<Date> & Sendable)
54+
@available(FoundationPreview 6.3, *)
55+
public func recurrences(of start: Date,
56+
in range: PartialRangeTo<Date>
57+
) -> some (Sequence<Date> & Sendable)
58+
@available(FoundationPreview 6.3, *)
59+
public func recurrences(of start: Date,
60+
in range: PartialRangeFrom<Date>
61+
) -> some (Sequence<Date> & Sendable)
62+
@available(FoundationPreview 6.3, *)
63+
public func recurrences(of start: Date,
64+
in range: ClosedRange<Date>
65+
) -> some (Sequence<Date> & Sendable)
66+
}
67+
```
68+
69+
70+
With this, the above example would simply become:
71+
72+
```swift
73+
for date in recurrence.recurrences(of: birthday, in: rangeStart...) {
74+
// All occurrences of `birthday` after 2000
75+
}
76+
```
77+
78+
79+
## Impact on existing code
80+
81+
None.
82+
83+
## Alternatives considered
84+
85+
This API is a convenience over the workarounds presented in the introduction, but it
86+
is also more performant since we don't calculate dates we don't need in the final range.
87+
88+
For cases where we're looking for recurrences up until a date, it might be tempting to set the `end` property of the recurrence rule to the end of the range:
89+
```swift
90+
recurrence.end = .afterDate(rangeEnd)
91+
```
92+
That is not advised for the recurrence rule might already have an `end` property of `.afterOcurrences()`. Besides, the recurrence rule struct represents when the event occurs, and the range in which we search is does not change that.
93+
94+
We did consider instead only adding one method that accepts a `RangeExpression<Date>` argument. However, that means supporting any arbitrary ranges conforming to the protocol, in which cases we may not have lower and upper bounds that allow us to optimize search.

0 commit comments

Comments
 (0)