ceek / solar Goto Github PK
View Code? Open in Web Editor NEWA Swift micro library for generating Sunrise and Sunset times.
License: MIT License
A Swift micro library for generating Sunrise and Sunset times.
License: MIT License
I'm in mexico city and noticed that the isDayTime returns false at exactly 18:00 local time (12:00 UTC) while current sunset time is around 20:15. After some analyses I found out that, at exactly that 12:00 UTC, the sunrise and sunset days are shifted one day forward. This results in both sunrise and sunset being later than the current time, which results in false for isDayTime.
I'm assuming this 1 day shift should only happen after sunset, right?
I really appreciate what you've done, and, honestly it goes a bit over my head. I understand that eventually the result of this code is probably wrong:
let shouldBeYesterday = lngHour > 0 && UT > 12 && sunriseSunset == .sunrise
let shouldBeTomorrow = lngHour < 0 && UT < 12 && sunriseSunset == .sunset
but I guess it's also very well possible, that is somewhere in the code above these lines.
These are the values for lngHour & UT (after normalisation) for sunrise and sunset respectively, in my location, mexico city:
lat: 19.39258888
lon: -99.28117439
lngHour: -6.61874495933333
UT sunrise: 11.9693350962159
UT sunset: 1.24306218714151
I could work around this by adding a day to the currentTime calculation if date > 12:00 UTC, but I'm not sure is a good idea. From what I've seen here it works for most people, so might be something with my location specifically, while the App I'm using this in is used all over the world.
Any thoughts to a fix or workaround?
WeatherKit has a CurrentWeather.isDaylight
property and SunEvents
struct that essentially replicate the functionality of this library. However, WeatherKit is only available for this year’s operating system releases (iOS 16, iPadOS 16, macOS 13, tvOS 16, watchOS 9). Should this library somehow integrate with WeatherKit on supported platforms, perhaps becoming a compatibility shim for it on unsupported Swift platforms like Linux?
Now, for calculate it is used current date/time Date().timeIntervalSince1970
(line 57).
You should use self.date.timeIntervalSince1970
.
Is it possible to return only the hours and minutes of the sunset or sunrise?
According to the CocoaPods podspec, this project supports macOS 10.9+ and tvOS 9.0+ alongside iOS. However, the Xcode project only has a scheme for iOS, which means this library can’t be installed for a macOS or tvOS project using Carthage. The fix would be to add two additional framework targets, two additional schemes, and the required entries in the Travis configuration file.
As Swift 3 has now entered beta, the project will need to be updated.
A develop branch will need to be made to house these changes before Swift 3 goes gold.
There is error when I adding Solar to my project with carthage and hope you can check it out :)
log:
*** Fetching Solar
*** Checking out Solar at "2.0.0"
*** xcodebuild output can be found in /var/folders/0m/bv501p9j043_w386gf5g7dlh0000gn/T/carthage-xcodebuild.vDSKOj.log
*** Building scheme "Solar" in Solar.xcodeproj
Build Failed
Task failed with exit code 65:
/usr/bin/xcrun xcodebuild -project
.../Carthage/Checkouts/Solar/Solar.xcodeproj -scheme Solar -configuration Release -derivedDataPath /Users/brucewzp/Library/Caches/org.carthage.CarthageKit/DerivedData/Solar/2.0.0 -sdk iphoneos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES clean build
This usually indicates that project itself failed to compile. Please check the xcodebuild log for more details: /var/folders/0m/bv501p9j043_w386gf5g7dlh0000gn/T/carthage-xcodebuild.vDSKOj.log
I am getting some incorrect results on my iOS simulator with the following conditions:
Returned results:
solarEvent: Optional(Solar.Solar(coordinate: __C.CLLocationCoordinate2D(latitude: 32.845753, longitude: -96.852206), date: 2023-03-27 04:46:28 +0000, sunrise: Optional(2023-03-27 12:22:16 +0000), sunset: Optional(2023-03-28 00:43:56 +0000), civilSunrise: Optional(2023-03-27 11:57:33 +0000), civilSunset: Optional(2023-03-28 01:08:40 +0000), nauticalSunrise: Optional(2023-03-27 11:28:37 +0000), nauticalSunset: Optional(2023-03-28 01:37:37 +0000), astronomicalSunrise: Optional(2023-03-27 10:59:16 +0000), astronomicalSunset: Optional(2023-03-28 02:07:00 +0000)))
for lat, lon: 32.845753 -96.852206
These results in GMT are all exactly two hours earlier than would be the correct times in Dallas. This seems to correlate with the two hour time difference between my simulator (PDT) and the lat/lon given (CDT).
Possible cause:
The Solar struct initializes its property 'self.date' using Date()
, which will take the time from the device. So when the solar events are requested in a timezone different from the device this can lead to a time offset.
I hope this helps!
For places where there's a polar day in the given time/date (see the example), isDaytime
returns false
and isNightTime
returns true
for all times of day (just opposite to reality).
Code:
private func isSunUp(dateTime: Date, coords: LatLon) -> Bool {
let solar = Solar(for: dateTime, coordinate: CLLocationCoordinate2D(latitude: coords.lat, longitude: coords.lon))
return solar!.isDaytime
}
Params:
dateTime: 2022-06-03 06:00:00 +0000
coords: lat: 67.94753132376813, lon: 13.131613209843637
Would you consider adding sun position information to this library, i.e. altitude and azimuth? See the docs at https://github.com/mourner/suncalc for an example. Feel free to close this issue if it's out of scope.
It seems as if the timezone shouldn't strictly be necessary. If we calculate the UTC time of sunrise, we should be able to convert it to the appropriate timezone within the client application.
Right now, removing the timezone results in sunrises happening in the evening of the supplied date. The sunrise obviously can't be 8pm on the 2nd April if the user supplied 2nd April.
A little bit of magic should be needed.
Could it be that Solar doesn't take daylight saving time into account? I live in Mexico City and we're currently on DST. Sunset is around 8:10 PM now, but Solar thinks it's at 7:10 PM
It seems that the dates are 1 day in the future if the local time is the next day UTC.
Not sure if this is the same problem as reported in #24?
Here is a complete test. Note that the reported times are one day in the future from the correct times.
/// Sun and Moon Data for One Day
///
/// U.S. Naval Observatory
/// Astronomical Applications Department
///
/// (Longitude W 94° 0', Latitude N 39° 0')
///
/// Friday, May 3, 2019 Universal Time - 5h
/// Sun
/// Begin civil twilight 05:47
/// Sunrise 06:16
/// Sun transit 13:13
/// Sunset 20:11
/// End civil twilight 20:40
/// Moon
/// Moonrise 05:56
/// Moon transit 12:24
/// Moonset 19:00
///
/// [https://aa.usno.navy.mil/data/docs/RS_OneDay.php](https://aa.usno.navy.mil/data/docs/RS_OneDay.php)
func testPlainLatLon() {
var tdf: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "MM/dd/yyyy HH:mm zzz"
return df
}()
let testCriticalEvening = tdf.date(from: "05/03/2019 19:20 CDT")!
let testLocation: CLLocation = CLLocation(latitude: 39.00, longitude: -94.0)
let expectedSunrise = "05/03/2019 06:16 CDT"
let expectedSunset = "05/03/2019 20:11 CDT"
let solar = Solar(for: testCriticalEvening, coordinate: testLocation.coordinate)
XCTAssert(tdf.string(from: solar!.sunrise!) == expectedSunrise,
"Expected: \(expectedSunrise), Received: \(tdf.string(from: solar!.sunrise!))")
XCTAssert(tdf.string(from: solar!.sunset!) == expectedSunset,
"Expected: \(expectedSunset), Received: \(tdf.string(from: solar!.sunset!))")
}
The results of the test are:
UI Test Activity:
Assertion Failure: Solar_iOSTests.swift:198: XCTAssertTrue failed - Expected: 05/03/2019 06:16 CDT, Received: 05/04/2019 06:14 CDT
UI Test Activity:
Assertion Failure: Solar_iOSTests.swift:200: XCTAssertTrue failed - Expected: 05/03/2019 20:11 CDT, Received: 05/04/2019 20:11 CDT
EDIT: I was mistaken in that I was testing phone locations and forgot to switch back to default thus was getting data for a remote location.
The times given for setting/rising events is consistently off by up to an hour with respect to simply googling or found at www.timeanddate.com
For example on June 17th, 2017 in San Diego, California gives:
Sunrise starts: 3:52:09 AM
Sunrise ends: 5:47:13 AM
Sunset starts: 8:33:50 PM
Sunset ends: 10:28:56 PM
Google gives:
Sunrise: 5:41 AM
Sunset: 7:59 PM
and www.timeanddate.com giving same results as google +/- a minute or so.
Inconsistencies like this are found with every date I have seen
Dear Chris,
Here is an example of what I mean:
Knowing the latitude, longitude of a location (Let's say: 40.730610, -73.935242 for New York) and knowing the sun angle (for example for the sunset the sun angle is defined as making an angle of -0.833 below the horizon) can I get the time of the sunset for a given date (for example: 6:22 AM on the 12th of July) ?
Of course, here I gave an example for the sunset but I want to get times for different sun angles.
So the call to the function would be like:
getTimeForGivenSunAngle(date,lat,long,sunAngle)
that will return something like: "In New York, on the 12th of July the Sun will make an angle of -0.833 degrees at 6:22 AM"
Best regards,
Michael
Hi Chris,
I think I've found an issue to do with time zones. When calling Solar with my lat/long (latitude: -38.2613, longitude: 145.1896), the returned date is always one day ahead of the for-date for all times (sunset, sunrise, all modes). Also, daylight savings is not being processed for my timezone (Melbourne, Australia)
I think the day-ahead problem may stem from line 188;
let localT = UT + (Double(timeZone.secondsFromGMT) / 3600.0)
as this pushes the "hour" variable in the following line;
let hour = floor(localT)
into numbers between 24 and 48 (i.e. the next day)
I tried fixing this up by putting the hour back into 0-24 hour range, but this mucked up the minute and second calculations. So, I had a think about timezones and went back to the underlying returned NSDate being an absolute number of seconds, regardless of timezone and that all your calculations were for UTC time. Given this, I (hack) changed line 188 to do nothing as follows;
let localT = UT
and replaced line 199;
var date = calendar.dateFromComponents(components)
with;
calendar.timeZone = NSTimeZone(abbreviation: "UTC")!
return calendar.dateFromComponents(components)
By changing the the time zone to suit your calculated UTC time, the returned NSDate will be the correct absolute number of seconds and any code using the returned NSDate will have the appropriate timezone set. This means the time zone calculations for the calling apps purposes will all be handled by the Swift libraries. As a side effect of getting the day correct, this also fixed up the issue where daylight savings was not being handled correctly.
NB: the early return I added in line 200 intentionally means lines 202 onwards are not called as they are no longer required.
I tested this fix for both Melbourne, Australia and London, England. It got the correct day, time and daylight savings was processed correctly. What do you think?
Cheers, Graham.
Update the docs to show that the Solar() constructor no longer accepts latitude and longitude as arguments, but requires an instance of CLLocationCoordinate2D instead
After updating Xcode to version 10, it shows me the following error on import Solar
:
Module compiled with Swift 4.1.2 cannot be imported by the Swift 4.2 compiler.
Any ideas how to fix this?
When using Solar.init(for: self.datePicker.dateValue, coordinate: coordinate)
I am having inconsistent results when dls changes.. Just to be sure, does your code calculate
DLS changes or am i doing something wrong.
Just to be more clear here is my result set:
Santa Monica - California
dls for santa monica = true
sunrise of Santa Monica : 06:53:32
sunset of Santa Monica : 18:28:59
Istanbul - Turkey
dls for istanbul = false
sunrise of Istanbul : 07:07:28
sunset of Istanbul : 18:35:24
Santa Monica - California
dls for santa monica = false
sunrise of Santa Monica : 07:19:52 **<- Wrong + 1 Hour**
sunset of Santa Monica : 17:55:15 **<- Wrong + 1 Hour**
Istanbul - Turkey
dls for istanbul = false
sunrise of Istanbul : 07:42:44
sunset of Istanbul : 17:52:28
if you need the project : https://github.com/kbirand/SolarTest
Thanks,
Koray Birand
I'm getting an issue with putting in a date that is after midnight UTC but before midnight local. It returns a solar instance for the following day. I've tried passing in a custom calendar but that doesn't seem to fix the issue. I'll continue working on it but for now here's a test I wrote to recreate this failure case.
func testLateTimeCorrectReturnDate() {
let lateTime = Date(timeIntervalSince1970: 1499130000) // After midnight UTC, before midnight in DC.
let city = cities.first(where: { $0.name == "Washington, D. C." })!
let calendar: Calendar = {
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "EST")!
return calendar
}()
guard let solar = Solar(for: lateTime, latitude: city.latitude, longitude: city.longitude) else {
XCTFail("Cannot get solar")
return
}
let sameDate = calendar.compare(lateTime, to: solar.sunset!, toGranularity: .day)
XCTAssertTrue(sameDate == .orderedSame, "The sunset given \(solar.sunset!) is not same day as given date \(lateTime)")
}
official sunset today (2/12/17) was 5:45 pm in San Francisco. The isNighttime method returned true at 5:31 pm.
I believe this is because it's using Date() without a time zone to calculate when sunset is. However, in Pacific timezone, it is already reporting GMT sunset for 2/14
I get the following build warning when building with Xcode 12.5:
/Users/me/Library/Developer/Xcode/DerivedData/Project/SourcePackages/checkouts/Solar/Package.swift The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.5.99.
This appears to have been fixed for Solar.podspec
by 07e6310
. Solar's Package.swift
was likely overlooked. Please update the Package.swift
deployment target to be consistent with the Solar.podspec
.
This will need fleshing out, but a good test suite would add confidence in using the library. Quick + Nimble preferred.
Any chance of adding Solar Noon option?
Hi Chris,
Thank you loads for the work porting the calculations to Swift, it's been very helpful!
In testing I found the sunset times for offical, civil, etc., were all the same. In Solar.swift on line 162, I noticed the calculate function was not using the passed-in zenith and was instead always using the "Official" zenith;
let cosH = (cos(Zenith.Official.rawValue.degreesToRadians) - (sinDec * sin(latitude.degreesToRadians))) / (cosDec * cos(latitude.degreesToRadians))
This can be corrected with the following;
let cosH = (cos(zenith.rawValue.degreesToRadians) - (sinDec * sin(latitude.degreesToRadians))) / (cosDec * cos(latitude.degreesToRadians))
Cheers, Graham.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.