Skip to content

Commit 76b9730

Browse files
committed
Address third round of PR #2028 review feedback
1 parent 0dd92f5 commit 76b9730

7 files changed

Lines changed: 109 additions & 151 deletions

File tree

Sources/FoundationEssentials/Calendar/Calendar.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,7 +1285,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
12851285
_calendar.nextDate(after: date, matching: components, direction: direction)
12861286
}
12871287

1288-
internal func _supportsNextDateFastPath(for components: DateComponents) -> Bool {
1288+
internal func _supportsNextDateFastPath(for components: ComponentSet) -> Bool {
12891289
_calendar.supportsNextDateFastPath(for: components)
12901290
}
12911291

@@ -1309,7 +1309,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
13091309
@available(iOS 8.0, *)
13101310
public func enumerateDates(startingAfter start: Date, matching components: DateComponents, matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy = .first, direction: SearchDirection = .forward, using block: (_ result: Date?, _ exactMatch: Bool, _ stop: inout Bool) -> Void) {
13111311
// Fast-path: drive the loop with direct nextDate calls when default policies are in effect.
1312-
if matchingPolicy == .nextTime && repeatedTimePolicy == .first, _supportsNextDateFastPath(for: components) {
1312+
if matchingPolicy == .nextTime && repeatedTimePolicy == .first, _supportsNextDateFastPath(for: components._populatedComponentSet) {
13131313
var current = start
13141314
var stop = false
13151315
while !stop {

Sources/FoundationEssentials/Calendar/CalendarUtility.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ internal enum _CalendarUtility {
7171
// MARK: - isDateInWeekend
7272

7373
/// Whether `weekday` + `timeInDay` falls within `weekendRange`.
74-
static func isDateInWeekend(weekday: Int, timeInDay: TimeInterval,
75-
weekendRange: WeekendRange) -> Bool {
74+
static func isDateInWeekend(weekday: Int, timeInDay: TimeInterval, weekendRange: WeekendRange) -> Bool {
7675
if weekendRange.start == weekendRange.end && weekday != weekendRange.start {
7776
return false
7877
} else if weekendRange.start < weekendRange.end && (weekday < weekendRange.start || weekday > weekendRange.end) {

Sources/FoundationEssentials/Calendar/Calendar_Enumerate.swift

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ extension Calendar {
331331
let validates = matchingComponents._validate(for: calendar)
332332
finished = !validates
333333

334-
self.usesFastPath = validates && matchingPolicy == .nextTime && repeatedTimePolicy == .first && calendar._supportsNextDateFastPath(for: matchingComponents)
334+
self.usesFastPath = validates && matchingPolicy == .nextTime && repeatedTimePolicy == .first && calendar._supportsNextDateFastPath(for: matchingComponents._populatedComponentSet)
335335
}
336336

337337
mutating func next() -> Element? {
@@ -536,11 +536,12 @@ extension Calendar {
536536
inSearchingDate searchingDate: Date,
537537
previouslyReturnedMatchDate: Date?) throws -> SearchStepResult {
538538

539-
// Fast-path: ask the calendar directly. Returns nil for unrecognized patterns.
540-
if _supportsNextDateFastPath(for: matchingComponents) && matchingPolicy == .nextTime && repeatedTimePolicy == .first {
539+
// Fast-path: ask the calendar directly.
540+
if _supportsNextDateFastPath(for: matchingComponents._populatedComponentSet) && matchingPolicy == .nextTime && repeatedTimePolicy == .first {
541541
if let fast = _calendarNextDate(after: searchingDate, matching: matchingComponents, direction: direction) {
542542
return SearchStepResult(result: (fast, true), newSearchDate: fast)
543543
}
544+
return SearchStepResult(result: nil, newSearchDate: searchingDate)
544545
}
545546

546547
// Step A: Call helper method that does the searching
@@ -1659,12 +1660,14 @@ extension Calendar {
16591660
if month != dateMonth {
16601661
// Fast-path: advance to target month directly.
16611662
if !isLeapMonthDesired || !strictMatching {
1662-
var minimal = DateComponents()
1663-
minimal.month = month
1664-
minimal.isLeapMonth = components.isLeapMonth
1665-
if let fast = _calendarNextDate(after: result, matching: minimal, direction: direction) {
1666-
result = fast
1667-
dateMonth = month
1663+
if _supportsNextDateFastPath(for: [.month]) {
1664+
var minimal = DateComponents()
1665+
minimal.month = month
1666+
minimal.isLeapMonth = components.isLeapMonth
1667+
if let fast = _calendarNextDate(after: result, matching: minimal, direction: direction) {
1668+
result = fast
1669+
dateMonth = month
1670+
}
16681671
}
16691672
}
16701673
if month != dateMonth {
@@ -1761,8 +1764,8 @@ extension Calendar {
17611764
if components.month == nil, components.weekday != nil {
17621765
var enriched = components
17631766
enriched.month = component(.month, from: startingAt)
1764-
if let fast = _calendarNextDate(after: startingAt, matching: enriched, direction: direction) {
1765-
return fast
1767+
if _supportsNextDateFastPath(for: enriched._populatedComponentSet) {
1768+
return _calendarNextDate(after: startingAt, matching: enriched, direction: direction)
17661769
}
17671770
}
17681771

@@ -1856,8 +1859,8 @@ extension Calendar {
18561859
}
18571860

18581861
// Fast-path: ask the calendar directly.
1859-
if let fast = _calendarNextDate(after: startingAt, matching: components, direction: direction) {
1860-
return fast
1862+
if _supportsNextDateFastPath(for: components._populatedComponentSet) {
1863+
return _calendarNextDate(after: startingAt, matching: components, direction: direction)
18611864
}
18621865

18631866
// After this point, result is at least startDate
@@ -1967,10 +1970,10 @@ extension Calendar {
19671970
}
19681971

19691972
// Fast-path: use minimal {weekday}-only components.
1970-
var minimal = DateComponents()
1971-
minimal.weekday = weekday
1972-
if let fast = _calendarNextDate(after: startingAt, matching: minimal, direction: direction) {
1973-
return fast
1973+
if _supportsNextDateFastPath(for: [.weekday]) {
1974+
var minimal = DateComponents()
1975+
minimal.weekday = weekday
1976+
return _calendarNextDate(after: startingAt, matching: minimal, direction: direction)
19741977
}
19751978

19761979
// After this point, result is at least startDate

Sources/FoundationEssentials/Calendar/Calendar_Hebrew.swift

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,26 @@ internal final class _CalendarHebrew: _CalendarProtocol, @unchecked Sendable {
9292
return _CalendarHebrew(identifier: identifier, timeZone: args.timeZone, locale: args.locale, firstWeekday: args.firstWeekday, minimumDaysInFirstWeek: args.minimumDaysInFirstWeek, gregorianStartDate: nil)
9393
}
9494

95-
// hash(into:) uses the `_CalendarProtocol` default impl.
95+
func hash(into hasher: inout Hasher) {
96+
hasher.combine(identifier)
97+
hasher.combine(timeZone)
98+
hasher.combine(firstWeekday)
99+
hasher.combine(minimumDaysInFirstWeek)
100+
hasher.combine(localeIdentifier)
101+
hasher.combine(preferredFirstWeekday)
102+
hasher.combine(preferredMinimumDaysInFirstweek)
103+
}
96104

97-
func supportsNextDateFastPath(for components: DateComponents) -> Bool {
98-
if components.era != nil || components.year != nil || components.weekOfYear != nil || components.yearForWeekOfYear != nil || components.dayOfYear != nil {
105+
func supportsNextDateFastPath(for components: Calendar.ComponentSet) -> Bool {
106+
if components.contains(.era) || components.contains(.year) || components.contains(.weekOfYear) || components.contains(.yearForWeekOfYear) || components.contains(.dayOfYear) {
99107
return false
100108
}
101109

102-
let hasMonth = components.month != nil
103-
let hasDay = components.day != nil
104-
let hasWeekday = components.weekday != nil
105-
let hasWdOrd = components.weekdayOrdinal != nil
106-
let hasWeekOfMonth = components.weekOfMonth != nil
110+
let hasMonth = components.contains(.month)
111+
let hasDay = components.contains(.day)
112+
let hasWeekday = components.contains(.weekday)
113+
let hasWdOrd = components.contains(.weekdayOrdinal)
114+
let hasWeekOfMonth = components.contains(.weekOfMonth)
107115

108116
if hasWeekOfMonth && !(hasMonth && hasWeekday && !hasDay && !hasWdOrd) { return false }
109117
if hasWdOrd && !(hasWeekday && !hasDay && !hasWeekOfMonth) { return false }
@@ -112,7 +120,7 @@ internal final class _CalendarHebrew: _CalendarProtocol, @unchecked Sendable {
112120

113121
let timeOnly = !hasMonth && !hasDay && !hasWeekday
114122
if timeOnly {
115-
guard components.hour != nil, components.minute != nil, components.second != nil else { return false }
123+
guard components.contains(.hour), components.contains(.minute), components.contains(.second) else { return false }
116124
}
117125

118126
return true
@@ -974,7 +982,7 @@ internal final class _CalendarHebrew: _CalendarProtocol, @unchecked Sendable {
974982

975983
package func nextDate(after date: Date, matching components: DateComponents,
976984
direction: Calendar.SearchDirection) -> Date? {
977-
guard supportsNextDateFastPath(for: components) else { return nil }
985+
guard supportsNextDateFastPath(for: components._populatedComponentSet) else { return nil }
978986

979987
let hasMonth = components.month != nil
980988
let hasDay = components.day != nil

Sources/FoundationEssentials/Calendar/Calendar_Protocol.swift

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ package protocol _CalendarProtocol: AnyObject, Sendable, CustomDebugStringConver
5454
func nextDate(after date: Date, matching components: DateComponents, direction: Calendar.SearchDirection) -> Date?
5555

5656
/// Whether this calendar can fast path the given pattern. Default returns false; calendar implementations override for patterns they handle.
57-
func supportsNextDateFastPath(for components: DateComponents) -> Bool
57+
func supportsNextDateFastPath(for components: Calendar.ComponentSet) -> Bool
5858

5959
#if FOUNDATION_FRAMEWORK
6060
func bridgeToNSCalendar() -> NSCalendar
@@ -66,7 +66,7 @@ extension _CalendarProtocol {
6666
nil
6767
}
6868

69-
package func supportsNextDateFastPath(for components: DateComponents) -> Bool { false }
69+
package func supportsNextDateFastPath(for components: Calendar.ComponentSet) -> Bool { false }
7070

7171
package var preferredFirstWeekday: Int? { nil }
7272
package var preferredMinimumDaysInFirstweek: Int? { nil }
@@ -81,16 +81,5 @@ extension _CalendarProtocol {
8181
// We use this to provide a consistent answer for hashing and equality -- null is equal to an empty string
8282
locale?.identifier ?? ""
8383
}
84-
85-
/// Default `hash(into:)` for the standard calendar identity tuple. Calendars with non-standard hashing (e.g. `_CalendarAutoupdating`, `_CalendarICU`, `_CalendarBridged`) override this.
86-
package func hash(into hasher: inout Hasher) {
87-
hasher.combine(identifier)
88-
hasher.combine(timeZone)
89-
hasher.combine(firstWeekday)
90-
hasher.combine(minimumDaysInFirstWeek)
91-
hasher.combine(localeIdentifier)
92-
hasher.combine(preferredFirstWeekday)
93-
hasher.combine(preferredMinimumDaysInFirstweek)
94-
}
9584
}
9685

0 commit comments

Comments
 (0)