fix(display): use stadium timezone for all game time displays
Game times were incorrectly using device timezone in several views. Now all game time displays use the stadium's local timezone via RichGame.localGameTime/localGameTimeShort properties. Fixes: - PDFGenerator: was using game.gameDate (midnight UTC) instead of actual time - GameRowCompact: was formatting with device timezone - TimelineGameRow: was using .formatted() with device timezone Also adds Date+GameTime.swift extension for centralized timezone formatting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
70
SportsTime/Core/Extensions/Date+GameTime.swift
Normal file
70
SportsTime/Core/Extensions/Date+GameTime.swift
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Date+GameTime.swift
|
||||
// SportsTime
|
||||
//
|
||||
// Centralized game time formatting with timezone support.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Date {
|
||||
|
||||
/// Formats the date as a game time string in the specified timezone.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - timeZone: The timezone to display the time in (typically the stadium's timezone).
|
||||
/// Falls back to the device's current timezone if nil.
|
||||
/// - includeZone: Whether to include the timezone abbreviation (e.g., "EDT", "PST").
|
||||
/// - Returns: A formatted time string like "7:00 PM" or "7:00 PM EDT".
|
||||
///
|
||||
/// Usage:
|
||||
/// ```swift
|
||||
/// // Without timezone indicator (for UI cards)
|
||||
/// game.dateTime.gameTimeString(in: stadium.timeZone) // "7:00 PM"
|
||||
///
|
||||
/// // With timezone indicator (for exports, detail views)
|
||||
/// game.dateTime.gameTimeString(in: stadium.timeZone, includeZone: true) // "7:00 PM EDT"
|
||||
/// ```
|
||||
func gameTimeString(in timeZone: TimeZone?, includeZone: Bool = false) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = includeZone ? "h:mm a z" : "h:mm a"
|
||||
formatter.timeZone = timeZone ?? .current
|
||||
return formatter.string(from: self)
|
||||
}
|
||||
|
||||
/// Formats the date as a full game date and time string in the specified timezone.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - timeZone: The timezone to display in (typically the stadium's timezone).
|
||||
/// - includeZone: Whether to include the timezone abbreviation.
|
||||
/// - Returns: A formatted string like "Sat, Jan 18 at 7:00 PM" or "Sat, Jan 18 at 7:00 PM EDT".
|
||||
func gameDateTimeString(in timeZone: TimeZone?, includeZone: Bool = false) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = includeZone ? "EEE, MMM d 'at' h:mm a z" : "EEE, MMM d 'at' h:mm a"
|
||||
formatter.timeZone = timeZone ?? .current
|
||||
return formatter.string(from: self)
|
||||
}
|
||||
|
||||
/// Returns the start of day in the specified timezone.
|
||||
///
|
||||
/// This is useful for grouping games by calendar day in the stadium's local timezone,
|
||||
/// not the user's device timezone.
|
||||
///
|
||||
/// - Parameter timeZone: The timezone to calculate start of day in.
|
||||
/// - Returns: A Date representing midnight in the specified timezone.
|
||||
func startOfDay(in timeZone: TimeZone?) -> Date {
|
||||
var calendar = Calendar.current
|
||||
calendar.timeZone = timeZone ?? .current
|
||||
return calendar.startOfDay(for: self)
|
||||
}
|
||||
|
||||
/// Returns the calendar day components in the specified timezone.
|
||||
///
|
||||
/// - Parameter timeZone: The timezone to extract components in.
|
||||
/// - Returns: DateComponents with year, month, and day.
|
||||
func calendarDay(in timeZone: TimeZone?) -> DateComponents {
|
||||
var calendar = Calendar.current
|
||||
calendar.timeZone = timeZone ?? .current
|
||||
return calendar.dateComponents([.year, .month, .day], from: self)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user