commons-suncalc offers six astronomical calculations:

Quick Start

All of the calculations mentioned above are invoked in the same way:

ZonedDateTime dateTime =    // date, time and timezone of calculation
double lat, lng =           // geolocation
SunTimes times = SunTimes.compute()
            .on(dateTime)   // set a date
            .at(lat, lng)   // set a location
            .execute();     // get the results
System.out.println("Sunrise: " + times.getRise());
System.out.println("Sunset: " + times.getSet());

You invoke compute(), set your parameters, invoke execute(), and then get the result of the calculation as an object.

All parameters are passed in by a fluent builder-style interface. After retrieving the builder from compute(), you can chain the parameter setters, and finally call execute() to perform the computation.

SunPosition pos = SunPosition.compute().today().at(12.3, 45.6).execute();

It is also possible to collect the parameters first, and execute the computation in a separate step:

SunPosition.Parameters param = SunPosition.compute();;, 45.6);

SunPosition pos = param.execute();

The instance that is returned by execute() is immutable and only holds the calculation result of the current set of parameters. You can modify the parameters without changing the first result, then call execute() again for a second result.

param.on(2016, 12, 24); // modify the param from above

SunPosition posAtChristmas = param.execute();
// pos from above is unchanged

Time-based Parameters

All calculations need a date and time parameter. Some examples:

// Now (the default)

// The same: Current time, local time zone
ZonedDateTime now =;

// August 21st, 2017, local midnight
SunPosition.compute().on(2017, 8, 21);

// Today (midnight), Berlin time zone

The available time-based parameters are:

  • on(int year, int month, int date): Midnight of the given date. Note that month is counted from 1 (1 = January, 2 = February, …).
  • on(int year, int month, int date, int hour, int minute, int second): Given date and time.
  • on(ZonedDateTime dateTime): Given date, time, and timezone.
  • on(LocalDateTime dateTime): Given local date and time, without a timezone.
  • on(LocalDate date): Midnight of the given local date, without a timezone.
  • on(Instant instant): An instant without a timezone.
  • on(Calendar cal): Date, time and timezone as given in the old-fashioned Calendar. The Calender is copied and can safely be modified after that.
  • on(Date date): Date and time as given in the old-fashioned Date. The Date is copied and can safely be modified after that.
  • plusDays(int days): Adds the given number of days to the current date. days can also be negative, of course.
  • now(): The current system date and time. This is the default.
  • midnight(): Past midnight of the current date. It just truncates the time.
  • today(): Identical to .now().midnight().
  • tomorrow(): Identical to today().plusDays(1).
  • timezone(ZoneId tz): Use the given ZoneId as timezone. The current local time is unchanged (this is, it is not converted to the new timezone), so the order of parameters is not important.
  • timezone(String id): Same as above, but accepts a String for your convenience.
  • timezone(TimeZone tz): Same as above, but accepts an old-fashioned TimeZone object.
  • localTime(): The system's timezone. This is the default.
  • utc(): UTC timezone. Identical to timezone("UTC").
  • sameTimeAs(TimeParameter<?> t): Copies the current date, time, and timezone from any other parameter object. Note that subsequent changes to the other object are not adopted.

If no time-based parameter is given, the current date and time, and the system's time zone is used.


The accuracy of the results is decreasing for dates that are far in the future, or far in the past.

Location-based Parameters

The geolocation is required, and execute() will throw an exception if the latitude or longitude is missing. The elevation is optional, and is 0 meters (sea level) if not set.

// At 20.5°N, 18.3°E
SunPosition.compute().at(20.5, 18.3);

// The same, but more verbose

// Use arrays for coordinate constants
final double[] COLOGNE = new double[] { 50.938056, 6.956944 };

There are two exceptions:

  • MoonIllumination: If the geolocation is set, the result is topocentric. If the geolocation is unset, the result is geocentric.
  • MoonPhase: The geolocation is not used here.

The available location-based parameters are:

  • at(double lat, double lng): Latitude and longitude to be used for computation.
  • at(double[] coords): Accepts an array of 2 values (latitude, longitude) or 3 values (latitude, longitude, elevation).
  • latitude(double lat): Verbose way to set the latitude only.
  • longitude(double lng): Verbose way to set the longitude only.
  • latitude(int d, int m, double s): Set the latitude in degrees, minutes, seconds and fraction of seconds.
  • longitude(int d, int m, double s): Set the longitude in degrees, minutes, seconds and fraction of seconds.
  • elevation(double h): Elevation above sea level, in meters. Sea level is used by default.
  • elevationFt(double h): Elevation above sea level, in foot. Sea level is used by default.
  • sameLocationAs(LocationParameter<?> l): Copies the current location and elevation from any other parameter object. Note that subsequent changes to the other object are not adoped.


elevation cannot be negative. If you pass in a negative elevation, it is silently changed to the accepted minimum of 0 meters. For this reason, it is safe to pass coordinates from satellite-based navigation systems without range checking.

Time Range

By default, SunTimes and MoonTimes will calculate a full cycle of the sun or moon. This means that rise, set, noon and nadir times may be more than 24 hours ahead.

You can limit the time window to the next 24 hours by using the oneDay() parameter. You can also give any other window by using limit(Duration duration). If the sun or moon does not rise or set within that window, the appropriate getters return null. You can check if the sun or moon is always above or below the horizon, by checking isAlwaysUp() and isAlwaysDown().


Twilight Zones

By default, SunTimes calculates the time of the visual sunrise and sunset. This means that getRise() returns the instant when the sun just starts to rise above the horizon, and getSet() returns the instant when the sun just disappeared from the horizon. Atmospheric refraction is taken into account.

There are other interesting twilight angles available. You can set them via the twilight() parameter, by using one of the SunTimes.Twilight constants:

Constant Description Angle of the Sun Topocentric
VISUAL The moment when the visual upper edge of the sun crosses the horizon. This is the default. yes
VISUAL_LOWER The moment when the visual lower edge of the sun crosses the horizon. yes
ASTRONOMICAL Astronomical twilight -18° no
NAUTICAL Nautical twilight -12° no
CIVIL Civil twilight -6° no
HORIZON The moment when the center of the sun crosses the horizon. no
GOLDEN_HOUR Transition from daylight to Golden Hour no
BLUE_HOUR Transition from Golden Hour to Blue Hour -4° no
NIGHT_HOUR Transition from Blue Hour to night -8° no

The illustration shows the transitions of each twilight constant. If you want to get the duration of a twilight, you need to calculate the times of both transitions of the twilight. For example, to get the beginning and ending of the civil twilight, you need to calculate both the VISUAL and the CIVIL twilight transition times.

Alternatively you can also pass any other angle (in degrees) to twilight().


Only VISUAL and VISUAL_LOWER are topocentric. They refer to the visual edge of the sun, take account of the elevation parameter, and compensate atmospheric refraction.

All other twilights are geocentric and heliocentric. The elevation parameter is then ignored, and atmospheric refraction is not compensated.




By default, MoonPhase calculates the date of the next new moon. If you want to compute the date of another phase, you can set it via the phase() parameter, by using one of the MoonPhase.Phase constants:

Constant Description Angle
NEW_MOON Moon is not illuminated (new moon). This is the default.
WAXING_CRESCENT Waxing crescent moon. 45°
FIRST_QUARTER Half of the waxing moon is illuminated. 90°
WAXING_GIBBOUS Waxing gibbous moon. 135°
FULL_MOON Moon is fully illuminated. 180°
WANING_GIBBOUS Waning gibbous moon. 225°
LAST_QUARTER Half of the waning moon is illuminated. 270°
WANING_CRESCENT Waning crescent moon. 315°

Alternatively you can also pass any other angle (in degrees) to phase().