diff --git a/README.md b/README.md index a02ce00..14a9979 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,11 @@ Solar performs its calculations locally using an algorithm from the [United Stat ## Usage + Solar simply needs a date and a location specified as a latitude and longitude: ```swift -let solar = Solar(for: someDate, latitude: 51.528308, longitude: -0.1340267) +let solar = Solar(for: someDate, CLLocationCoordinate2D(latitude: 51.528308, longitude: -0.1340267)) let sunrise = solar.sunrise let sunset = solar.sunset ``` @@ -24,11 +25,13 @@ let sunset = solar.sunset We can also omit providing a date if we just need the sunrise and sunset for today: ```swift -let solar = Solar(latitude: 51.528308, longitude: -0.1340267) +let solar = Solar(CLLocationCoordinate2D(latitude: 51.528308, longitude: -0.1340267)) let sunrise = solar.sunrise let sunset = solar.sunset ``` +Solar with then cache the calculation and update it as the time moves when you ask. + Note that all dates are UTC. Don't forget to format your date into the appropriate timezone if required. ### Types of sunrise and sunset diff --git a/Solar iOSTests/Solar_iOSTests.swift b/Solar iOSTests/Solar_iOSTests.swift index fca0ef5..ab8fbee 100644 --- a/Solar iOSTests/Solar_iOSTests.swift +++ b/Solar iOSTests/Solar_iOSTests.swift @@ -162,4 +162,11 @@ final class Solar_iOSTests: XCTestCase { let solar2 = Solar(for: testDate, coordinate: invalidCoordinate2) XCTAssertNil(solar2) } + + func testSolar_implicit_creation_for_currentTime() { + let city = cities.first(where: { $0.name == "London" })! + let solar = Solar(coordinate: city.coordinate) + + XCTAssertNotNil(solar, "Solar creation work without time") + } } diff --git a/Solar.podspec b/Solar.podspec index f91c124..8914757 100644 --- a/Solar.podspec +++ b/Solar.podspec @@ -1,16 +1,16 @@ Pod::Spec.new do |s| s.name = "Solar" - s.version = "2.1.0" + s.version = "3.1.0" s.summary = "A Swift library for generating Sunrise and Sunset times." s.description = "A Swift library for generating Sunrise and Sunset times. All calculations take place locally without the need for a network request." s.homepage = "http://github.com/ceek/solar" s.license = { :type => "MIT", :file => "LICENSE" } - s.author = { "Chris Howell" => "chris.kevin.howell@gmail.com" } + s.author = { "Chris Howell" => "chris.kevin.howell@gmail.com" } s.ios.deployment_target = '8.0' s.watchos.deployment_target = '3.0' s.tvos.deployment_target = '9.0' s.osx.deployment_target = '10.9' - s.source = { :git => "https://github.com/ceek/Solar.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/ceek/Solar.git", :tag => "#{s.version}" } s.source_files = "Solar/*.{swift}" s.requires_arc = true end diff --git a/Solar.xcodeproj/project.pbxproj b/Solar.xcodeproj/project.pbxproj index 6e68f73..8182424 100644 --- a/Solar.xcodeproj/project.pbxproj +++ b/Solar.xcodeproj/project.pbxproj @@ -33,6 +33,10 @@ 14456B861EF96ACA00D76A4D /* Solar_iOSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Solar_iOSTests.swift; path = "Solar iOSTests/Solar_iOSTests.swift"; sourceTree = ""; }; 14456B891EF96AD900D76A4D /* CorrectResults.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = CorrectResults.json; path = "Solar iOSTests/CorrectResults.json"; sourceTree = ""; }; 14456B8A1EF96AD900D76A4D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Solar iOSTests/Info.plist"; sourceTree = ""; }; + 55C04150209A25A100A642BC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 55D65083209A3D1800D69A50 /* Solar_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Solar_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 55D65093209A3D5D00D69A50 /* Info-macOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = ""; }; + 55D65098209A3DEF00D69A50 /* Solar.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Solar.podspec; sourceTree = ""; }; 97837EAB1E4BB1DE000FEF64 /* Solar_iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Solar_iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7FC5B5A1D469B2700C4D3EB /* Solar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Solar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F7FC5B651D469B4E00C4D3EB /* Solar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Solar.swift; sourceTree = ""; }; @@ -46,6 +50,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 55D6507F209A3D1800D69A50 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97837EA81E4BB1DE000FEF64 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -97,6 +108,8 @@ F7FC5B5C1D469B2700C4D3EB /* Solar */, 14456B801EF96A8F00D76A4D /* Solar iOSTests */, F7FC5B5B1D469B2700C4D3EB /* Products */, + 55C04150209A25A100A642BC /* README.md */, + 55D65098209A3DEF00D69A50 /* Solar.podspec */, ); sourceTree = ""; }; @@ -106,6 +119,7 @@ F7FC5B5A1D469B2700C4D3EB /* Solar.framework */, 97837EAB1E4BB1DE000FEF64 /* Solar_iOSTests.xctest */, 1410E9C01EF12B5A001829A5 /* Solar.framework */, + 55D65083209A3D1800D69A50 /* Solar_macOS.framework */, ); name = Products; sourceTree = ""; @@ -115,6 +129,7 @@ children = ( F7FC5B651D469B4E00C4D3EB /* Solar.swift */, 1410E9B71EF12AD4001829A5 /* Info-iOS.plist */, + 55D65093209A3D5D00D69A50 /* Info-macOS.plist */, 1410E9B81EF12AD4001829A5 /* Info-watchOS.plist */, ); path = Solar; @@ -130,6 +145,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 55D65080209A3D1800D69A50 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F7FC5B571D469B2700C4D3EB /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -158,6 +180,24 @@ productReference = 1410E9C01EF12B5A001829A5 /* Solar.framework */; productType = "com.apple.product-type.framework"; }; + 55D65082209A3D1800D69A50 /* Solar_macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 55D6508A209A3D1900D69A50 /* Build configuration list for PBXNativeTarget "Solar_macOS" */; + buildPhases = ( + 55D6507E209A3D1800D69A50 /* Sources */, + 55D6507F209A3D1800D69A50 /* Frameworks */, + 55D65080209A3D1800D69A50 /* Headers */, + 55D65081209A3D1800D69A50 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Solar_macOS; + productName = Solar_macOS; + productReference = 55D65083209A3D1800D69A50 /* Solar_macOS.framework */; + productType = "com.apple.product-type.framework"; + }; 97837EAA1E4BB1DE000FEF64 /* Solar_iOSTests */ = { isa = PBXNativeTarget; buildConfigurationList = 97837EB51E4BB1DE000FEF64 /* Build configuration list for PBXNativeTarget "Solar_iOSTests" */; @@ -208,9 +248,14 @@ CreatedOnToolsVersion = 8.3.3; ProvisioningStyle = Manual; }; + 55D65082209A3D1800D69A50 = { + CreatedOnToolsVersion = 9.3; + DevelopmentTeam = V7JGZY6Z4Z; + ProvisioningStyle = Automatic; + }; 97837EAA1E4BB1DE000FEF64 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 34J4G6G665; + DevelopmentTeam = V7JGZY6Z4Z; LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; @@ -234,6 +279,7 @@ targets = ( F7FC5B591D469B2700C4D3EB /* Solar_iOS */, 1410E9BF1EF12B5A001829A5 /* Solar_watchOS */, + 55D65082209A3D1800D69A50 /* Solar_macOS */, 97837EAA1E4BB1DE000FEF64 /* Solar_iOSTests */, ); }; @@ -247,6 +293,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 55D65081209A3D1800D69A50 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97837EA91E4BB1DE000FEF64 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -273,6 +326,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 55D6507E209A3D1800D69A50 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97837EA71E4BB1DE000FEF64 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -354,11 +414,76 @@ }; name = Release; }; + 55D65088209A3D1900D69A50 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = V7JGZY6Z4Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "$(SRCROOT)/Solar/Info-macOS.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_BUNDLE_IDENTIFIER = "me.chrishowell.Solar-macOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + 55D65089209A3D1900D69A50 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = V7JGZY6Z4Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "$(SRCROOT)/Solar/Info-macOS.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_BUNDLE_IDENTIFIER = "me.chrishowell.Solar-macOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; 97837EB31E4BB1DE000FEF64 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = 34J4G6G665; + DEVELOPMENT_TEAM = V7JGZY6Z4Z; INFOPLIST_FILE = "Solar iOSTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = me.chrishowell.SolarTests; @@ -373,7 +498,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - DEVELOPMENT_TEAM = 34J4G6G665; + DEVELOPMENT_TEAM = V7JGZY6Z4Z; INFOPLIST_FILE = "Solar iOSTests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = me.chrishowell.SolarTests; @@ -550,6 +675,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 55D6508A209A3D1900D69A50 /* Build configuration list for PBXNativeTarget "Solar_macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 55D65088209A3D1900D69A50 /* Debug */, + 55D65089209A3D1900D69A50 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97837EB51E4BB1DE000FEF64 /* Build configuration list for PBXNativeTarget "Solar_iOSTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Solar.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Solar.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 211a933..4fe62dd 100644 --- a/Solar.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Solar.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,9 @@ + location = "group:../Test.playground"> + + diff --git a/Solar.xcodeproj/xcshareddata/xcschemes/Solar iOS.xcscheme b/Solar.xcodeproj/xcshareddata/xcschemes/Solar iOS.xcscheme index 8579a27..afa0cd5 100644 --- a/Solar.xcodeproj/xcshareddata/xcschemes/Solar iOS.xcscheme +++ b/Solar.xcodeproj/xcshareddata/xcschemes/Solar iOS.xcscheme @@ -40,9 +40,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -71,7 +70,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Solar/Info-iOS.plist b/Solar/Info-iOS.plist index f69838d..4c70e90 100644 --- a/Solar/Info-iOS.plist +++ b/Solar/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.1.0 + 3.1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Solar/Info-macOS.plist b/Solar/Info-macOS.plist new file mode 100644 index 0000000..4c70e90 --- /dev/null +++ b/Solar/Info-macOS.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Solar/Info-watchOS.plist b/Solar/Info-watchOS.plist index f69838d..4c70e90 100644 --- a/Solar/Info-watchOS.plist +++ b/Solar/Info-watchOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.1.0 + 3.1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Solar/Solar.swift b/Solar/Solar.swift index d2d67e6..5dbf538 100644 --- a/Solar/Solar.swift +++ b/Solar/Solar.swift @@ -3,7 +3,7 @@ // SolarExample // // Created by Chris Howell on 16/01/2016. -// Copyright © 2016 Chris Howell. All rights reserved. +// Changed by Brandon Roehl on 02/05/2018. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the “Software”), to deal @@ -27,27 +27,40 @@ import Foundation import CoreLocation -public struct Solar { +public class Solar { + public static let lengthOfDay: TimeInterval = 86400 /// The coordinate that is used for the calculation - public let coordinate: CLLocationCoordinate2D - - /// The date to generate sunrise / sunset times for - public fileprivate(set) var date: Date - - public fileprivate(set) var sunrise: Date? - public fileprivate(set) var sunset: Date? - public fileprivate(set) var civilSunrise: Date? - public fileprivate(set) var civilSunset: Date? - public fileprivate(set) var nauticalSunrise: Date? - public fileprivate(set) var nauticalSunset: Date? - public fileprivate(set) var astronomicalSunrise: Date? - public fileprivate(set) var astronomicalSunset: Date? + private let coordinate: CLLocationCoordinate2D + private var date: Date? + private var zenith: Zenith + + private var _sunrise: Date? + public var sunrise: Date? { + let date = self.date == nil ? Date() : self.date! + + if self._sunrise == nil || (self.date == nil && self._sunrise! < Date()) { + self._sunrise = calculate(.sunrise, for: date, and: zenith) + } + + return self._sunrise; + } + private var _sunset: Date? + public var sunset: Date? { + let date = self.date == nil ? Date() : self.date! + + if self._sunset == nil || (self.date == nil && self._sunset! < Date()) { + self._sunset = calculate(.sunset, for: date, and: zenith) + } + + return self._sunset; + } // MARK: Init - public init?(for date: Date = Date(), coordinate: CLLocationCoordinate2D) { + public init?(for date: Date? = nil, coordinate: CLLocationCoordinate2D, and zenith: Zenith = .official) { self.date = date + self.zenith = zenith guard CLLocationCoordinate2DIsValid(coordinate) else { return nil @@ -56,33 +69,27 @@ public struct Solar { self.coordinate = coordinate // Fill this Solar object with relevant data - calculate() + self.calculate() } // MARK: - Public functions /// Sets all of the Solar object's sunrise / sunset variables, if possible. /// - Note: Can return `nil` objects if sunrise / sunset does not occur on that day. - public mutating func calculate() { - sunrise = calculate(.sunrise, for: date, and: .official) - sunset = calculate(.sunset, for: date, and: .official) - civilSunrise = calculate(.sunrise, for: date, and: .civil) - civilSunset = calculate(.sunset, for: date, and: .civil) - nauticalSunrise = calculate(.sunrise, for: date, and: .nautical) - nauticalSunset = calculate(.sunset, for: date, and: .nautical) - astronomicalSunrise = calculate(.sunrise, for: date, and: .astronimical) - astronomicalSunset = calculate(.sunset, for: date, and: .astronimical) + public func calculate() { + self._sunrise = nil + self._sunrise = nil } // MARK: - Private functions - fileprivate enum SunriseSunset { + public enum SunriseSunset { case sunrise case sunset } /// Used for generating several of the possible sunrise / sunset times - fileprivate enum Zenith: Double { + public enum Zenith: Double { case official = 90.83 case civil = 96 case nautical = 102 @@ -90,6 +97,10 @@ public struct Solar { } fileprivate func calculate(_ sunriseSunset: SunriseSunset, for date: Date, and zenith: Zenith) -> Date? { + return Solar.calculate(sunriseSunset, at: coordinate, for: date, and: zenith) + } + + public static func calculate(_ sunriseSunset: SunriseSunset, at coordinate: CLLocationCoordinate2D, for date: Date, and zenith: Zenith) -> Date? { guard let utcTimezone = TimeZone(identifier: "UTC") else { return nil } // Get the day of the year @@ -157,7 +168,7 @@ public struct Solar { var UT = T - lngHour // Normalise UT into [0, 24] range - UT = normalise(UT, withMaximum: 24) + UT = Solar.normalise(UT, withMaximum: 24) // Calculate all of the sunrise's / sunset's date components let hour = floor(UT) @@ -186,55 +197,56 @@ public struct Solar { } /// Normalises a value between 0 and `maximum`, by adding or subtracting `maximum` - fileprivate func normalise(_ value: Double, withMaximum maximum: Double) -> Double { + fileprivate static func normalise(_ value: Double, withMaximum maximum: Double) -> Double { var value = value if value < 0 { value += maximum - } - - if value > maximum { + } else if value > maximum { value -= maximum } return value } - -} -extension Solar { - + public enum Cycle { + case day + case night + } + /// Whether the location specified by the `latitude` and `longitude` is in daytime on `date` /// - Complexity: O(1) - public var isDaytime: Bool { - guard - let sunrise = sunrise, - let sunset = sunset - else { - return false + public var currentCycle: Cycle { + + guard let sunrise = sunrise, let sunset = sunset else { + return .day } - - let beginningOfDay = sunrise.timeIntervalSince1970 - let endOfDay = sunset.timeIntervalSince1970 - let currentTime = self.date.timeIntervalSince1970 - - let isSunriseOrLater = currentTime >= beginningOfDay - let isBeforeSunset = currentTime < endOfDay - - return isSunriseOrLater && isBeforeSunset + + // Get the seconds of the begining of the day + let beginningOfDay = sunrise.timeIntervalSince1970.truncatingRemainder(dividingBy: Solar.lengthOfDay) + + // Set the beginging of the day to zero + let endOfDay = sunset.timeIntervalSince1970.advanced(by: -(beginningOfDay)).truncatingRemainder(dividingBy: Solar.lengthOfDay) + let currentTime = (self.date == nil ? Date() : self.date!).timeIntervalSince1970.advanced(by: -(beginningOfDay)).truncatingRemainder(dividingBy: Solar.lengthOfDay) + + return currentTime < endOfDay ? .day : .night + } + + public var isDaytime: Bool { + return self.currentCycle == .day } /// Whether the location specified by the `latitude` and `longitude` is in nighttime on `date` /// - Complexity: O(1) public var isNighttime: Bool { - return !isDaytime + return self.currentCycle == .night } } // MARK: - Helper extensions -private extension Double { +fileprivate extension Double { var degreesToRadians: Double { return Double(self) * (Double.pi / 180.0) }