2015-12-10 1 views
0

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 

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 

Répondre

1

Je ne sais pas pourquoi votre code pour obtenir le décalage ne fonctionne pas (je suis la même résultat). Mais il existe une solution plus simple qui fonctionne. Il suffit de demander le fuseau horaire local, en utilisant secondsFromGMTForDate. Avec des dates à six mois d'intervalle, j'obtiens des résultats différents:

let now = NSDate() 
let future = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Month, value: 6, toDate: now, options: NSCalendarOptions(rawValue: 0))! 

let nowOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(now)/3600 
let futureOffset = NSTimeZone.localTimeZone().secondsFromGMTForDate(future)/3600