@@ -232,24 +232,40 @@ extension Calendar {
232
232
}
233
233
var componentsForEnumerating = recurrence. calendar. _dateComponents ( components, from: start)
234
234
235
- let rangeForBaseRecurrence : Range < Date > ? = nil
236
- var baseRecurrenceStartDate = start
237
- if componentsForEnumerating. day != nil , dayOfMonthAction == . expand || weekdayAction == . expand {
235
+ let expansionChangesDay = dayOfYearAction == . expand || dayOfMonthAction == . expand || weekAction == . expand || weekdayAction == . expand
236
+ let expansionChangesMonth = dayOfYearAction == . expand || monthAction == . expand || weekAction == . expand
237
+
238
+ if expansionChangesDay, componentsForEnumerating. day != nil {
238
239
// If we expand either the day of the month or weekday, then
239
240
// the day of month is likely to not match that of the start
240
241
// date. Reset it to 1 in the base recurrence as to not skip
241
242
// "invalid" anchors, such as February 30
242
243
componentsForEnumerating. day = 1
243
244
}
244
- if componentsForEnumerating. month != nil , monthAction == . expand {
245
+ if expansionChangesMonth , componentsForEnumerating. month != nil {
245
246
// Likewise, if we will be changing the month, reset it to 1
246
247
// in case the start date falls on a leap month
247
248
componentsForEnumerating. month = 1
248
249
componentsForEnumerating. isLeapMonth = nil
249
250
}
251
+ if expansionChangesDay || expansionChangesMonth, weekAction == . expand, weekdayAction != . expand {
252
+ // If we are expanding weeks, all expansions in a given year
253
+ // will have the same weekday. Above we have reset the month
254
+ // or the day of the month, so we also changed that weekday.
255
+
256
+ // To specify a yearly recurrence which starts from the same
257
+ // weekday, and which doesn't start from a leap day / month,
258
+ // simply use `dayOfYear` of the start date
259
+ componentsForEnumerating. day = nil
260
+ componentsForEnumerating. month = nil
261
+ componentsForEnumerating. isLeapMonth = nil
262
+ let daysInWeek = recurrence. calendar. maximumRange ( of: . weekday) !. count
263
+ componentsForEnumerating. dayOfYear = recurrence. calendar. component ( . dayOfYear, from: start) % daysInWeek // mod 7 to get the same weekday in the beginning of the year, so it's guaranteed to always exist
264
+ }
265
+
250
266
baseRecurrence = Calendar . DatesByMatching ( calendar: recurrence. calendar,
251
267
start: start,
252
- range: rangeForBaseRecurrence ,
268
+ range: nil ,
253
269
matchingComponents: componentsForEnumerating,
254
270
matchingPolicy: recurrence. matchingPolicy,
255
271
repeatedTimePolicy: recurrence. repeatedTimePolicy,
@@ -349,6 +365,9 @@ extension Calendar {
349
365
componentCombinations. weekdays = recurrence. weekdays
350
366
componentCombinations. daysOfYear = nil
351
367
componentCombinations. daysOfMonth = nil
368
+ if recurrence. frequency == . yearly, monthAction != . expand {
369
+ componentCombinations. months = nil
370
+ }
352
371
} else if recurrence. frequency == . weekly || weekAction == . expand {
353
372
if let weekdayIdx = components. weekday, let weekday = Locale . Weekday ( weekdayIdx) {
354
373
// In a weekly recurrence (or one that expands weeks of year), we want results to fall on the same weekday as the initial date
@@ -360,6 +379,11 @@ extension Calendar {
360
379
if weekAction == . expand {
361
380
// In a yearly recurrence with weeks specified, results do not land on any specific month
362
381
componentCombinations. weeksOfYear = recurrence. weeks
382
+ if componentCombinations. weekdays == nil {
383
+ if let weekdayIdx = components. weekday, let weekday = Locale . Weekday ( weekdayIdx) {
384
+ componentCombinations. weekdays = [ . every( weekday) ]
385
+ }
386
+ }
363
387
componentCombinations. months = nil
364
388
}
365
389
if recurrence. frequency != . hourly, recurrence. frequency != . minutely {
0 commit comments