- 
                Notifications
    
You must be signed in to change notification settings  - Fork 207
 
Description
Description
Calendar.RecurrenceRule produces incorrect results or returns no results for ordinal weekday calculations (e.g., "1st Sunday", "last Friday") when the calendar's firstWeekday property is not set to 1 (Sunday). This affects users in some European or other locales where Monday (or another weekday) is the first day of the week.
Steps to Reproduce 1
import Foundation
// Create calendar with Monday as first day (standard in some countries in Europe)
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
calendar.firstWeekday = 2  // Monday
// Find the last Sunday of January 2024
let rule = Calendar.RecurrenceRule(
    calendar: calendar,
    frequency: .monthly,
    weekdays: [.nth(-1, .sunday)]  // -1 means "last"
)
let jan2024 = calendar.date(from: DateComponents(year: 2024, month: 1, day: 1))!
let feb2024 = calendar.date(from: DateComponents(year: 2024, month: 2, day: 1))!
let results = Array(rule.recurrences(of: jan2024, in: jan2024..<feb2024))
// Expected: January 28 (the last Sunday)
// Actual: No results returned!
print("Results count: \(results.count)")  // Prints "Results count: 0"Expected Results
Ordinal weekday calculations should return the same dates regardless of firstWeekday:
- Last Sunday of January 2024: January 28
 - 1st Sunday of January 2024: January 7
 
Actual Results
With firstWeekday = 2 (Monday):
- Last Sunday of January 2024: No result ❌
 - 1st Sunday of January 2024: January 14 ❌ (returns 2nd Sunday instead)
 
Full test results for "1st Sunday of January 2024":
firstWeekday=1(Sun): January 7 ✅firstWeekday=2(Mon): January 14 ❌ (wrong week)firstWeekday=3(Tue): January 7 ✅firstWeekday=4(Wed): January 7 ✅firstWeekday=5(Thu): January 7 ✅firstWeekday=6(Fri): January 7 ✅firstWeekday=7(Sat): January 7 ✅
Steps to Reproduce 2
import Foundation
// Create a calendar with Wednesday as first day
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
calendar.firstWeekday = 4  // Wednesday
// Find the 4th Thursday of January 2024
let rule = Calendar.RecurrenceRule(
    calendar: calendar,
    frequency: .monthly,
    weekdays: [.nth(4, .thursday)]
)
let startDate = calendar.date(from: DateComponents(year: 2024, month: 1, day: 1))!
let endDate = calendar.date(from: DateComponents(year: 2024, month: 2, day: 1))!
let results = Array(rule.recurrences(of: startDate, in: startDate..<endDate))
if let result = results.first {
    let day = calendar.component(.day, from: result)
    print("Got January \(day)")  // Prints "Got January 18" (incorrect)
}Expected Results
The 4th Thursday of January 2024 should always be January 25, regardless of the calendar's firstWeekday setting.
Actual Results
The calculation returns different (incorrect) dates depending on firstWeekday:
firstWeekday=1(Sun): January 25 ✅firstWeekday=2(Mon): January 25 ✅firstWeekday=3(Tue): January 18 ❌firstWeekday=4(Wed): January 18 ❌firstWeekday=5(Thu): January 18 ❌firstWeekday=6(Fri): January 25 ✅firstWeekday=7(Sat): January 25 ✅
Environment
- iOS 18+, macOS 15.5
 - Xcode 16.4
 
Workaround
Temporarily set calendar.firstWeekday = 1 before creating the RecurrenceRule:
var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale(identifier: "en_GB")  // UK locale (Monday first)
// ... configure calendar ...
// Workaround: Force Sunday as first day for calculation only
var calcCalendar = calendar
calcCalendar.firstWeekday = 1
let rule = Calendar.RecurrenceRule(
    calendar: calcCalendar,
    frequency: .monthly,
    weekdays: [.nth(-1, .sunday)]
)
// Now returns correct results