diff --git a/Project.toml b/Project.toml index 92a78b58..6ee98071 100644 --- a/Project.toml +++ b/Project.toml @@ -12,9 +12,9 @@ DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [weakdeps] +TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" @@ -26,6 +26,7 @@ SolarPositionInterpolationsExt = "Interpolations" SolarPositionMakieExt = "Makie" SolarPositionModelingToolkitExt = ["ModelingToolkit", "Symbolics"] SolarPositionOhMyThreadsExt = "OhMyThreads" +SolarPositionTimeZonesExt = "TimeZones" [compat] Aqua = "0.8" diff --git a/README.md b/README.md index 639d3d2f..53a79340 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ accuracy and implementation status. | USNO | [U.S. Naval Observatory](https://aa.usno.navy.mil/faq/sun_approx) | ±0.0500° | None | ✅ | | SPA | [Reda & Andreas, 2004](https://doi.org/10.1016/j.solener.2003.12.003) | ±0.0003° | Built-in | ✅ | | Iqbal | [Iqbal, 1983](https://doi.org/10.1016/B978-0-12-373750-2.X5001-0) | ±0.0100° | None | ✅ | +| Michalsky | [Michalsky, 1988]() | ±0.0100° | MICHALSKY | ✅ | ## Fast repeated evaluation @@ -141,14 +142,14 @@ tracker example. Atmospheric refraction correction algorithms available in SolarPosition.jl. -| Algorithm | Reference | Atmospheric Parameters | Status | -| --------- | ------------------------------------------------------------------------------------------------ | ---------------------- | ------ | -| HUGHES | [Hughes, 1985](https://pvpmc.sandia.gov/app/uploads/sites/243/2022/10/Engineering-Astronomy.pdf) | Pressure, Temperature | ✅ | -| ARCHER | Archer et al., 1980 | None | ✅ | -| BENNETT | [Bennett, 1982](https://doi.org/10.1017/S0373463300022037) | Pressure, Temperature | ✅ | -| MICHALSKY | [Michalsky, 1988]() | None | ✅ | -| SG2 | [Blanc & Wald, 2012](https://doi.org/10.1016/j.solener.2012.07.018) | Pressure, Temperature | ✅ | -| SPA | [Reda & Andreas, 2004](https://doi.org/10.1016/j.solener.2003.12.003) | Pressure, Temperature | ✅ | +| Algorithm | Reference | Atmospheric Parameters | Status | +| ------------- | ------------------------------------------------------------------------------------------------ | ---------------------- | ------ | +| HUGHES | [Hughes, 1985](https://pvpmc.sandia.gov/app/uploads/sites/243/2022/10/Engineering-Astronomy.pdf) | Pressure, Temperature | ✅ | +| ARCHER | Archer et al., 1980 | None | ✅ | +| BENNETT | [Bennett, 1982](https://doi.org/10.1017/S0373463300022037) | Pressure, Temperature | ✅ | +| MICHALSKY | [Michalsky, 1988]() | None | ✅ | +| SG2 | [Blanc & Wald, 2012](https://doi.org/10.1016/j.solener.2012.07.018) | Pressure, Temperature | ✅ | +| SPARefraction | [Reda & Andreas, 2004](https://doi.org/10.1016/j.solener.2003.12.003) | Pressure, Temperature | ✅ | ## Extensions @@ -161,6 +162,11 @@ import the corresponding packages: | OhMyThreads | [`OhMyThreads.jl`](https://github.com/JuliaFolds2/OhMyThreads.jl) | Parallel computation of solar positions | | ModelingToolkit | [`ModelingToolkit.jl`](https://github.com/SciML/ModelingToolkit.jl) | Symbolic solar position models for simulations | | Interpolations | [`Interpolations.jl`](https://github.com/JuliaMath/Interpolations.jl) | Fast `Interpolated` algorithm construction | +| TimeZones | [`TimeZones.jl`](https://github.com/JuliaTime/TimeZones.jl) | `ZonedDateTime` input and zoned sunrise/sunset | + +Loading `TimeZones.jl` is what enables `ZonedDateTime` arguments. In practice this needs +no thought, since a `ZonedDateTime` cannot be constructed without it, and it means users +who only ever pass a `DateTime` do not pay for TZJData and its download stack. ## Numeric precision diff --git a/docs/src/index.md b/docs/src/index.md index b1dbadd7..55176909 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -88,6 +88,50 @@ accuracy and implementation status. | [`Iqbal`](@ref SolarPosition.Positioning.Iqbal) | [Iqbal, 1983](https://doi.org/10.1016/B978-0-12-373750-2.X5001-0) | ±0.0100° | None | ✅ | | [`Michalsky`](@ref SolarPosition.Positioning.Michalsky) | [Michalsky, 1988](https://doi.org/10.1016/0038-092X(88)90045-X) | ±0.0100° | [`MICHALSKY`](@ref SolarPosition.Refraction.MICHALSKY) | ✅ | +Pass an algorithm as the third argument to pick one; the default is +[`PSA`](@ref SolarPosition.Positioning.PSA). + +```@example srt +solar_position(obs, DateTime(2023, 6, 21, 12), Michalsky()) +``` + +## Fast repeated evaluation + +For dense time series, the [`Interpolated`](@ref SolarPosition.Positioning.Interpolated) +wrapper precomputes cubic B-splines of SPA's geocentric solar coordinates and reconstructs +positions analytically, roughly 10× faster per query at matching accuracy. One interpolant +serves every observer. It activates as a package extension when +[Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl) is loaded: + +```@example srt +using Interpolations + +alg = Interpolated(SPA(); tspan = (DateTime(2023, 1, 1), DateTime(2024, 1, 1))) +solar_position(obs, times, alg) +``` + +See the [Interpolated Solar Position](@ref interpolated-position) guide for accuracy +figures and when the construction cost pays off. + +## Automatic differentiation + +All algorithms are generic over the number type, so solar positions are differentiable +with [ForwardDiff.jl](https://github.com/JuliaDiff/ForwardDiff.jl) out of the box, with no +extension package needed: + +```@example srt +using ForwardDiff + +ForwardDiff.gradient( + x -> solar_position(Observer(x[1], x[2]), DateTime(2023, 6, 21, 12)).elevation, + [52.35888, 4.88185], +) +``` + +The [Automatic Differentiation](@ref automatic-differentiation) guide shows gradients +through refraction models, panel orientation optimization, and a single axis tracker +example. + ## Numeric precision The computation runs at the precision of the @@ -121,12 +165,19 @@ import the corresponding packages: | Makie | [`Makie.jl`](https://github.com/MakieOrg/Makie.jl) | Plotting recipes for solar position visualization | | OhMyThreads | [`OhMyThreads.jl`](https://github.com/JuliaFolds2/OhMyThreads.jl) | Parallel computation of solar positions | | ModelingToolkit | [`ModelingToolkit.jl`](https://github.com/SciML/ModelingToolkit.jl) | Symbolic solar position models for simulations | +| Interpolations | [`Interpolations.jl`](https://github.com/JuliaMath/Interpolations.jl) | Fast `Interpolated` algorithm construction | +| TimeZones | [`TimeZones.jl`](https://github.com/JuliaTime/TimeZones.jl) | `ZonedDateTime` input and zoned sunrise/sunset | + +Loading `TimeZones.jl` is what enables `ZonedDateTime` arguments. In practice this needs +no thought, since a `ZonedDateTime` cannot be constructed without it, and it means users +who only ever pass a `DateTime` do not pay for TZJData and its download stack. !!! note For more details on the extensions, see: - [ModelingToolkit Extension](guides/modelingtoolkit.md) - [Makie Extension](guides/plotting.md) - [OhMyThreads Extension](guides/parallel.md) + - [Interpolated Solar Position](@ref interpolated-position) ## How to Cite diff --git a/ext/SolarPositionOhMyThreadsExt.jl b/ext/SolarPositionOhMyThreadsExt.jl index 98a83525..bc84d164 100644 --- a/ext/SolarPositionOhMyThreadsExt.jl +++ b/ext/SolarPositionOhMyThreadsExt.jl @@ -7,7 +7,6 @@ using OhMyThreads using OhMyThreads: tmap, tmap! using StructArrays: StructArrays using Dates: DateTime -using TimeZones: ZonedDateTime using DocStringExtensions: TYPEDSIGNATURES """ @@ -57,7 +56,7 @@ function SolarPosition.solar_position!( alg::SolarAlgorithm, refraction::RefractionAlgorithm, executor::OhMyThreads.Scheduler, - ) where {T <: AbstractSolPos, Z <: Union{DateTime, ZonedDateTime}} + ) where {T <: AbstractSolPos, Z} f = dt -> SolarPosition.solar_position(obs, dt, alg, refraction) tmap!(f, pos, dts; scheduler = executor) return pos @@ -108,7 +107,7 @@ function SolarPosition.solar_position( alg::SolarAlgorithm, refraction::RefractionAlgorithm, executor::OhMyThreads.Scheduler, - ) where {T <: AbstractFloat, Z <: Union{DateTime, ZonedDateTime}} + ) where {T <: AbstractFloat, Z} f = dt -> SolarPosition.solar_position(obs, dt, alg, refraction) results = tmap(f, dts; scheduler = executor) return StructArrays.StructVector(results) @@ -118,7 +117,7 @@ function SolarPosition.solar_position( obs::Observer{T}, dts::AbstractVector{Z}, executor::OhMyThreads.Scheduler, - ) where {T <: AbstractFloat, Z <: Union{DateTime, ZonedDateTime}} + ) where {T <: AbstractFloat, Z} return SolarPosition.solar_position( obs, dts, diff --git a/ext/SolarPositionTimeZonesExt.jl b/ext/SolarPositionTimeZonesExt.jl new file mode 100644 index 00000000..76dfbbc1 --- /dev/null +++ b/ext/SolarPositionTimeZonesExt.jl @@ -0,0 +1,193 @@ +""" +TimeZones support for SolarPosition.jl. + +`ZonedDateTime` inputs and timezone aware sunrise/sunset results live here rather than in +the package proper, so that loading SolarPosition does not pull in TimeZones and, through +it, TZJData, Downloads and LibCURL. This is invisible in practice: a `ZonedDateTime` or a +`TimeZone` cannot be constructed without TimeZones being loaded, which is exactly what +triggers this extension. + +Every method here converts to UTC and delegates to the `DateTime` method in the package, +converting back into the given zone where the result is itself a time. +""" +module SolarPositionTimeZonesExt + +using Dates: DateTime +using SolarPosition: Positioning, Utilities +using StructArrays: StructArrays +using TimeZones: TimeZone, ZonedDateTime, UTC, timezone + +using SolarPosition.Positioning: AbstractObserver, AbstractSolPos, Interpolated, Observer, + PSA, SolarAlgorithm, _result_eltype, calculate_deltat, result_type, solar_position, + solar_position!, solar_rate +using SolarPosition.Refraction: DefaultRefraction, RefractionAlgorithm +using SolarPosition.Utilities: SPA, TransitSunriseSunset, _transit_sunrise_sunset, + _transit_sunrise_sunset_impl, next_solar_noon, next_sunrise, next_sunset, + previous_solar_noon, previous_sunrise, previous_sunset, transit_sunrise_sunset + +# ------------------------------------------------------------------ solar position + +function Positioning.solar_position( + obs::AbstractObserver{T}, + dt::ZonedDateTime, + alg::SolarAlgorithm = PSA(), + refraction::RefractionAlgorithm = DefaultRefraction(), + ) where {T <: Real} + return solar_position(obs, DateTime(dt, UTC), alg, refraction) +end + +function Positioning.solar_position!( + pos::StructArrays.StructVector{T}, + obs::AbstractObserver, + dts::AbstractVector{ZonedDateTime}, + alg::SolarAlgorithm = PSA(), + refraction::RefractionAlgorithm = DefaultRefraction(), + ) where {T <: AbstractSolPos} + @inbounds for i in eachindex(dts, pos) + pos[i] = solar_position(obs, dts[i], alg, refraction) + end + return pos +end + +function Positioning.solar_position!( + pos::StructArrays.StructVector{T}, + obs::AbstractObserver, + dts::AbstractVector{Union{DateTime, ZonedDateTime}}, + alg::SolarAlgorithm = PSA(), + refraction::RefractionAlgorithm = DefaultRefraction(), + ) where {T <: AbstractSolPos} + @inbounds for i in eachindex(dts, pos) + pos[i] = solar_position(obs, dts[i], alg, refraction) + end + return pos +end + +function Positioning.solar_position( + obs::AbstractObserver{T}, + dts::AbstractVector{ZonedDateTime}, + alg::SolarAlgorithm = PSA(), + refraction::RefractionAlgorithm = DefaultRefraction(), + ) where {T <: Real} + RetType = result_type( + typeof(alg), typeof(refraction), _result_eltype(T, refraction), + ) + pos = StructArrays.StructVector{RetType}(undef, length(dts)) + solar_position!(pos, obs, dts, alg, refraction) + return pos +end + +function Positioning.solar_position( + obs::AbstractObserver{T}, + dts::AbstractVector{Union{DateTime, ZonedDateTime}}, + alg::SolarAlgorithm = PSA(), + refraction::RefractionAlgorithm = DefaultRefraction(), + ) where {T <: Real} + RetType = result_type( + typeof(alg), typeof(refraction), _result_eltype(T, refraction), + ) + pos = StructArrays.StructVector{RetType}(undef, length(dts)) + solar_position!(pos, obs, dts, alg, refraction) + return pos +end + +Positioning.solar_rate(obs::AbstractObserver, dt::ZonedDateTime, alg::Interpolated) = + solar_rate(obs, DateTime(dt, UTC), alg) + +# --------------------------------------------------------------------------- delta T + +Positioning.calculate_deltat(datetime::ZonedDateTime) = + calculate_deltat(DateTime(datetime, UTC)) + +Positioning.calculate_deltat(::Type{T}, datetime::ZonedDateTime) where {T <: Real} = + T(calculate_deltat(datetime)) + +# the interpolant span accepts zoned endpoints +Positioning._as_utc(zdt::ZonedDateTime) = DateTime(zdt, UTC) + +# --------------------------------------------------------- sunrise, sunset, transit + +function Utilities.TransitSunriseSunset{ZonedDateTime}( + transit::DateTime, + sunrise::DateTime, + sunset::DateTime, + tz::TimeZone, + ) + return TransitSunriseSunset{ZonedDateTime}( + ZonedDateTime(transit, tz; from_utc = true), + ZonedDateTime(sunrise, tz; from_utc = true), + ZonedDateTime(sunset, tz; from_utc = true), + ) +end + +function Utilities._transit_sunrise_sunset( + tz::TimeZone, + obs::Observer{T}, + dt::DateTime, + alg::SPA, + ) where {T <: Real} + return _transit_sunrise_sunset_impl(ZonedDateTime, obs, dt, alg, tz) +end + +function Utilities.transit_sunrise_sunset( + obs::Observer{T}, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + )::TransitSunriseSunset{ZonedDateTime} where {T <: Real} + return _transit_sunrise_sunset(timezone(zdt), obs, DateTime(zdt, UTC), alg) +end + +function Utilities.next_sunrise( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = next_sunrise(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +function Utilities.next_sunset( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = next_sunset(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +function Utilities.next_solar_noon( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = next_solar_noon(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +function Utilities.previous_sunrise( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = previous_sunrise(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +function Utilities.previous_sunset( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = previous_sunset(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +function Utilities.previous_solar_noon( + obs::Observer, + zdt::ZonedDateTime, + alg::SolarAlgorithm = SPA(), + ) + result_utc = previous_solar_noon(obs, DateTime(zdt, UTC), alg) + return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) +end + +end # module SolarPositionTimeZonesExt diff --git a/src/Positioning/Positioning.jl b/src/Positioning/Positioning.jl index 21f12fa4..98b52da3 100644 --- a/src/Positioning/Positioning.jl +++ b/src/Positioning/Positioning.jl @@ -10,7 +10,6 @@ module Positioning using Dates: Dates, DateTime, Date, daysinmonth, dayofyear using Dates: year, month, day -using TimeZones: ZonedDateTime, UTC using StructArrays: StructArrays using Tables: Tables using DocStringExtensions: TYPEDFIELDS, TYPEDEF, TYPEDSIGNATURES @@ -358,28 +357,6 @@ function solar_position( return _solar_position(obs, dt, alg, refraction) end -function solar_position( - obs::AbstractObserver{T}, - dt::ZonedDateTime, - alg::SolarAlgorithm = PSA(), - refraction::RefractionAlgorithm = DefaultRefraction(), - ) where {T <: Real} - return solar_position(obs, DateTime(dt, UTC), alg, refraction) -end - -function solar_position!( - pos::StructArrays.StructVector{T}, - obs::AbstractObserver, - dts::AbstractVector{Union{DateTime, ZonedDateTime}}, - alg::SolarAlgorithm = PSA(), - refraction::RefractionAlgorithm = DefaultRefraction(), - ) where {T <: AbstractSolPos} - @inbounds for i in eachindex(dts, pos) - pos[i] = solar_position(obs, dts[i], alg, refraction) - end - return pos -end - function solar_position!( pos::StructArrays.StructVector{T}, obs::AbstractObserver, @@ -393,33 +370,6 @@ function solar_position!( return pos end -function solar_position!( - pos::StructArrays.StructVector{T}, - obs::AbstractObserver, - dts::AbstractVector{ZonedDateTime}, - alg::SolarAlgorithm = PSA(), - refraction::RefractionAlgorithm = DefaultRefraction(), - ) where {T <: AbstractSolPos} - @inbounds for i in eachindex(dts, pos) - pos[i] = solar_position(obs, dts[i], alg, refraction) - end - return pos -end - -function solar_position( - obs::AbstractObserver{T}, - dts::AbstractVector{Union{DateTime, ZonedDateTime}}, - alg::SolarAlgorithm = PSA(), - refraction::RefractionAlgorithm = DefaultRefraction(), - ) where {T <: Real} - RetType = result_type( - typeof(alg), typeof(refraction), _result_eltype(T, refraction), - ) - pos = StructArrays.StructVector{RetType}(undef, length(dts)) - solar_position!(pos, obs, dts, alg, refraction) - return pos -end - function solar_position( obs::AbstractObserver{T}, dts::AbstractVector{DateTime}, @@ -434,21 +384,6 @@ function solar_position( return pos end -function solar_position( - obs::AbstractObserver{T}, - dts::AbstractVector{ZonedDateTime}, - alg::SolarAlgorithm = PSA(), - refraction::RefractionAlgorithm = DefaultRefraction(), - ) where {T <: Real} - RetType = result_type( - typeof(alg), typeof(refraction), _result_eltype(T, refraction), - ) - pos = StructArrays.StructVector{RetType}(undef, length(dts)) - solar_position!(pos, obs, dts, alg, refraction) - return pos -end - - """ $(TYPEDSIGNATURES) diff --git a/src/Positioning/deltat.jl b/src/Positioning/deltat.jl index 53f7bf53..4416248e 100644 --- a/src/Positioning/deltat.jl +++ b/src/Positioning/deltat.jl @@ -197,10 +197,6 @@ function calculate_deltat(date::Union{DateTime, Date}) return calculate_deltat(y, frac_month) end -function calculate_deltat(datetime::ZonedDateTime) - return calculate_deltat(DateTime(datetime, UTC)) -end - # Type-generic entry points: keep the Float64 polynomial value (ΔT is uncertain to ~seconds), # convert to T so the result stays type-stable through the pipeline. calculate_deltat(::Type{T}, year::Real, month::Real) where {T <: Real} = @@ -208,6 +204,3 @@ calculate_deltat(::Type{T}, year::Real, month::Real) where {T <: Real} = calculate_deltat(::Type{T}, date::Union{DateTime, Date}) where {T <: Real} = T(calculate_deltat(date)) - -calculate_deltat(::Type{T}, datetime::ZonedDateTime) where {T <: Real} = - T(calculate_deltat(datetime)) diff --git a/src/Positioning/interpolated.jl b/src/Positioning/interpolated.jl index 93d4ce1c..666c29d2 100644 --- a/src/Positioning/interpolated.jl +++ b/src/Positioning/interpolated.jl @@ -66,7 +66,7 @@ end function Interpolated( algorithm::SPA = SPA(); - tspan::Tuple{<:Union{DateTime, ZonedDateTime}, <:Union{DateTime, ZonedDateTime}}, + tspan::Tuple{Any, Any}, step::Dates.Period = Dates.Hour(1), out_of_range::Symbol = :error, ) @@ -95,7 +95,6 @@ function Interpolated( end _as_utc(dt::DateTime) = dt -_as_utc(zdt::ZonedDateTime) = DateTime(zdt, UTC) # Extension hook. SolarPositionInterpolationsExt defines the working method for SPA; # this fallback exists so construction without the extension fails with a clear message. @@ -216,7 +215,3 @@ function solar_rate( daz = mod(p2.azimuth - p1.azimuth + 180, 360) - 180 return (dazimuth_dt = daz * 1800, delevation_dt = (p2.elevation - p1.elevation) * 1800) end - -function solar_rate(obs::AbstractObserver, dt::ZonedDateTime, alg::Interpolated) - return solar_rate(obs, DateTime(dt, UTC), alg) -end diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index 0eff072c..cd5dca1a 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -3,7 +3,6 @@ module Utilities using DocStringExtensions: TYPEDEF, TYPEDFIELDS, TYPEDSIGNATURES using ..Positioning: Observer, SPA, SolarAlgorithm, calculate_deltat import Dates: DateTime, Date, Day -import TimeZones: ZonedDateTime, timezone, UTC include("spa.jl") include("srt.jl") diff --git a/src/Utilities/spa.jl b/src/Utilities/spa.jl index 8d659125..3f2821b3 100644 --- a/src/Utilities/spa.jl +++ b/src/Utilities/spa.jl @@ -1,7 +1,6 @@ """SPA sunrise/sunset calculations.""" using Dates: Dates -using TimeZones: TimeZone using ..Positioning: _compute_spa_srt_parameters @@ -23,22 +22,13 @@ function _transit_sunrise_sunset( return _transit_sunrise_sunset_impl(R, obs, dt, alg, nothing) end -function _transit_sunrise_sunset( - tz::TimeZone, - obs::Observer{T}, - dt::DateTime, - alg::SPA, - ) where {T <: Real} - return _transit_sunrise_sunset_impl(ZonedDateTime, obs, dt, alg, tz) -end - function _transit_sunrise_sunset_impl( ::Type{R}, obs::Observer{T}, dt::DateTime, alg::SPA, - tz::Union{Nothing, TimeZone}, - ) where {T <: Real, R <: Union{DateTime, ZonedDateTime}} + tz, + ) where {T <: Real, R} """Calculate the sun transit, sunrise, and sunset for a given date at an Observer location using the SPA algorithm. diff --git a/src/Utilities/srt.jl b/src/Utilities/srt.jl index 6328a1a9..66464e35 100644 --- a/src/Utilities/srt.jl +++ b/src/Utilities/srt.jl @@ -27,7 +27,7 @@ TransitSunriseSunset{ZonedDateTime}( # Fields $(TYPEDFIELDS) """ -struct TransitSunriseSunset{T <: Union{DateTime, ZonedDateTime}} +struct TransitSunriseSunset{T} transit::T sunrise::T sunset::T @@ -43,20 +43,6 @@ function TransitSunriseSunset{DateTime}( return TransitSunriseSunset{DateTime}(transit, sunrise, sunset) end -# Constructor for ZonedDateTime (with timezone conversion) -function TransitSunriseSunset{ZonedDateTime}( - transit::DateTime, - sunrise::DateTime, - sunset::DateTime, - tz::TimeZone, - ) - return TransitSunriseSunset{ZonedDateTime}( - ZonedDateTime(transit, tz; from_utc = true), - ZonedDateTime(sunrise, tz; from_utc = true), - ZonedDateTime(sunset, tz; from_utc = true), - ) -end - """Calculate the sun transit, sunrise, and sunset for a given date at an Observer location. """ @@ -76,14 +62,6 @@ function transit_sunrise_sunset( return transit_sunrise_sunset(obs, DateTime(dt), alg) end -function transit_sunrise_sunset( - obs::Observer{T}, - zdt::ZonedDateTime, - alg::SolarAlgorithm = SPA(), - )::TransitSunriseSunset{ZonedDateTime} where {T <: Real} - return _transit_sunrise_sunset(timezone(zdt), obs, DateTime(zdt, UTC), alg) -end - # Helper function for next_* functions function _next_event(obs::Observer, dt::DateTime, alg::SolarAlgorithm, event_field::Symbol) date_only = Date(dt) @@ -153,12 +131,6 @@ function next_sunrise(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return next_sunrise(obs, DateTime(dt), alg) end -function next_sunrise(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = next_sunrise(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end - """ $(TYPEDSIGNATURES) @@ -180,12 +152,6 @@ function next_sunset(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return next_sunset(obs, DateTime(dt), alg) end -function next_sunset(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = next_sunset(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end - """ $(TYPEDSIGNATURES) @@ -208,12 +174,6 @@ function next_solar_noon(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return next_solar_noon(obs, DateTime(dt), alg) end -function next_solar_noon(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = next_solar_noon(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end - """ $(TYPEDSIGNATURES) @@ -235,12 +195,6 @@ function previous_sunrise(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return previous_sunrise(obs, DateTime(dt), alg) end -function previous_sunrise(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = previous_sunrise(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end - """ $(TYPEDSIGNATURES) @@ -262,12 +216,6 @@ function previous_sunset(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return previous_sunset(obs, DateTime(dt), alg) end -function previous_sunset(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = previous_sunset(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end - """ $(TYPEDSIGNATURES) @@ -288,9 +236,3 @@ end function previous_solar_noon(obs::Observer, dt::Date, alg::SolarAlgorithm = SPA()) return previous_solar_noon(obs, DateTime(dt), alg) end - -function previous_solar_noon(obs::Observer, zdt::ZonedDateTime, alg::SolarAlgorithm = SPA()) - dt_utc = DateTime(zdt, UTC) - result_utc = previous_solar_noon(obs, dt_utc, alg) - return ZonedDateTime(result_utc, timezone(zdt); from_utc = true) -end