summaryrefslogtreecommitdiffstats
path: root/Solsnu.Widget/Models/SolsticeData.swift
blob: 5a36da72775b96189bdc1b3c39476bc2a4a54235 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import Foundation

class SolsticeData {
    static let shared = SolsticeData()

    private let events: [SolsticeEvent]

    private init() {
        // Hardcoded solstice/equinox events for 2025-2030 (all in UTC)
        var allEvents: [SolsticeEvent] = []

        // 2025
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2025", date: SolsticeData.dateFromUTC(year: 2025, month: 3, day: 20, hour: 9, minute: 1), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2025", date: SolsticeData.dateFromUTC(year: 2025, month: 6, day: 20, hour: 14, minute: 42), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2025", date: SolsticeData.dateFromUTC(year: 2025, month: 9, day: 22, hour: 18, minute: 20), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2025", date: SolsticeData.dateFromUTC(year: 2025, month: 12, day: 21, hour: 15, minute: 3), season: .winter))

        // 2026
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2026", date: SolsticeData.dateFromUTC(year: 2026, month: 3, day: 20, hour: 14, minute: 46), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2026", date: SolsticeData.dateFromUTC(year: 2026, month: 6, day: 21, hour: 8, minute: 25), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2026", date: SolsticeData.dateFromUTC(year: 2026, month: 9, day: 23, hour: 0, minute: 6), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2026", date: SolsticeData.dateFromUTC(year: 2026, month: 12, day: 21, hour: 20, minute: 50), season: .winter))

        // 2027
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2027", date: SolsticeData.dateFromUTC(year: 2027, month: 3, day: 20, hour: 20, minute: 25), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2027", date: SolsticeData.dateFromUTC(year: 2027, month: 6, day: 21, hour: 14, minute: 11), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2027", date: SolsticeData.dateFromUTC(year: 2027, month: 9, day: 23, hour: 6, minute: 2), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2027", date: SolsticeData.dateFromUTC(year: 2027, month: 12, day: 22, hour: 2, minute: 43), season: .winter))

        // 2028
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2028", date: SolsticeData.dateFromUTC(year: 2028, month: 3, day: 20, hour: 2, minute: 17), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2028", date: SolsticeData.dateFromUTC(year: 2028, month: 6, day: 20, hour: 20, minute: 2), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2028", date: SolsticeData.dateFromUTC(year: 2028, month: 9, day: 22, hour: 11, minute: 45), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2028", date: SolsticeData.dateFromUTC(year: 2028, month: 12, day: 21, hour: 8, minute: 20), season: .winter))

        // 2029
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2029", date: SolsticeData.dateFromUTC(year: 2029, month: 3, day: 20, hour: 8, minute: 1), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2029", date: SolsticeData.dateFromUTC(year: 2029, month: 6, day: 21, hour: 1, minute: 48), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2029", date: SolsticeData.dateFromUTC(year: 2029, month: 9, day: 22, hour: 17, minute: 37), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2029", date: SolsticeData.dateFromUTC(year: 2029, month: 12, day: 21, hour: 14, minute: 14), season: .winter))

        // 2030
        allEvents.append(SolsticeEvent(name: "Spring Equinox 2030", date: SolsticeData.dateFromUTC(year: 2030, month: 3, day: 20, hour: 13, minute: 51), season: .spring))
        allEvents.append(SolsticeEvent(name: "Summer Solstice 2030", date: SolsticeData.dateFromUTC(year: 2030, month: 6, day: 21, hour: 7, minute: 31), season: .summer))
        allEvents.append(SolsticeEvent(name: "Autumn Equinox 2030", date: SolsticeData.dateFromUTC(year: 2030, month: 9, day: 22, hour: 23, minute: 27), season: .autumn))
        allEvents.append(SolsticeEvent(name: "Winter Solstice 2030", date: SolsticeData.dateFromUTC(year: 2030, month: 12, day: 21, hour: 20, minute: 9), season: .winter))

        // Sort events by date
        self.events = allEvents.sorted { $0.date < $1.date }
    }

    /// Helper function to create UTC dates (static to be usable during init)
    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()
    }

    /// Returns the next upcoming solstice/equinox event
    func nextEvent() -> SolsticeEvent? {
        let now = Date()
        return events.first { $0.date > now }
    }

    /// Returns the next N upcoming events
    func upcomingEvents(count: Int) -> [SolsticeEvent] {
        let now = Date()
        let futureEvents = events.filter { $0.date > now }
        return Array(futureEvents.prefix(count))
    }

    /// Returns the progress to the next event as (elapsed days, total days)
    func progressToNextEvent() -> (elapsed: Int, total: Int)? {
        guard let nextEvent = nextEvent() else {
            return nil
        }

        let now = Date()
        let today = Calendar.current.startOfDay(for: now)
        let eventDay = Calendar.current.startOfDay(for: nextEvent.date)

        // Find the previous event to calculate total days
        guard let previousEventIndex = events.firstIndex(where: { $0.date > now }) else {
            return nil
        }

        let previousEvent: Date
        if previousEventIndex > 0 {
            previousEvent = events[previousEventIndex - 1].date
        } else {
            // If this is the first event, we need to handle this case
            // Use the event itself minus some arbitrary period (not applicable for first event)
            return nil
        }

        let previousEventDay = Calendar.current.startOfDay(for: previousEvent)

        // Calculate elapsed and total days
        let elapsedComponents = Calendar.current.dateComponents([.day], from: previousEventDay, to: today)
        let totalComponents = Calendar.current.dateComponents([.day], from: previousEventDay, to: eventDay)

        let elapsed = max(0, elapsedComponents.day ?? 0)
        let total = max(1, totalComponents.day ?? 1)

        return (elapsed: elapsed, total: total)
    }
}