A Java library for finding topocentric solar coordinates, i.e. the sun’s position on the sky for a given date, latitude, and longitude (and other parameters), as well as times of sunrise and sunset. Calculations strictly follow well-known, peer-reviewed algorithms: SPA by Reda and Andreas and, alternatively, Grena/ENEA by Grena. More than 1000 test points are included to validate against the reference code and other sources.
Note
This library is not based on or derived from any code published by NREL, ENEA or other parties. It is an implementation precisely following the algorithms described in the respective papers.
<dependency>
<groupId>net.e175.klaus</groupId>
<artifactId>solarpositioning</artifactId>
<version>2.0.9</version>
</dependency>Java 17 or newer. No additional runtime dependencies.
The API is intentionally "flat", comprising a handful of static methods and simple records as results. To get refraction-corrected topocentric coordinates:
var dateTime = new ZonedDateTime.now();
// replace SPA with Grena3 as needed
var position = SPA.calculateSolarPosition(
dateTime,
48.21, // latitude (degrees)
16.37, // longitude (degrees)
190, // elevation (m)
DeltaT.estimate(dateTime.toLocalDate()), // delta T (s)
1010, // avg. air pressure (hPa)
11); // avg. air temperature (°C)
System.out.println(position);The SPA class includes methods to calculate the times of sunrise, sun transit, and sunset in one fell swoop. The actual return type depends on the type of day (regular day, polar day, polar night).
var result=SPA.calculateSunriseTransitSet(
dateTime,
70.978, // latitude
25.974, // longitude
69); // delta T
if(result instanceof SunriseResult.RegularDay regular) {
System.out.println(regular);
} else {
System.out.println("no sunrise or sunset today!");
}Twilight start and end times can be obtained like sunrise and sunset, but assuming a different horizon:
var result=SPA.calculateSunriseTransitSet(
dateTime,
70.978, // latitude
25.974, // longitude
69, // delta T
SPA.Horizon.CIVIL_TWILIGHT); For bulk position processing at a fixed time with many coordinates using SPA, use the optimized split methods for significantly better performance:
// Compute time-dependent parts once
final var timeDependent = SPA.calculateSpaTimeDependentParts(dateTime, deltaT);
// Reuse for multiple coordinates (up to 10x faster)
for(var coordinate: coordinates) {
var position = SPA.calculateSolarPositionWithTimeDependentParts(
coordinate.lat, coordinate.lon, coordinate.elevation, timeDependent);
}See the Javadoc for more methods.
- For many applications, Grena3 should work just fine. It's simple, fast, and pretty accurate for a time window from 2010 to 2110 CE.
- If you're looking for maximum accuracy or need to calculate for historic dates, use SPA. It's widely considered a reference algorithm for solar positioning, being very accurate and usable in a very large time window.
While Grena3 is about an order of magnitude faster than SPA, in absolute terms we are talking about microseconds. The difference mostly matters for bulk calculations.
- Calculation is based on the usual correction of 0.833° on the zenith angle, i.e. sunrise and sunset are assumed to occur when the center of the solar disc is 50 arc-minutes below the horizon. While commonly used, this fixed value fails to account for the varying effects of atmospheric refraction. Calculated and observed sunrise and sunset times may easily differ by several minutes (cf. Wilson 2018).
- As a general note on accuracy, Jean Meeus advises that "giving rising or setting times .. more accurately than to the nearest minute makes no sense" (Astronomical Algorithms). Errors increase the farther the position from the equator, i.e. values for polar regions are much less reliable.
- The SPA sunset/sunrise algorithm is one of the most accurate ones around. Results of this implementation correspond very closely to the NOAA calculator's. Also see a comparison with some other Java sunrise libraries.
The library follows the procedure in the SPA paper: sidereal time is evaluated at 0 UT (A.2.1)
while the geocentric α/δ for sunrise/sunset interpolation are evaluated at 0 TT for D−1/D/D+1 (A.2.2). The NREL
reference code (spa.c) resets ΔT to zero when building those intermediate ephemerides, effectively keeping
them in UT. This Java code preserves the supplied ΔT to stay faithful to the published algorithm rather than the
C code. As a consequence, sunrise/sunset times differ slightly from spa.c but should line up better with
high-precision ephemerides (JPL Horizons, USNO almanacs, etc.).
See Wikipedia for an explanation. For many simple applications, and particularly for sunrise and sunset, this value could be negligible as it's just over a minute (about 70 seconds) as of this writing. However, if you're looking for maximum accuracy, you should use an observed value (available from e.g. the US Naval Observatory) or at least a solid estimate.
The DeltaT class provides an estimator based on polynomials fitting a number of observed (or extrapolated) historical values, published by Espenak and Meeus in 2007 and slightly updated by Espenak in 2014.
As of 2025, it appears that today's extrapolated values from this estimator are a little high (some 2 seconds). This gap will widen in the coming decades (cf. Morrison et al. 2021). Still, the estimates should work sufficiently well for most applications.
Yes. None of the classes hold any mutable shared state.