import Foundation class SolsticeData { static let shared = SolsticeData() private let events: [SolsticeEvent] private init() { let table: [(year: Int, month: Int, day: Int, hour: Int, minute: Int, season: Season)] = [ // 2025 (2025, 3, 20, 9, 1, .spring), (2025, 6, 20, 14, 42, .summer), (2025, 9, 22, 18, 20, .autumn), (2025, 12, 21, 15, 3, .winter), // 2026 (2026, 3, 20, 14, 46, .spring), (2026, 6, 21, 8, 25, .summer), (2026, 9, 23, 0, 6, .autumn), (2026, 12, 21, 20, 50, .winter), // 2027 (2027, 3, 20, 20, 25, .spring), (2027, 6, 21, 14, 11, .summer), (2027, 9, 23, 6, 2, .autumn), (2027, 12, 22, 2, 43, .winter), // 2028 (2028, 3, 20, 2, 17, .spring), (2028, 6, 20, 20, 2, .summer), (2028, 9, 22, 11, 45, .autumn), (2028, 12, 21, 8, 20, .winter), // 2029 (2029, 3, 20, 8, 1, .spring), (2029, 6, 21, 1, 48, .summer), (2029, 9, 22, 17, 37, .autumn), (2029, 12, 21, 14, 14, .winter), // 2030 (2030, 3, 20, 13, 51, .spring), (2030, 6, 21, 7, 31, .summer), (2030, 9, 22, 23, 27, .autumn), (2030, 12, 21, 20, 9, .winter), ] events = table .map { row in SolsticeEvent( date: SolsticeData.dateFromUTC(year: row.year, month: row.month, day: row.day, hour: row.hour, minute: row.minute), season: row.season ) } .sorted { $0.date < $1.date } } private static func dateFromUTC(year: Int, month: Int, day: Int, hour: Int, minute: Int) -> Date { var components = DateComponents() components.year = year components.month = month components.day = day components.hour = hour components.minute = minute components.second = 0 components.timeZone = TimeZone(abbreviation: "UTC") return Calendar(identifier: .gregorian).date(from: components) ?? Date() } func nextEvent(from now: Date = Date()) -> SolsticeEvent? { return events.first(where: { $0.date > now }) } func currentEvent(from now: Date = Date()) -> SolsticeEvent? { guard let nextIndex = events.firstIndex(where: { $0.date > now }), nextIndex > 0 else { return nil } return events[nextIndex - 1] } func upcomingEvents(count: Int, from now: Date = Date()) -> [SolsticeEvent] { let futureEvents = events.filter { $0.date > now } return Array(futureEvents.prefix(count)) } func progressToNextEvent(from now: Date = Date()) -> (elapsed: Int, total: Int)? { guard let nextIndex = events.firstIndex(where: { $0.date > now }), nextIndex > 0 else { return nil } let today = Calendar.current.startOfDay(for: now) let previousEventDay = Calendar.current.startOfDay(for: events[nextIndex - 1].date) let eventDay = Calendar.current.startOfDay(for: events[nextIndex].date) let elapsed = max(0, Calendar.current.dateComponents([.day], from: previousEventDay, to: today).day ?? 0) let total = max(1, Calendar.current.dateComponents([.day], from: previousEventDay, to: eventDay).day ?? 1) return (elapsed: elapsed, total: total) } }