关于NSTimeZone.seconds的混淆从谷歌

问题描述:

我正在开发一个应用程序,该应用程序具有在夜间自动进入黑暗/夜间模式的功能。该应用程序要求用户的位置,并使用this algorithm确定日出/日落时间(在世界时)。关于NSTimeZone.seconds的混淆从谷歌

唯一不清楚的是从UT转换到本地时间,因为这在算法中没有解释。假设我的日出时间为8.5(早上UT为8:30)。我怎么能将它转换为用户当地的时间来检查它是白天还是夜晚?或者等价地,我如何将用户的本地时间转换为UT以便能够比较它们?

到目前为止,我已尝试使用NSCalendar获取当前日期的NSDateComponentsNSDate())。其中一个组件是NSTimeZone?,我可以从中获得secondsFromGMT。事情是这样的:

let dateComponents = NSCalendar.currentCalendar().components([.TimeZone], fromDate: NSDate()) 
let localOffset = Double(dateComponents.timeZone?.secondsFromGMT ?? 0)/3600 

其中localOffset应的时间差(以小时计)从UT(即格林尼治标准时间,如果我是正确的)本地时间,默认为0,如果dateComponents.timeZone == nil(我不知道在哪些情况下这可能发生)。问题是,我现在比将来的6个月获得相同的localOffset(夏令时与现在西班牙我的位置不同)。这是否意味着我需要使用属性daylightSavingTime和/或daylightSavingTimeOffset以及secondsFromGMTsecondsFromGMT本身是否占这个?

当我从算法中读取结果时,事情变得更加令人困惑。日落时间(当地时间)与Google给定的时间完全一致,但太阳升起时间比Google所说的(我的位置和日期)提前了一个小时。我与你分享了整个Swift算法的实现,希望能帮助别人发现我做错了什么。

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 
} 

答案这是我如何使用函数来确定,如果是晚上或者不:

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 

我不知道为什么你的代码来获取偏移不工作(我得到了相同的结果)。但是有一个更简单的解决方案可以工作。只需询问当地时区,使用secondsFromGMTForDate即可。日期相隔六个月,我得到了不同的结果:

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