Skip to content

Commit 47bad03

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

7 files changed

Lines changed: 107 additions & 150 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: 18 additions & 16 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? {
@@ -537,7 +537,7 @@ extension Calendar {
537537
previouslyReturnedMatchDate: Date?) throws -> SearchStepResult {
538538

539539
// Fast-path: ask the calendar directly. Returns nil for unrecognized patterns.
540-
if _supportsNextDateFastPath(for: matchingComponents) && matchingPolicy == .nextTime && repeatedTimePolicy == .first {
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
}
@@ -1659,12 +1659,14 @@ extension Calendar {
16591659
if month != dateMonth {
16601660
// Fast-path: advance to target month directly.
16611661
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
1662+
if _supportsNextDateFastPath(for: [.month]) {
1663+
var minimal = DateComponents()
1664+
minimal.month = month
1665+
minimal.isLeapMonth = components.isLeapMonth
1666+
if let fast = _calendarNextDate(after: result, matching: minimal, direction: direction) {
1667+
result = fast
1668+
dateMonth = month
1669+
}
16681670
}
16691671
}
16701672
if month != dateMonth {
@@ -1761,8 +1763,8 @@ extension Calendar {
17611763
if components.month == nil, components.weekday != nil {
17621764
var enriched = components
17631765
enriched.month = component(.month, from: startingAt)
1764-
if let fast = _calendarNextDate(after: startingAt, matching: enriched, direction: direction) {
1765-
return fast
1766+
if _supportsNextDateFastPath(for: enriched._populatedComponentSet) {
1767+
return _calendarNextDate(after: startingAt, matching: enriched, direction: direction)
17661768
}
17671769
}
17681770

@@ -1856,8 +1858,8 @@ extension Calendar {
18561858
}
18571859

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

18631865
// After this point, result is at least startDate
@@ -1967,10 +1969,10 @@ extension Calendar {
19671969
}
19681970

19691971
// 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
1972+
if _supportsNextDateFastPath(for: [.weekday]) {
1973+
var minimal = DateComponents()
1974+
minimal.weekday = weekday
1975+
return _calendarNextDate(after: startingAt, matching: minimal, direction: direction)
19741976
}
19751977

19761978
// 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)