Class MeeusCalculator

java.lang.Object
com.kosherjava.zmanim.util.AstronomicalCalculator
com.kosherjava.zmanim.util.MeeusCalculator
All Implemented Interfaces:
Cloneable

Implementation of sunrise and sunset methods using the higher-accuracy solar position algorithm of Jean Meeus from Astronomical Algorithms (2nd ed.). Unlike the NOAACalculator (which uses the low-accuracy method from the same work, with a three-term equation of center and W. M. Smart's truncated equation of time, good to roughly 0.01° / about 1 minute of time), this calculator computes the Earth's heliocentric position from the abridged VSOP87 series printed in Meeus's Appendix III, then derives the Sun's apparent geocentric longitude (applying the FK5 frame correction, nutation in longitude and the aberration of light), apparent right ascension and declination, and the equation of time per Meeus chapter 28. The resulting solar position is good to roughly an arc-second, an order of magnitude better than the low-accuracy method.

Practical note on accuracy. For horizon events (sunrise / sunset) the extra precision is largely masked by atmospheric refraction (uncertain by arc-minutes with temperature and pressure) and is therefore typically worth only a few seconds over NOAACalculator. The improvement is most visible for solar noon, the equation of time, and the Sun's elevation / azimuth when it is well above the horizon.

Refraction and the Sun's radius are applied at sunrise and sunset only. Like NOAACalculator, this class does not model atmospheric refraction itself; the horizon corrections come entirely from the parent AstronomicalCalculator.adjustZenith(double, double, LocalDate), which adds AstronomicalCalculator.getRefraction() (default 0.5667° / 34′), the solar radius - the date-based AstronomicalCalculator.getApparentSolarRadius(LocalDate) by default (it varies slightly day to day), or the fixed AstronomicalCalculator.getSolarRadius() (default 0.26667° / 16′) when AstronomicalCalculator.isUseApparentSolarRadius() is false - and the elevation dip, but only when the requested zenith is exactly the geometric 90°, that is, only for true sunrise and sunset. For every other zenith - twilight angles and depression-angle zmanim such as alos and tzais (e.g. 16.1°, 19.8°) - no refraction or solar-radius term is added and the time is solved for the Sun's true geometric depression, as those zmanim are defined geometrically. Consequently AstronomicalCalculator.setRefraction(double) nd AstronomicalCalculator.setSolarRadius(double) affect sunrise / sunset (and any 90°-based time) but have no effect on depression-angle zmanim. This calculator has no temperature or pressure inputs of any kind, since the only place they would matter is a refraction model it does not use.

ΔT (terrestrial vs. universal time). Meeus computes positions in Terrestrial Time (TT), whereas the civil times returned here are Universal Time (UT ≈ UTC). This calculator therefore applies an estimate of ΔT (currently about a minute) when evaluating the Sun's position. This is more correct than NOAACalculator, which omits ΔT. The correction can be disabled via setApplyDeltaT(boolean) to reproduce NOAA-style UT = TT behavior, which is useful for direct comparison.

Coefficient coverage. The VSOP87 tables below are the abridged Earth series from Meeus Appendix III and the principal (Meeus chapter 22 abridged) nutation terms. They yield sub-arc-second to a few arc-seconds of accuracy - far beyond what any zman requires. For full textbook precision the term tables can simply be extended with the remaining Appendix III / IAU 1980 entries; the algorithm itself does not change.

Author:
© Eliyahu Hershfeld 2026
  • Field Details

    • JULIAN_DAY_JAN_1_2000

      private static final double JULIAN_DAY_JAN_1_2000
      The Julian day of January 1, 2000, known as J2000.0.
      See Also:
    • JULIAN_DAYS_PER_CENTURY

      private static final double JULIAN_DAYS_PER_CENTURY
      Julian days per century.
      See Also:
    • applyDeltaT

      private boolean applyDeltaT
      Whether to apply the ΔT (TT - UT) correction when evaluating the Sun's position. Defaults to true (the astronomically correct behavior). Set to false to reproduce NOAA-style UT = TT results for direct comparison with NOAACalculator.
      See Also:
    • EARTH_L

      private static final double[][][] EARTH_L
      Earth heliocentric longitude series L0..L5. Abridged VSOP87 Earth series (Meeus, Astronomical Algorithms 2nd ed., Appendix III). Each term is {A, B, C}; the series value is Σ A·cos(B + C·τ), τ in Julian millennia, result in 1e-8 rad (L, B) or 1e-8 AU (R).
    • EARTH_B

      private static final double[][][] EARTH_B
      Earth heliocentric latitude series B0..B1.
    • EARTH_R

      private static final double[][][] EARTH_R
      Earth radius-vector series R0..R4 (AU).
  • Constructor Details

    • MeeusCalculator

      public MeeusCalculator()
      Default constructor of the MeeusCalculator.
  • Method Details

    • getCalculatorName

      Description copied from class: AstronomicalCalculator
      Returns the name of the algorithm.
      Specified by:
      getCalculatorName in class AstronomicalCalculator
      Returns:
      the descriptive name of the algorithm.
    • setApplyDeltaT

      public void setApplyDeltaT(boolean applyDeltaT)
      A method to enable or disable the ΔT (TT - UT) correction.
      Parameters:
      applyDeltaT - true to apply ΔT (default, astronomically correct), false to omit it and reproduce NOAA-style UT = TT behavior.
      See Also:
    • isApplyDeltaT

      public boolean isApplyDeltaT()
      is the ΔT correction being applied.
      Returns:
      whether the ΔT correction is being applied.
      See Also:
    • getUTCSunrise

      public double getUTCSunrise(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation)
      Description copied from class: AstronomicalCalculator
      A method that calculates UTC sunrise as well as any time based on an angle above or below sunrise. This abstract method is implemented by the classes that extend this class.
      Specified by:
      getUTCSunrise in class AstronomicalCalculator
      Parameters:
      dt - The LocalDate representing the date to calculate sunrise for.
      geoLocation - The location information used for astronomical calculation of solar times.
      zenith - the azimuth below the vertical zenith of 90°. For sunrise typically the zenith used for the calculation uses geometric zenith of 90° and adjusts this slightly to account for solar refraction and the sun's radius. Another example would be AstronomicalCalendar.getBeginNauticalTwilight() that passes AstronomicalCalendar.NAUTICAL_ZENITH to this method.
      adjustForElevation - Should the time be adjusted for elevation
      Returns:
      The UTC time of sunrise in 24-hour format. 5:45:00 AM will return 5.75. If an error was encountered in the calculation (expected behavior for some locations such as near the poles, Double.NaN will be returned.
      See Also:
    • getUTCSunset

      public double getUTCSunset(LocalDate dt, GeoLocation geoLocation, double zenith, boolean adjustForElevation)
      Description copied from class: AstronomicalCalculator
      A method that calculates UTC sunset as well as any time based on an angle above or below sunset. This abstract method is implemented by the classes that extend this class.
      Specified by:
      getUTCSunset in class AstronomicalCalculator
      Parameters:
      dt - The LocalDate> representing the date to calculate sunset for.
      geoLocation - The location information used for astronomical calculation of solar times.
      zenith - the azimuth below the vertical zenith of 90°. For sunset typically the zenith used for the calculation uses geometric zenith of 90° and adjusts this slightly to account for solar refraction and the sun's radius. Another example would be AstronomicalCalendar.getEndNauticalTwilight() that passes AstronomicalCalendar.NAUTICAL_ZENITH to this method.
      adjustForElevation - Should the time be adjusted for elevation
      Returns:
      The UTC time of sunset in 24-hour format. 5:45:00 AM will return 5.75. If an error was encountered in the calculation (expected behavior for some locations such as near the poles, Double.NaN will be returned.
      See Also:
    • getUTCSunRiseSet

      private double getUTCSunRiseSet(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation, MeeusCalculator.SolarEvent solarEvent)
      A method that calculates UTC sunrise or sunset as well as any time based on an angle above or below sunset and returns it as a double in 24-hour format. 5:45:00 AM will return 5.75.
      Parameters:
      localDate - Used to calculate day of year.
      geoLocation - The location information used for astronomical calculation of solar times.
      zenith - the azimuth below the vertical zenith of 90°, adjusted for refraction, the sun's radius and (optionally) elevation.
      adjustForElevation - Should the time be adjusted for elevation.
      solarEvent - if the calculation is for MeeusCalculator.SolarEvent.SUNRISE or MeeusCalculator.SolarEvent.SUNSET.
      Returns:
      The UTC time of sunrise or sunset in 24-hour format. 5:45:00 AM will return 5.75, while 5:45 PM will return 17.75. If an error was encountered in the calculation (expected behavior for some locations such as near the poles, Double.NaN will be returned.
      See Also:
    • getUTCNoon

      public double getUTCNoon(LocalDate localDate, GeoLocation geoLocation)
      Return the Universal Coordinated Time (UTC) of solar noon (UTC) for the given day at the given location. The NOAACalculator, the default calculator implementation calculates true solar noon, something that can be calculated even in the Arctic / Antarctic where there may be no sunrise or sunset, while the SunTimesCalculator approximates it, calculating the time as halfway between sunrise and sunset, something that can't be calculated in Polar regions where there is no sunrise or sunset for part of the year. See The Definition of Chatzos for details on solar noon / midnight calculations.
      Specified by:
      getUTCNoon in class AstronomicalCalculator
      Parameters:
      localDate - The LocalDate representing the date to calculate noon for.
      geoLocation - The location information used for astronomical calculation of solar times.
      Returns:
      The UTC time of solar noon in 24-hour format. 1:45:00 PM will return 13.75. If an error was encountered in the the calculation (expected behavior for some locations such as near the poles, Double.NaN will be returned.
      See Also:
    • getUTCMidnight

      public double getUTCMidnight(LocalDate localDate, GeoLocation geoLocation)
      Return the Universal Coordinated Time (UTC) of solar midnight (UTC) for the given day at the given location. The NOAACalculator, the default calculator implementation calculates true solar midnight, something that can be calculated even in the Arctic / Antarctic where there may be no sunrise or sunset, while the SunTimesCalculator approximates it, calculating the time as 12 hours after halfway between sunrise and sunset, something that can't be calculated in Polar regions where there is no sunrise or sunset for part of the year. See The Definition of Chatzos for details on solar noon / midnight calculations.
      Specified by:
      getUTCMidnight in class AstronomicalCalculator
      Parameters:
      localDate - The LocalDate representing the date to calculate midnight for. The calculation will be for midnight at the end of the day passed in.
      geoLocation - The location information used for astronomical calculation of solar times.
      Returns:
      The UTC time of solar midnight in a 24-hour double format. 1:45:00 AM will return 1.75. If an error was encountered in the calculation (expected behavior for the SunTimesCalculator at times of the year in Polar regions), Double.NaN will be returned.
      See Also:
    • getSolarNoonMidnightUTC

      private double getSolarNoonMidnightUTC(double julianDay, double longitude, MeeusCalculator.SolarEvent solarEvent)
      Return the Universal Coordinated Time (UTC) of solar noon, or the upcoming midnight (about 12 hours after solar noon) of the given day at the given location, in minutes after 0:00 UTC. Uses the high-accuracy equation of time.
      Parameters:
      julianDay - The Julian day (UT, for the start of the day).
      longitude - The longitude of the observer in degrees (positive west, matching the value passed by the callers).
      solarEvent - If the calculation is for NOON or MIDNIGHT.
      Returns:
      the time in minutes after 0:00 UTC.
    • getSunRiseSetUTC

      private double getSunRiseSetUTC(LocalDate localDate, double latitude, double longitude, double zenith, MeeusCalculator.SolarEvent solarEvent)
      Return the Universal Coordinated Time (UTC) of sunrise or sunset in minutes for the given day at the given location on earth, using the high-accuracy solar declination and equation of time.
      Parameters:
      localDate - The LocalDate.
      latitude - The latitude of observer in degrees.
      longitude - Longitude of observer in degrees (positive west).
      zenith - Zenith (already adjusted for refraction / radius / elevation by the caller).
      solarEvent - If the calculation is for SUNRISE or SUNSET.
      Returns:
      The UTC time of sunrise or sunset in minutes. Double.NaN if the event does not occur (e.g. polar day / night).
    • getTimeAtAzimuth

      public double getTimeAtAzimuth(LocalDate localDate, GeoLocation geoLocation, double targetAzimuth)
      Returns the time that the azimuth will occur for the date and location passed to this method. As an example, to know when the sun will be directly west, pass in an azimuth of 270°, and for directly east, pass in an azimuth of 90°. The current implementation in this class only supports azimuth values of 90° (directly east) or 270° (directly west) that are directly needed in this library for the ComprehensiveZmanimCalendar.getPolarSunsetBenIshChai() and ComprehensiveZmanimCalendar.getPolarSunriseBenIshChai().
      Specified by:
      getTimeAtAzimuth in class AstronomicalCalculator
      Parameters:
      localDate - The LocalDate representing the date to calculate the time of the azimuth for.
      geoLocation - The location information used for astronomical calculation of solar times.
      targetAzimuth - the azimuth to run the calculation for
      Returns:
      The UTC time that the azimuth will be reached in a 24-hour double format. 5:45:00 PM will return 17.75. If an error was encountered in the calculation (expected behavior for some dates at latitudes below 23.44°, a Double.NaN will be returned.
      Throws:
      IllegalArgumentException - if the azimuth is not 90° or 270°.
      TODO:
      complete the implementation for other azimuths. While not needed by this library, they may be of value to some projects. There will be edge cases where the azimuth will occur more than once a day when based on the equation of time, the day is shorter than 24 hours. In that case, the time for the first one will be returned.
    • getSolarElevation

      public double getSolarElevation(Instant instant, GeoLocation geoLocation)
      Description copied from class: AstronomicalCalculator
      Return the Solar Elevation for the horizontal coordinate system at the given location at the given time. Can be negative if the sun is below the horizon. Not corrected for altitude.
      Specified by:
      getSolarElevation in class AstronomicalCalculator
      Parameters:
      instant - The instant to calculate the elevation for.
      geoLocation - The location information used for astronomical calculation of solar times.
      Returns:
      solar elevation in degrees. The horizon (calculated in a vacuum using the solar radius as the point)is 90°, civil twilight is 96° etc. This means that sunrise and sunset that do use refraction and are calculated from the upper limb of the sun will return about 0.8333°.
    • getSolarAzimuth

      public double getSolarAzimuth(Instant instant, GeoLocation geoLocation)
      Description copied from class: AstronomicalCalculator
      Return the Solar Azimuth for the horizontal coordinate system at the given location at the given time. Not corrected for altitude. True south is 180°.
      Specified by:
      getSolarAzimuth in class AstronomicalCalculator
      Parameters:
      instant - The instant to calculate the azimuth for.
      geoLocation - The location information used for astronomical calculation of solar times.
      Returns:
      the solar azimuth in degrees. Astronomical midday would be 180° in the northern hemisphere and 0° in the southern hemisphere. Depending on the location and time of year, sunrise will have an azimuth of about 90° and sunset about 270°.
    • getSolarElevationAzimuth

      private double getSolarElevationAzimuth(Instant instant, GeoLocation geoLocation, boolean isAzimuth)
      Return the solar elevation or solar azimuth at the given location and time, using the high-accuracy solar declination and equation of time. Elevation is sea-level based and corrected for atmospheric refraction; it is not adjusted for the observer's altitude.
      Parameters:
      instant - date-time of calculation.
      geoLocation - the location for calculating the elevation or azimuth.
      isAzimuth - true for azimuth, false for elevation.
      Returns:
      solar elevation or azimuth in degrees.
    • adjustElevationForRefraction

      private double adjustElevationForRefraction(double elevation)
      Apply an atmospheric refraction adjustment to the solar elevation (same model used by NOAACalculator).
      Parameters:
      elevation - the elevation to adjust.
      Returns:
      the refraction adjustment in degrees.
    • getSunHourAngle

      private static double getSunHourAngle(double latitude, double solarDeclination, double zenith, MeeusCalculator.SolarEvent solarEvent)
      Return the hour angle of the sun in radians for the given latitude, declination and zenith.
      Parameters:
      latitude - the latitude of observer in degrees.
      solarDeclination - the declination of the sun in degrees.
      zenith - the (adjusted) zenith in degrees.
      solarEvent - MeeusCalculator.SolarEvent.SUNRISE or MeeusCalculator.SolarEvent.SUNSET.
      Returns:
      the hour angle in radians, or Double.NaN if the sun does not reach the zenith on that day.
    • getJulianDay

      private static double getJulianDay(LocalDate localDate)
      Return the Julian day for the start (0h UT) of the given date. Fractional days / time of day should be added later. (Meeus, chapter 7.)
      Parameters:
      localDate - the LocalDate.
      Returns:
      the Julian day for 0h UT of the date.
    • getJulianCenturiesFromJulianDay

      private double getJulianCenturiesFromJulianDay(double julianDayUT)
      Convert a Universal-Time Julian day to Julian centuries since J2000.0 in Terrestrial Time. When applyDeltaT is true the ΔT (TT - UT) offset is added so that the solar-position series, which are defined in TT, are evaluated correctly.
      Parameters:
      julianDayUT - the UT Julian day to convert.
      Returns:
      the Terrestrial-Time centuries since J2000.0.
    • getDeltaT

      private static double getDeltaT(double julianDay)
      Estimate ΔT = TT - UT in seconds for the given Julian day, using the polynomial expressions of Espenak and Meeus. The value is approximate (especially in the far past / future) but accurate to a few seconds for the modern era, which is well within the precision relevant here.
      Parameters:
      julianDay - the (UT) Julian day.
      Returns:
      ΔT in seconds.
    • getSunDeclination

      private static double getSunDeclination(double julianCenturies)
      Return the apparent declination of the sun in degrees, computed from the apparent geocentric ecliptic coordinates derived from the VSOP87 series.
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      the sun's apparent declination in degrees.
    • getSunRightAscension

      private static double getSunRightAscension(double julianCenturies)
      Return the apparent geocentric right ascension of the sun in degrees (0-360).
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      the sun's apparent right ascension in degrees.
    • getSunApparentEclipticCoordinates

      private static double[] getSunApparentEclipticCoordinates(double julianCenturies)
      Return the sun's apparent geocentric ecliptic longitude and latitude in degrees, built from the Earth's heliocentric VSOP87 position with the FK5 frame correction, nutation in longitude and the aberration of light applied (Meeus chapter 25).
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      a two-element array {apparent longitude λ, apparent latitude β} in degrees.
    • getTrueObliquity

      private static double getTrueObliquity(double julianCenturies)
      Return the true obliquity of the ecliptic ε = ε0 + Δε in degrees (mean obliquity, Meeus 22.2, plus nutation in obliquity).
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      the true obliquity in degrees.
    • getNutation

      private static double[] getNutation(double julianCenturies)
      Return the principal terms of the nutation in longitude (Δψ) and obliquity (Δε) in arc-seconds (Meeus chapter 22, abridged). These dominant terms give sub-arc-second accuracy in the nutation, which is far beyond what is required here; the full IAU 1980 series can be substituted for textbook precision.
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      a two-element array {Δψ, Δε} in arc-seconds.
    • getEquationOfTime

      private static double getEquationOfTime(double julianCenturies)
      Return the equation of time - apparent minus mean solar time - in minutes of time, following Meeus chapter 28 (E = L0 - 0.0057183° - α + Δψ·cos ε). The sign convention matches NOAACalculator so the rise/set formulas are interchangeable.
      Parameters:
      julianCenturies - Terrestrial-Time centuries since J2000.0.
      Returns:
      the equation of time in minutes of time.
    • sumSeries

      private static double sumSeries(double[][][] series, double tau)
      Evaluate a VSOP87 series of the form Σni Ai·cos(Bi + Ci·τ)) · τn, where the coefficients are stored in 1e-8 of the series' native unit (radians for longitude/latitude, AU for the radius vector).
      Parameters:
      series - the series, indexed as series[n][term][{A, B, C}].
      tau - the time in Julian millennia from J2000.0.
      Returns:
      the evaluated quantity (radians or AU).
    • equals

      public boolean equals(Object object)

      Two AstronomicalCalculator instances are considered equal if their Earth radius, refraction, and solar radius values are identical.

      Two MeeusCalculator instances are considered equal if their isApplyDeltaT() value is identical.

      Overrides:
      equals in class AstronomicalCalculator
      Parameters:
      object - the reference object with which to compare
      Returns:
    • hashCode

      public int hashCode()

      This implementation hashes the earth radius, refraction, and solar radius properties to maintain the contract with AstronomicalCalculator.equals(Object).

      This implementation hashes the applyDeltaT property to maintain the contract with equals(Object).

      Overrides:
      hashCode in class AstronomicalCalculator
      Returns: