Je suis en train de développer une application qui a une fonction pour entrer en mode nuit/nuit pendant les heures de nuit automatiquement. L'application demande l'emplacement de l'utilisateur et détermine l'heure de lever/coucher du soleil (en heure universelle) en utilisant this algorithm.Confusion à propos de NSTimeZone.secondsFromGMT
La seule étape qui n'est pas claire est de convertir de UT à l'heure locale, car cela n'est pas expliqué dans l'algorithme. Dites que je reçois un temps de lever du soleil de 8,5 (8h30 le matin UT). Comment puis-je le convertir en heure locale de l'utilisateur pour vérifier s'il fait jour ou nuit? Ou, de manière équivalente, comment puis-je convertir l'heure locale de l'utilisateur en UT afin de pouvoir les comparer? Jusqu'à présent, j'ai essayé d'utiliser NSCalendar
pour obtenir le NSDateComponents
de la date actuelle (NSDate()
). Un de ces composants est un NSTimeZone?
à partir de laquelle je peux obtenir le secondsFromGMT
. Quelque chose comme ceci:
let dateComponents = NSCalendar.currentCalendar().components([.TimeZone], fromDate: NSDate())
let localOffset = Double(dateComponents.timeZone?.secondsFromGMT ?? 0)/3600
où localOffset
devrait être la différence de temps (en heures) de UT (par exemple GMT si je suis à droite) à l'heure locale, par défaut à 0 si dateComponents.timeZone == nil
(je ne sais pas dans quelles situations cela pourrait arriver). Le problème est que je reçois le même localOffset
pour maintenant que pour 6 mois dans le futur (quand l'heure d'été sera différente de ce qu'elle est maintenant à mon emplacement, l'Espagne). Est-ce que cela signifie que j'ai besoin d'utiliser les propriétés daylightSavingTime
et/ou daylightSavingTimeOffset
ainsi que secondsFromGMT
? Cela ne compte-t-il pas secondsFromGMT
? Les choses deviennent encore plus confuses pour moi lorsque je lis les résultats de l'algorithme. L'heure du coucher du soleil (en heure locale) est exactement celle donnée par Google, mais l'heure de lever du soleil est d'une heure en avance sur ce que Google dit (pour mon lieu et ma date). Je partage avec vous toute la mise en œuvre de l'algorithme Swift en espérant que cela puisse aider quelqu'un à voir ce que je fais de mal.
import Foundation
import CoreLocation
enum SunriseSunsetZenith: Double {
case Official = 90.83
case Civil = 96
case Nautical = 102
case Astronomical = 108
}
func sunriseSunsetHoursForLocation(coordinate: CLLocationCoordinate2D, atDate date: NSDate = NSDate(), zenith: SunriseSunsetZenith = .Civil) -> (sunrise: Double, sunset: Double) {
// Initial values (will be changed later)
var sunriseTime = 7.5
var sunsetTime = 19.5
// Get the longitude and latitude
let latitude = coordinate.latitude
let longitude = coordinate.longitude
// Get the day, month, year and local offset
let dateComponents = NSCalendar.currentCalendar().components([.Day, .Month, .Year, .TimeZone], fromDate: date)
let day = Double(dateComponents.day)
let month = Double(dateComponents.month)
let year = Double(dateComponents.year)
let localOffset = Double(dateComponents.timeZone?.daylightSavingTimeOffset ?? 0)/3600
// Calculate the day of the year
let N1 = floor(275*month/9)
let N2 = floor((month + 9)/12)
let N3 = 1 + floor((year - 4*floor(year/4) + 2)/3)
let dayOfYear = N1 - N2*N3 + day - 30
for i in 0...1 {
// Convert the longitude to hour value and calculate an approximate time
let longitudeHour = longitude/15
let t = dayOfYear + ((i == 0 ? 6.0 : 18.0) - longitudeHour)/24
// Calculate the Sun's mean anomaly
let M = 0.9856*t - 3.289
// Calculate the Sun's true longitude
var L = M + 1.916*sind(M) + 0.020*sind(2*M) + 282.634
L %= 360
// Calculate the Sun's right ascension
var RA = atand(0.91764 * tand(L))
RA %= 360
let Lquadrant = (floor(L/90))*90
let RAquadrant = (floor(RA/90))*90
RA += Lquadrant - RAquadrant
RA /= 15
// Calculate the Sun's declination
let sinDec = 0.39782*sind(L)
let cosDec = cosd(asind(sinDec))
// Calculate the Sun's local hour angle
let cosH = (cosd(zenith.rawValue) - sinDec*sind(latitude))/(cosDec*cosd(latitude))
if cosH > 1 { // The sun never rises on this location (on the specified date)
sunriseTime = Double.infinity
sunsetTime = -Double.infinity
} else if cosH < -1 { // The sun never sets on this location (on the specified date)
sunriseTime = -Double.infinity
sunsetTime = Double.infinity
} else {
// Finish calculating H and convert into hours
var H = (i == 0 ? 360.0 : 0.0) + (i == 0 ? -1.0 : 1.0)*acosd(cosH)
H /= 15
// Calculate local mean time of rising/setting
let T = H + RA - 0.06571*t - 6.622
// Adjust back to UTC
let UT = T - longitudeHour
// Convert UT value to local time zone of latitude/longitude
let localT = UT + localOffset
if i == 0 { // Add 24 and modulo 24 to be sure that the results is between 0..<24
sunriseTime = (localT + 24)%24
} else {
sunsetTime = (localT + 24)%24
}
}
}
return (sunriseTime, sunsetTime)
}
func sind(valueInDegrees: Double) -> Double {
return sin(valueInDegrees*M_PI/180)
}
func cosd(valueInDegrees: Double) -> Double {
return cos(valueInDegrees*M_PI/180)
}
func tand(valueInDegrees: Double) -> Double {
return tan(valueInDegrees*M_PI/180)
}
func asind(valueInRadians: Double) -> Double {
return asin(valueInRadians)*180/M_PI
}
func acosd(valueInRadians: Double) -> Double {
return acos(valueInRadians)*180/M_PI
}
func atand(valueInRadians: Double) -> Double {
return atan(valueInRadians)*180/M_PI
}
Ans voici comment utiliser la fonction pour déterminer si elle est ou non la nuit:
let latitude = ...
let longitude = ...
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let (sunriseHour, sunsetHour) = sunriseSunsetHoursForLocation(coordinate)
let componetns = NSCalendar.currentCalendar().components([.Hour, .Minute], fromDate: NSDate())
let currentHour = Double(componetns.hour) + Double(componetns.minute)/60
let isNight = currentHour < sunriseHour || currentHour > sunsetHour