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
|
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)
}
}
|