Fix game times with UTC data, restructure schedule by date

- Update games_canonical.json to use ISO 8601 UTC timestamps (game_datetime_utc)
- Fix BootstrapService timezone-aware parsing for venue-local fallback
- Fix thread-unsafe shared DateFormatter in RichGame local time display
- Bump SchemaVersion to 4 to force re-bootstrap with correct UTC data
- Restructure schedule view: group by date instead of sport, with sport
  icons on each row and date section headers showing game counts
- Fix schedule row backgrounds using Theme.cardBackground instead of black
- Sort games by UTC time with local-time tiebreaker for same-instant games

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-19 11:43:39 -06:00
parent e6c4b8e12b
commit 999b5a1190
12 changed files with 13387 additions and 26877 deletions

View File

@@ -32,8 +32,8 @@ final class ScheduleViewModel {
// MARK: - Pre-computed Groupings (avoid computed property overhead)
/// Games grouped by sport - pre-computed to avoid re-grouping on every render
private(set) var gamesBySport: [(sport: Sport, games: [RichGame])] = []
/// Games grouped by date - pre-computed to avoid re-grouping on every render
private(set) var gamesByDate: [(date: Date, games: [RichGame])] = []
/// All games matching current filters (before any display limiting)
private var filteredGames: [RichGame] = []
@@ -193,15 +193,20 @@ final class ScheduleViewModel {
}
}
// Step 2: Pre-compute grouping by sport (done once, not per-render)
let grouped = Dictionary(grouping: filteredGames) { $0.game.sport }
gamesBySport = grouped
.sorted { lhs, rhs in
let lhsIndex = Sport.allCases.firstIndex(of: lhs.key) ?? 0
let rhsIndex = Sport.allCases.firstIndex(of: rhs.key) ?? 0
return lhsIndex < rhsIndex
}
.map { (sport: $0.key, games: $0.value.sorted { $0.game.dateTime < $1.game.dateTime }) }
// Step 2: Pre-compute grouping by date (done once, not per-render)
let calendar = Calendar.current
let grouped = Dictionary(grouping: filteredGames) { calendar.startOfDay(for: $0.game.dateTime) }
gamesByDate = grouped
.sorted { $0.key < $1.key }
.map { (date: $0.key, games: $0.value.sorted { lhs, rhs in
if lhs.game.dateTime == rhs.game.dateTime {
// Same UTC time: sort by local display time (earlier local times first)
let lhsOffset = lhs.stadium.timeZone?.secondsFromGMT(for: lhs.game.dateTime) ?? 0
let rhsOffset = rhs.stadium.timeZone?.secondsFromGMT(for: rhs.game.dateTime) ?? 0
return lhsOffset < rhsOffset
}
return lhs.game.dateTime < rhs.game.dateTime
}) }
}
}