# \[ANN\] SolarPosition.jl

**URL:** https://discourse.julialang.org/t/ann-solarposition-jl/134263
**Category:** Package Announcements
**Tags:** announcement, parallel, makie, modelingtoolkit, modelling
**Created:** [December 1, 2025, 5:53pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263 "2025-12-01T17:53:10Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 1, 2025, 5:53pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/1 "2025-12-01T17:53:10Z")

</div>

I am excited to announce the release of my first public Julia package, SolarPosition.jl.

## TLDR

SolarPosition.jl is a common interface to a variety of solar positioning algorithms which have been rewritten from scratch in Julia.

## Installation

SolarPosition.jl is part of the general registry, so you can simply install from the REPL:

```julia
] add SolarPosition

```

## Example usage

```julia
julia> using SolarPosition, Dates, TimeZones, DataFrames

julia> timezone = tz"Asia/Kolkata"
Asia/Kolkata (UTC+05:30)

julia> obs = Observer(28.6, 77.2, 0.0)
Observer(latitude=28.6°, longitude=77.2°, altitude=0.0m)

julia> times = collect(ZonedDateTime(DateTime(2019), timezone):Hour(1):ZonedDateTime(DateTime(2020), timezone))

julia> positions = solar_position(obs, times)

julia> df = DataFrame(positions)
8761×3 DataFrame
  Row │ azimuth elevation zenith  
      │ Float64 Float64 Float64 
──────┼──────────────────────────────
    1 │ 314.042 -82.2189 172.219
    2 │ 57.3871 -80.2452 170.245
    3 │ 81.0308 -67.8108 157.811
    4 │ 90.0973 -54.6909 144.691
    5 │ 96.4595 -41.5545 131.555
    6 │ 102.165 -28.5678 118.568
  ⋮ │ ⋮ ⋮ ⋮
 8757 │ 258.871 -31.0619 121.062
 8758 │ 264.625 -44.0833 134.083
 8759 │ 271.261 -57.2304 147.23
 8760 │ 281.445 -70.3099 160.31
 8761 │ 314.092 -82.2513 172.251
                    8750 rows omitted

```

## Features

- Supports PSA, NOAA, Walraven, USNO and the gold standard SPA algorithm.
- Support for more algorithms can be added as PRs by following the supplied guide.
- All algorithms are implemented in Julia, so no interfaces to C or python are required.
- All algorithms are validated using reference implementations.
- Tables.jl interface so you can easily work with solar positions in dataframes or any other container that supports the tables interface.
- Efficient caching of time-independent quantities through the `Observer` struct.
- Refraction models are abstracted from the base algorithms so you can mix and match refraction and positioning models as you see fit. By default, each algorithm will use the refraction model defined by its accompanying paper reference.

## Extensions

- Full multithreading support for all algorithms via OhMyThreads.jl extension.
- Can be intergrated into symbolic workflows through a ModelingToolkit.jl extension that supplies a `SolarPositionBlock` component.
- Makie.jl extension for making some basic plots.

 ![image](https://global.discourse-cdn.com/julialang/original/3X/3/2/32cdd28010149e4b64ce2707e89084ddfa5b68ea.jpeg)

## Why SolarPosition.jl?

The primary application I had in mind when writing this package is solar photovoltaics engineering, but it can be used in any application where you need to know something about the position of the sun.

A second reason is that almost every programming language has a similar package offering solar positioning algorithms, and Julia didn’t have one yet (that I could find, I did find a few scripts scattered here and there). Python has [solposx](https://solposx.readthedocs.io/en/stable/) and [pvlib](https://pvlib-python.readthedocs.io/en/stable/), R has [solarPos](https://cran.r-project.org/web/packages/solarPos/solarPos.pdf), Rust has [spa-rs](https://github.com/frehberg/spa-rs) and so on. Now Julia has one too!

## Feedback

As this is my first ‘serious’ Julia package, I am looking for feedback. Anything is welcome. You can either put a comment in this thread, or just make a PR or an issue on the GH repository.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [December 1, 2025, 6:08pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/2 "2025-12-01T18:08:55Z")

</div>

> [@langestefan](#):
>
> Julia didn’t have one yet

What do you think of SunCalc.jl?

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 1, 2025, 6:30pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/3 "2025-12-01T18:30:03Z")

</div>

Did not show up in my initial google search. (it looks unmaintained)

---

<div class="post-metadata">

### Author: ![George9000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/george9000/32/23619_2.png) [@George9000](https://discourse.julialang.org/u/George9000)
#### Post date: [December 1, 2025, 9:45pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/4 "2025-12-01T21:45:53Z")

</div>

Appreciate the effort put into the documentation: very thorough and with literature references.

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 1, 2025, 9:50pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/5 "2025-12-01T21:50:49Z")

</div>

Thanks to Documenter.jl for making the process so smooth 🙂

---

<div class="post-metadata">

### Author: ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)
#### Post date: [December 2, 2025, 2:29am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/6 "2025-12-02T02:29:28Z")

</div>

> [@langestefan](#):
>
> A second reason is that almost every programming language has a similar package offering solar positioning algorithms, and Julia didn’t have one yet (that I could find, I did find a few scripts scattered here and there). Python has [solposx](https://solposx.readthedocs.io/en/stable/) and [pvlib](https://pvlib-python.readthedocs.io/en/stable/), R has [solarPos](https://cran.r-project.org/web/packages/solarPos/solarPos.pdf), Rust has [spa-rs](https://github.com/frehberg/spa-rs) and so on. Now Julia has one too!

It has had it for almost ten years now: [Reference · AstroLib](https://juliaastro.org/AstroLib/stable/ref/#AstroLib.sunpos). Example of drawing the analemma with it: [Drawing the analemma with Julia](https://giordano.github.io/blog/2017-11-12-analemma/)

---

<div class="post-metadata">

### Author: ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)
#### Post date: [December 2, 2025, 2:37am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/7 "2025-12-02T02:37:26Z")

</div>

> [@langestefan](#):
>
> I am excited to announce the release of my first public Julia package, SolarPosition.jl.

Also, you may want to provide an actual link to your package when you announce it, especially if it’s not registered so one doesn’t even know where to find it. _ **Edit** _: I presume it’s [GitHub - JuliaAstro/SolarPosition.jl: Solar positioning algorithms for Julia.](https://github.com/JuliaAstro/SolarPosition.jl)

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 2, 2025, 6:09am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/8 "2025-12-02T06:09:17Z")

</div>

Link should work when the registry PR is merged, I thought that would be right after I posted this but seems I timed it slightly wrong. Any hour now…

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 2, 2025, 6:10am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/9 "2025-12-02T06:10:32Z")

</div>

I know about that one. Also: [Canonize all the things · Issue #59 · JuliaAstro/JuliaAstro.github.io · GitHub](https://github.com/JuliaAstro/JuliaAstro.github.io/issues/59#issuecomment-3442154127) (I am not claiming I’m the first in any way 🙂 )

---

<div class="post-metadata">

### Author: ![icweaver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/icweaver/32/45764_2.png) [@icweaver](https://discourse.julialang.org/u/icweaver)
#### Post date: [December 2, 2025, 8:22am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/10 "2025-12-02T08:22:10Z")

</div>

Howdy folks

First off, this is an awesome package, Stefan! I’ll definitely be stealing some of your project management workflows as I learn more, and as we continue to spiff up our JuliaAstro ecosystem.

Thanks for bringing up the analemma thread. There’s actually a [quick comparison plot](https://github.com/JuliaAstro/JuliaAstro.github.io/issues/59#issuecomment-3445231658) between AstroLib.jl (which we are working on transitioning to a [maintenance-only mode](https://github.com/JuliaAstro/AstroLib.jl/issues/105)) and SolarPosition.jl a few messages down. This was just me starting to fiddle around with things, certainly not a comprehensive breakdown.

 ![image](https://global.discourse-cdn.com/julialang/original/3X/b/f/bfae948a551ae30bd0ce774b4018a3719968ce51.png)

In general, there’s been some discussion behind the scenes with Stefan and others about coordinating these astrometry related efforts; I think there is lots to learn from each other. This is actually going to be one of our agenda items during our inaugural JuliaAstro steering council meeting next week.

So, stay tuned y’all! We’ve got some good things cooking =]

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 2, 2025, 7:35pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/11 "2025-12-02T19:35:32Z")

</div>

The registry PR was just merged 🙂

so you can now do:

```julia
] add SolarPosition

```

---

<div class="post-metadata">

### Author: ![tp2750](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tp2750/32/207806_2.png) [@tp2750](https://discourse.julialang.org/u/tp2750)
#### Post date: [December 25, 2025, 11:09am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/12 "2025-12-25T11:09:44Z")

</div>

Thank you for a great package @langestefan .  
In these dark times I wanted to compute the sunrise and sunset, but I could not find a function for it.

I can do something like this:

```julia-auto
using SolarPosition
using Dates
using TimeZones
using Roots

function sunup(date, obs, tz)
    sun_fun(t) = solar_position(obs,ZonedDateTime(unix2datetime(t), tz)).elevation
    t1 = datetime2unix(DateTime(Date(date)))
    t2 = datetime2unix(DateTime(Date(date) + Day(1))) - 12*60*60
    s1 = find_zero(sun_fun, (t1,t2))
    ZonedDateTime(unix2datetime(s1), tz)
end

sunup("2025-12-25", Observer(55.681461901184896, 12.57570843999704), TimeZone("Europe/Copenhagen", TimeZones.Class(:LEGACY)))
2025-12-25T08:47:01.617+01:00

```

But it’s a bit convoluted.  
Is there an easier way to do it?

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [December 25, 2025, 2:14pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/13 "2025-12-25T14:14:40Z")

</div>

Hi, thanks for trying the package. I think sunrise/sunset times can be calculated as a side effect of the solar position algorithm, making use of a few intermediate quantities. (Maybe an astronomer can weigh in here)

There are several ways to calculate sunrise/sunset times, see [Solar Position — pvlib python 0.13.1 documentation](https://pvlib-python.readthedocs.io/en/stable/reference/solarposition.html) for some examples. Could you create an issue so we can track it as a feature request?

---

<div class="post-metadata">

### Author: ![tp2750](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tp2750/32/207806_2.png) [@tp2750](https://discourse.julialang.org/u/tp2750)
#### Post date: [January 4, 2026, 5:39pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/14 "2026-01-04T17:39:23Z")

</div>

Issue filed: [Calculate sunraise and sunset · Issue #53 · JuliaAstro/SolarPosition.jl · GitHub](https://github.com/JuliaAstro/SolarPosition.jl/issues/53)

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [January 4, 2026, 9:55pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/15 "2026-01-04T21:55:39Z")

</div>

Check [Utilities · SolarPosition.jl](https://juliaastro.org/SolarPosition.jl/dev/utilities/) for a preview 🙂

I will make a new release soon.

---

<div class="post-metadata">

### Author: ![tp2750](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tp2750/32/207806_2.png) [@tp2750](https://discourse.julialang.org/u/tp2750)
#### Post date: [January 5, 2026, 12:06am UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/16 "2026-01-05T00:06:03Z")

</div>

Wow! Really nice.  
Thanks a lot.  
That is exactly what I was looking for.

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [January 5, 2026, 7:35pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/17 "2026-01-05T19:35:54Z")

</div>

# SolarPosition.jl v0.4.0

I just released SolarPosition.jl v0.4.0 🙂

It is now possible to calculate sunrise / sunset for any location on earth. The implementation is based on the SPA algorithm by Jean Meeus, particularly chapter 15 of the book Astronomical Algorithms.

### Example

```julia
using SolarPosition, Dates, TimeZones

tz = TimeZone("Europe/Brussels")
obs = Observer(52.35888, 4.88185, 100.0)
zdt = ZonedDateTime(2023, 6, 21, 12, 0, tz)

next_sunrise_time = next_sunrise(obs, zdt)
next_sunset_time = next_sunset(obs, zdt)
println("Next Sunrise: ", next_sunrise_time)
println("Next Sunset: ", next_sunset_time)

Next Sunrise: 2023-06-22T05:18:19+02:00
Next Sunset: 2023-06-21T22:06:24+02:00

```

We can also detect polar days and nights:

```julia-auto
julia> obs = Observer(78.2232, 15.6267) # Longyearbyen, Svalbard
Observer(latitude=78.2232°, longitude=15.6267°, altitude=0.0m, horizon=0.0°)

julia> dt = DateTime(2023, 6)
2023-06-01T00:00:00

julia> next_sunrise(obs, dt)
┌ Warning: Sun does not rise or set on this date at the given location: polar day (sun above horizon). Returning midnight UTC for all events.
└ @ SolarPosition.Utilities ~/dev/solar/SolarPosition.jl/src/Utilities/spa.jl:102
2023-06-02T00:00:00

```

### Sun graphs

With sunrise/sunset information you can create sun graphs as found on websites like [https://www.timeanddate.com](https://www.timeanddate.com):

 ![image](https://global.discourse-cdn.com/julialang/original/3X/9/e/9e877f1a3e0a5263805e933344f183a0591f9aa1.png)

## Benchmarks

I have added automated benchmarks, provided by AirspeedVelocity.jl. Benchmarks are run on every PR so we can automatically track performance regressions (and improvements).

 ![image](https://global.discourse-cdn.com/julialang/original/3X/7/0/70c77652d77f3d1e6b1ccf3b64e51836783c4cda.png)

The algorithms in SolarPosition.jl are typically 1-2 orders of magnitude faster than their python equivalents, but this decreases with input size.

## Numerical improvements

This release also features a bunch of numerical improvements by @nsajko.

---

<div class="post-metadata">

### Author: ![mihalybaci](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mihalybaci/32/13528_2.png) [@mihalybaci](https://discourse.julialang.org/u/mihalybaci)
#### Post date: [January 5, 2026, 8:11pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/18 "2026-01-05T20:11:24Z")

</div>

This is really cool. In the past I used the python [skyfield](https://rhodesmill.org/skyfield/) package to compute a “daylight” calendar that includes sunrise, sunset, and all the twilight times (for outdoor exercise purposes). It’ll be interesting to see if I can recreate that in Julia now!

---

<div class="post-metadata">

### Author: ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)
#### Post date: [January 5, 2026, 9:08pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/19 "2026-01-05T21:08:39Z")

</div>

See also [solar](https://www.generic-mapping-tools.org/GMTjl_doc/documentation/modules/solar.html). It plots the twilights with the GMT quality mapping.

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [January 5, 2026, 9:31pm UTC](https://discourse.julialang.org/t/ann-solarposition-jl/134263/20 "2026-01-05T21:31:57Z")

</div>

The visualizations are very nice!

[Next page](https://discourse.julialang.org/t/ann-solarposition-jl/134263.md?page=2)
