summaryrefslogtreecommitdiffstats
path: root/Solsnu.Widget/Utilities/SunTimes.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Solsnu.Widget/Utilities/SunTimes.swift')
-rw-r--r--Solsnu.Widget/Utilities/SunTimes.swift148
1 files changed, 0 insertions, 148 deletions
diff --git a/Solsnu.Widget/Utilities/SunTimes.swift b/Solsnu.Widget/Utilities/SunTimes.swift
deleted file mode 100644
index 8c7132e..0000000
--- a/Solsnu.Widget/Utilities/SunTimes.swift
+++ /dev/null
@@ -1,148 +0,0 @@
-import Foundation
-
-class SunTimes {
- let latitude: Double
- let longitude: Double
- let date: Date
-
- init(latitude: Double, longitude: Double, date: Date) {
- self.latitude = latitude
- self.longitude = longitude
- self.date = date
- }
-
- /// Calculate sunrise time for the location and date
- func sunrise() -> Date? {
- guard let result = calculateSunTimes() else { return nil }
- return result.sunrise
- }
-
- /// Calculate sunset time for the location and date
- func sunset() -> Date? {
- guard let result = calculateSunTimes() else { return nil }
- return result.sunset
- }
-
- // NOAA solar position algorithm
- // Reference: https://www.esrl.noaa.gov/gmd/grad/solcalc/
- private func calculateSunTimes() -> (sunrise: Date, sunset: Date)? {
- let calendar = Calendar(identifier: .gregorian)
- let components = calendar.dateComponents([.year, .month, .day], from: date)
- guard let year = components.year else {
- return nil
- }
-
- // Step 1: Calculate day of year
- let jan1 = calendar.date(from: DateComponents(year: year, month: 1, day: 1))!
- let dayOfYear = calendar.dateComponents([.day], from: jan1, to: date).day! + 1
-
- // Step 2: Fractional year in radians
- let daysInYear = Double(isLeapYear(year) ? 366 : 365)
- let gamma = 2.0 * Double.pi * Double(dayOfYear - 1) / daysInYear
-
- // Step 3: Solar declination (radians)
- let decl = 0.006918 - 0.399912 * cos(gamma) + 0.070257 * sin(gamma)
- - 0.006758 * cos(2.0 * gamma) + 0.000907 * sin(2.0 * gamma)
- - 0.002697 * cos(3.0 * gamma) + 0.00148 * sin(3.0 * gamma)
-
- // Step 4: Equation of time (minutes)
- let eot = 229.18 * (0.000075 + 0.001868 * cos(gamma) - 0.032077 * sin(gamma)
- - 0.014615 * cos(2.0 * gamma) - 0.040849 * sin(2.0 * gamma))
-
- // Step 5: Hour angle at sunrise/sunset
- let latRad = latitude * Double.pi / 180.0
- let cosH = -tan(latRad) * tan(decl)
-
- guard cosH >= -1.0 && cosH <= 1.0 else {
- // Sun is always up or always down at this latitude/date
- return nil
- }
-
- let h = acos(cosH) * 180.0 / Double.pi
-
- // Step 6: Solar noon in UTC (decimal hours)
- let solarNoonUTC = 12.0 - (longitude / 15.0) - (eot / 60.0)
-
- // Step 7: Sunrise and sunset in UTC (decimal hours)
- let sunriseUTC = solarNoonUTC - (h / 15.0)
- let sunsetUTC = solarNoonUTC + (h / 15.0)
-
- // Step 8: Convert to local time (add timezone offset)
- let tzOffset = Double(TimeZone.current.secondsFromGMT(for: date)) / 3600.0
- let sunriseLocal = sunriseUTC + tzOffset
- let sunsetLocal = sunsetUTC + tzOffset
-
- // Step 9: Create date components, handling day boundary crossing
- let currentCalendar = Calendar.current
- let baseComponents = currentCalendar.dateComponents([.year, .month, .day], from: date)
-
- // Sunrise
- var sunriseComp = baseComponents
- let srHourDouble = sunriseLocal
- let srHour = Int(srHourDouble)
- let srMinuteDouble = (srHourDouble - Double(srHour)) * 60.0
- let srMinute = Int(srMinuteDouble)
-
- if srHour < 0 {
- if let prevDay = currentCalendar.date(byAdding: .day, value: -1, to: date) {
- let prevComponents = currentCalendar.dateComponents([.year, .month, .day], from: prevDay)
- sunriseComp = prevComponents
- sunriseComp.hour = 24 + srHour
- } else {
- sunriseComp.hour = 0
- }
- } else if srHour >= 24 {
- if let nextDay = currentCalendar.date(byAdding: .day, value: 1, to: date) {
- let nextComponents = currentCalendar.dateComponents([.year, .month, .day], from: nextDay)
- sunriseComp = nextComponents
- sunriseComp.hour = srHour - 24
- } else {
- sunriseComp.hour = 23
- }
- } else {
- sunriseComp.hour = srHour
- }
- sunriseComp.minute = max(0, min(59, srMinute))
- sunriseComp.second = 0
-
- // Sunset
- var sunsetComp = baseComponents
- let ssHourDouble = sunsetLocal
- let ssHour = Int(ssHourDouble)
- let ssMinuteDouble = (ssHourDouble - Double(ssHour)) * 60.0
- let ssMinute = Int(ssMinuteDouble)
-
- if ssHour < 0 {
- if let prevDay = currentCalendar.date(byAdding: .day, value: -1, to: date) {
- let prevComponents = currentCalendar.dateComponents([.year, .month, .day], from: prevDay)
- sunsetComp = prevComponents
- sunsetComp.hour = 24 + ssHour
- } else {
- sunsetComp.hour = 0
- }
- } else if ssHour >= 24 {
- if let nextDay = currentCalendar.date(byAdding: .day, value: 1, to: date) {
- let nextComponents = currentCalendar.dateComponents([.year, .month, .day], from: nextDay)
- sunsetComp = nextComponents
- sunsetComp.hour = ssHour - 24
- } else {
- sunsetComp.hour = 23
- }
- } else {
- sunsetComp.hour = ssHour
- }
- sunsetComp.minute = max(0, min(59, ssMinute))
- sunsetComp.second = 0
-
- guard let sunriseDate = currentCalendar.date(from: sunriseComp),
- let sunsetDate = currentCalendar.date(from: sunsetComp) else {
- return nil
- }
-
- return (sunriseDate, sunsetDate)
- }
-
- private func isLeapYear(_ year: Int) -> Bool {
- return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
- }
-}