High-level API for common astronomical observations.
This module is the primary public interface for the Astro library. It
provides sunrise and sunset, moonrise and moonset, equinoxes and
solstices, lunar phases, crescent visibility, and the positions of the
sun and moon. Functions accept standard Elixir dates and date times.
Those that can fail, such as the rise and set times, return
{:ok, value} or {:error, reason}. The positions and phases, which
cannot fail, return their value directly.
For lower-level access see Astro.Solar, Astro.Lunar, Astro.Time,
Astro.Earth and Astro.Ephemeris.
How accurate the results are, inside and outside the JPL ephemeris and far from the present, is described in the accuracy guide.
Specifying a location
A location is a {longitude, latitude} tuple (note the order, which
matches Geo.Point), a Geo.Point struct, or a Geo.PointZ struct
that also carries an elevation in metres.
Longitude is positive east and negative west, in degrees.
Latitude is positive north and negative south, in degrees.
Time zone resolution
The rise and set functions (sunrise/3, sunset/3, moonrise/3 and
moonset/3) return a DateTime in the local time zone of the
location, resolved with TzWorld when it is a dependency. These
options override that:
:time_zoneis a time zone name,:utc, or:defaultto resolve the time zone from the location.:time_zone_databaseis the time zone database module, such asTz.TimeZoneDatabase.:time_zone_resolveris a 1-arity function that receives aGeo.Pointand returns{:ok, time_zone_name}.
Function groups
Solar
sunrise/3andsunset/3return the local times of sunrise and sunset.solar_noon/2returns the solar noon for a location and date.hours_of_daylight/2andduration_of_daylight/2return the length of the day.sun_position_at/1returns the sun's right ascension, declination and distance.sun_azimuth_elevation/2returns the sun's azimuth and elevation at a date time.sun_apparent_longitude/1returns the sun's apparent ecliptic longitude.
Lunar
moonrise/3andmoonset/3return the local times of moonrise and moonset.moon_position_at/1returns the moon's right ascension, declination and distance.illuminated_fraction_of_moon_at/1returns the fraction of the moon that is illuminated.lunar_phase_at/1returns the phase angle, from 0° to 360°.lunar_phase_emoji/1returns the emoji for a phase angle.
New moon and phase searches
date_time_new_moon_before/1,date_time_new_moon_at_or_after/1anddate_time_new_moon_nearest/1find new moons.date_time_lunar_phase_at_or_before/2anddate_time_lunar_phase_at_or_after/2find any phase.
Crescent visibility
new_visible_crescent/4predicts the visibility of the new crescent moon.
Equinoxes and solstices
equinox/3returns the March or September equinox.solstice/3returns the June or December solstice.
Summary
Functions
Returns the date time of a given lunar phase at or after a given date time or date.
Returns the date time of a given lunar phase at or before a given date time or date.
Returns the date time of the new moon at or after a given date or date time.
Returns the date time of the new moon before a given date or date time.
Returns the date time of the new moon nearest to a given date or date time.
Returns the duration of daylight for a given location on a
given date as a Duration.t/0.
Returns the datetime of either the March or September equinox, in UTC or a requested time zone.
Returns the evening on which the next new crescent is first seen, on or after a date.
Returns the evening on which the last new crescent was first seen, on or before a date.
Returns the number of hours of daylight for a given location on a given date.
Returns the fraction of the Moon's disc that is illuminated at a date or date time.
Guards that a value is a lunar phase angle.
Returns the local circumstances of a lunar eclipse at a location: the eclipse's contacts and greatest eclipse, and the Moon's altitude at each.
Returns the first lunar eclipse whose greatest eclipse is at or after a date or date time.
Returns the last lunar eclipse whose greatest eclipse is before a date or date time.
Returns the lunar phase in degrees at a date or date time.
Returns the moon phase as a UTF8 binary representing an emoji of the moon phase.
Returns the Moon's apparent right ascension, declination and distance at a date or date time.
Returns the moonrise for a given location and date.
Returns the moonset for a given location and date.
Predicts the visibility of the new crescent moon at a given location on a given date using one of three published criteria.
Predicts the visibility of the new crescent moon, with options.
Returns the local circumstances of a solar eclipse at a location: when it begins and ends there, its maximum, how much of the Sun is covered, and the Sun's altitude at each contact.
Returns the first solar eclipse whose greatest eclipse is at or after a date or date time.
Returns the last solar eclipse whose greatest eclipse is before a date or date time.
Returns the path of a solar eclipse: its central line and the northern and southern limits of the band in which it is total or annular, and the bounds of the region in which it is seen as a partial eclipse.
Returns solar noon at a location on a date, as a UTC date time.
Returns the datetime of either the June or December solstice, in UTC or a requested time zone.
Returns the Sun's apparent ecliptic longitude at a date or date time.
Returns the Sun's azimuth and elevation as seen from a location at a date time.
Returns the Sun's apparent right ascension, declination and distance at a date or date time.
Calculates the sunrise for a given location and date.
Calculates the sunset for a given location and date.
Types
@type altitude() :: float()
@type angle() :: number()
@type astronomical_units() :: number()
@type date() :: Calendar.date() | Calendar.datetime()
@type degrees() :: float()
@type kilometers() :: number()
@type latitude() :: float()
@type location() :: {longitude(), latitude()} | {longitude(), latitude(), altitude()} | Geo.Point.t() | Geo.PointZ.t()
@type longitude() :: float()
@type meters() :: number()
@type method() :: :odeh | :yallop | :schaefer
@type options() :: keyword()
@type phase() :: angle()
@type radians() :: float()
Functions
@spec date_time_lunar_phase_at_or_after(date(), phase()) :: {:ok, DateTime.t()} | {:error, :invalid_date | :invalid_phase | :year_out_of_range}
Returns the date time of a given lunar phase at or after a given date time or date.
The lunar phase (see lunar_phase_at/1) is the Moon's apparent
ecliptic longitude less the Sun's: 0° at new moon, 90° at first
quarter, 180° at full moon and 270° at last quarter. The search
estimates when the phase is next reached at or after the date from
the mean synodic month of 29.530589 days, then bisects a four-day
bracket around the estimate until it is narrower than a
millisecond. The phase comes from the JPL DE440s ephemeris where
the loaded kernel covers the date, and from the analytical series
elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0.phaseis the required lunar phase expressed as a float number of degrees between0.0and360.0.
Returns
{:ok, date_time}, the UTCDateTime.t/0at which the phase occurs.{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000, the range of the new moon searches.{:error, :invalid_phase}ifphaseis not a number from 0 to 360.
Accuracy
The search ends within a millisecond of the crossing. For the new moon of 18 February 1977, Meeus gives 3h37m40s TD as the correct time (Astronomical Algorithms, example 49.a); Astro gives 3h37m39.9s TD. Outside the ephemeris the series place the phases within about 6 s RMS of JPL over 1900–2100 (new moons 20 s and full moons 30 s at most). Far from the present the result is limited by ΔT; the accuracy guide gives the details.
Examples
iex> Astro.date_time_lunar_phase_at_or_after(~D[2021-08-01], Astro.Lunar.full_moon_phase())
{:ok, ~U[2021-08-22 12:01:58.152306Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 49 (phases of the Moon).
Lunar phase and Bisection method, Wikipedia.
@spec date_time_lunar_phase_at_or_before(date(), phase()) :: {:ok, DateTime.t()} | {:error, :invalid_date | :invalid_phase | :year_out_of_range}
Returns the date time of a given lunar phase at or before a given date time or date.
The lunar phase (see lunar_phase_at/1) is the Moon's apparent
ecliptic longitude less the Sun's: 0° at new moon, 90° at first
quarter, 180° at full moon and 270° at last quarter. The search
estimates when the phase is next reached at or before the date from
the mean synodic month of 29.530589 days, then bisects a four-day
bracket around the estimate until it is narrower than a
millisecond. The phase comes from the JPL DE440s ephemeris where
the loaded kernel covers the date, and from the analytical series
elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0.phaseis the required lunar phase expressed as a float number of degrees between0.0and3660.0.
Returns
{:ok, date_time}, the UTCDateTime.t/0at which the phase occurs.{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000, the range of the new moon searches.{:error, :invalid_phase}ifphaseis not a number from 0 to 360.
Accuracy
The search ends within a millisecond of the crossing. For the new moon of 18 February 1977, Meeus gives 3h37m40s TD as the correct time (Astronomical Algorithms, example 49.a); Astro gives 3h37m39.9s TD. Outside the ephemeris the series place the phases within about 6 s RMS of JPL over 1900–2100 (new moons 20 s and full moons 30 s at most). Far from the present the result is limited by ΔT; the accuracy guide gives the details.
Examples
iex> Astro.date_time_lunar_phase_at_or_before(~D[2021-08-01], Astro.Lunar.new_moon_phase())
{:ok, ~U[2021-07-10 01:16:37.291067Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 49 (phases of the Moon).
Lunar phase and Bisection method, Wikipedia.
@spec date_time_new_moon_at_or_after(date()) :: {:ok, DateTime.t()} | {:error, :invalid_date | :year_out_of_range}
Returns the date time of the new moon at or after a given date or date time.
A new moon is the moment the lunar phase (see lunar_phase_at/1)
passes 0°: the Moon's apparent ecliptic longitude equals the Sun's.
The Moon is then between the Earth and the Sun with its dark side
towards us. This is a conjunction in longitude; the Moon can pass
up to about 5° north or south of the Sun, which is why most new
moons are not eclipses.
The search numbers lunations as Meeus, Astronomical Algorithms, chapter 49, does, and that chapter's series estimates each lunation's new moon to within seconds near the present. The estimates of two consecutive lunations bracket the date, and each is refined to the moment the phase passes 0° by the secant method, which converges in a few steps, to under a millisecond. The phase comes from the JPL DE440s ephemeris where the loaded kernel covers the date, and from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0.
Returns
{:ok, date_time}, the UTCDateTime.t/0at which the new moon occurs.{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000, beyond which the Meeus chapter 49 estimates can no longer tell the lunations apart.
Accuracy
For the new moon of 18 February 1977, Meeus gives 3h37m40s TD as the correct time (Astronomical Algorithms, example 49.a); Astro gives 3h37m39.9s TD. Outside the ephemeris the series place new moons within 6.0 s RMS of JPL over 1900–2100, and 20 s at most. Far from the present the result is limited by ΔT, uncertain by minutes before about 1000 and after about 2500; the accuracy guide gives the details.
Examples
iex> Astro.date_time_new_moon_at_or_after(~D[2021-08-23])
{:ok, ~U[2021-09-07 00:51:46.307784Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 49 (phases of the Moon).
New moon and Secant method, Wikipedia.
@spec date_time_new_moon_before(date()) :: {:ok, DateTime.t()} | {:error, :invalid_date | :year_out_of_range}
Returns the date time of the new moon before a given date or date time.
A new moon is the moment the lunar phase (see lunar_phase_at/1)
passes 0°: the Moon's apparent ecliptic longitude equals the Sun's.
The Moon is then between the Earth and the Sun with its dark side
towards us. This is a conjunction in longitude; the Moon can pass
up to about 5° north or south of the Sun, which is why most new
moons are not eclipses.
The search numbers lunations as Meeus, Astronomical Algorithms, chapter 49, does, and that chapter's series estimates each lunation's new moon to within seconds near the present. The estimates of two consecutive lunations bracket the date, and each is refined to the moment the phase passes 0° by the secant method, which converges in a few steps, to under a millisecond. The phase comes from the JPL DE440s ephemeris where the loaded kernel covers the date, and from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0.
Returns
{:ok, date_time}, the UTCDateTime.t/0at which the new moon occurs.{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000, beyond which the Meeus chapter 49 estimates can no longer tell the lunations apart.
Accuracy
For the new moon of 18 February 1977, Meeus gives 3h37m40s TD as the correct time (Astronomical Algorithms, example 49.a); Astro gives 3h37m39.9s TD. Outside the ephemeris the series place new moons within 6.0 s RMS of JPL over 1900–2100, and 20 s at most. Far from the present the result is limited by ΔT, uncertain by minutes before about 1000 and after about 2500; the accuracy guide gives the details.
Examples
iex> Astro.date_time_new_moon_before(~D[2021-08-23])
{:ok, ~U[2021-08-08 13:50:08.051917Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 49 (phases of the Moon).
New moon and Secant method, Wikipedia.
@spec date_time_new_moon_nearest(date()) :: {:ok, DateTime.t()} | {:error, :invalid_date | :year_out_of_range}
Returns the date time of the new moon nearest to a given date or date time.
A new moon is the moment the lunar phase (see lunar_phase_at/1)
passes 0°: the Moon's apparent ecliptic longitude equals the Sun's.
The Moon is then between the Earth and the Sun with its dark side
towards us. This is a conjunction in longitude; the Moon can pass
up to about 5° north or south of the Sun, which is why most new
moons are not eclipses.
It is the nearer of the new moons found by the lunar phase searches
either side of the date (see date_time_lunar_phase_at_or_before/2
and date_time_lunar_phase_at_or_after/2), and agrees with
date_time_new_moon_before/1 and date_time_new_moon_at_or_after/1
to a millisecond.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0.
Returns
{:ok, date_time}, the UTCDateTime.t/0at which the new moon occurs.{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000, beyond which the Meeus chapter 49 estimates can no longer tell the lunations apart.
Accuracy
For the new moon of 18 February 1977, Meeus gives 3h37m40s TD as the correct time (Astronomical Algorithms, example 49.a); Astro gives 3h37m39.9s TD. Outside the ephemeris the series place new moons within 6.0 s RMS of JPL over 1900–2100, and 20 s at most. Far from the present the result is limited by ΔT, uncertain by minutes before about 1000 and after about 2500; the accuracy guide gives the details.
Examples
iex> Astro.date_time_new_moon_nearest(~D[2021-08-23])
{:ok, ~U[2021-08-08 13:50:08.051866Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 49 (phases of the Moon).
New moon and Secant method, Wikipedia.
@spec duration_of_daylight(location(), Calendar.date()) :: {:ok, Duration.t()} | {:error, atom()}
Returns the duration of daylight for a given location on a
given date as a Duration.t/0.
This is the same calculation as hours_of_daylight/2 but,
because a Duration.t/0 is not bounded like a Time.t/0,
it can represent a full 24 hours of daylight (returned as
%Duration{hour: 24}) during the polar summer rather than
capping at ~T[23:59:59].
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first.dateis anyDate.t/0in the Gregorian calendar (for example,Calendar.ISO).
Returns
{:ok, duration}wheredurationis aDuration.t/0between%Duration{}(no daylight) and%Duration{hour: 24}(24 hours of daylight).{:error, reason}for an invalid location or date, or if the time zone for the location cannot be resolved, as forsunrise/3.
Accuracy
It is taken from sunrise/3 and sunset/3, each within 1.6 s of
Skyfield for the same definition of the horizon, and is rounded
down to the second. Refraction in the real atmosphere moves both
events, by different amounts.
Examples
iex> Astro.duration_of_daylight({151.20666584, -33.8559799094}, ~D[2019-12-10])
{:ok, %Duration{hour: 14, minute: 20, second: 50}}
# 24 hours of daylight in the polar summer, uncapped
iex> Astro.duration_of_daylight({-62.3481, 82.5018}, ~D[2019-06-07])
{:ok, %Duration{hour: 24}}
# No daylight in the polar winter
iex> Astro.duration_of_daylight({-62.3481, 82.5018}, ~D[2019-12-07])
{:ok, %Duration{}}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting).
Daytime, Midnight sun and Polar night, Wikipedia.
@spec equinox(Calendar.year(), :march | :september, options()) :: {:ok, DateTime.t()} | {:error, :invalid_year | :invalid_event | :year_out_of_range | :time_zone_not_found | :utc_only_time_zone_database | :invalid_time_zone_database}
Returns the datetime of either the March or September equinox, in UTC or a requested time zone.
An equinox is the moment the Sun's apparent ecliptic longitude reaches 0° (the March equinox) or 180° (the September equinox). The Sun is then very nearly on the celestial equator, heading north in March and south in September, and day and night are of almost equal length everywhere.
The moment is found from the Sun's apparent ecliptic longitude
(see sun_apparent_longitude/1). Starting 20 days before the 1st
of the month, the Sun's mean motion of 360° a tropical year
estimates when it reaches the target longitude, and a ten-day
bracket around that estimate is bisected until it is narrower than
a millisecond. The longitude comes from the JPL DE440s ephemeris where the
loaded kernel covers the year, and from the analytical series for
any other year.
Arguments
yearis the Gregorian year for which the equinox is to be calculated, within 50,000 years of 2000.eventis either:marchor:septemberindicating which of the two annual equinox datetimes is required.optionsis a keyword list of options.
Options
:time_zoneis:utc(the default) or a time zone name such as"Asia/Tokyo". The equinox is one instant everywhere; the time zone decides the local date and time it is given in, and so the civil day it falls on.:time_zone_databaseis the module implementingCalendar.TimeZoneDatabasein which a named time zone is looked up. The default is the configured database,Calendar.get_time_zone_database/0. UTC needs none.
Returns
{:ok, datetime}, the equinox in the requested time zone.{:error, :time_zone_not_found}if the time zone is not known to the time zone database.{:error, :utc_only_time_zone_database}if a time zone other than UTC is requested and no time zone database is configured.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :invalid_year}ifyearis not an integer.{:error, :year_out_of_range}ifyearis more than 50,000 years from 2000.{:error, :invalid_event}ifeventis not:marchor:september.
Accuracy
The search ends within a millisecond of the crossing. For the June solstice of 1962, Meeus gives 21h24m42s TD from the full VSOP87 theory (Astronomical Algorithms, chapter 27); Astro gives 21:24:07.8 UTC, which is 21h24m42.0s TD. Outside the ephemeris the series put the events 14 s early on average over 1900–2100, 18 s RMS and 50 s at most. Far from the present the time is limited by ΔT, which is uncertain by minutes before about 1000 and after about 2500; the accuracy guide gives the details.
Examples
iex> {:ok, dt} = Astro.equinox 2019, :march
iex> DateTime.truncate(dt, :second)
~U[2019-03-20 21:58:24Z]
iex> {:ok, dt} = Astro.equinox 2019, :september
iex> DateTime.truncate(dt, :second)
~U[2019-09-23 07:50:08Z]
iex> {:ok, dt} = Astro.equinox 900, :march
iex> DateTime.to_date(dt)
~D[0900-03-20]
# The March 2019 equinox is on the 20th in UTC but the 21st in Tokyo
iex> {:ok, dt} = Astro.equinox(2019, :march, time_zone: "Asia/Tokyo")
iex> DateTime.to_date(dt)
~D[2019-03-21]
iex> Astro.equinox(2019, :march,
...> time_zone: "Asia/Tokyo",
...> time_zone_database: Calendar.UTCOnlyTimeZoneDatabase
...> )
{:error, :utc_only_time_zone_database}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 27 (equinoxes and solstices).
United States Naval Observatory, Earth's Seasons.
Equinox and Tropical year, Wikipedia.
@spec first_visible_crescent_on_or_after(location(), date(), method(), keyword()) :: {:ok, Date.t()} | {:error, atom()}
Returns the evening on which the next new crescent is first seen, on or after a date.
See first_visible_crescent_on_or_before/4 for the first sighting.
The month after the one date falls in begins the day after the
first sighting on or after the day after date.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0. Longitude comes first.dateis aDate.t/0, the evening to search forward from.methodis:odeh(the default),:yallopor:schaefer.optionsis a keyword list of options, as forfirst_visible_crescent_on_or_before/4.
Returns
{:ok, date}, the evening of the first sighting, or the errors offirst_visible_crescent_on_or_before/4.
Accuracy
As for first_visible_crescent_on_or_before/4.
Examples
iex> Astro.first_visible_crescent_on_or_after({39.8262, 21.4225}, ~D[2024-12-20])
{:ok, ~D[2025-01-01]}Reference
- Edward M. Reingold and Nachum Dershowitz, Calendrical
Calculations,
phasis-on-or-after.
@spec first_visible_crescent_on_or_before(location(), date(), method(), keyword()) :: {:ok, Date.t()} | {:error, atom()}
Returns the evening on which the last new crescent was first seen, on or before a date.
A lunar month that begins at the first sighting of the new crescent,
as an observational Islamic calendar's does, begins the day after the
evening it is first seen. A lunation's crescent is first seen on the
first evening, from the evening before the day of its new moon, on
which new_visible_crescent/4 classes it visible (:A, :B or :C
by default). This is the latest such evening on or before date:
the month date falls in began the day after it.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0. Longitude comes first.dateis aDate.t/0, the evening to search back from.methodis:odeh(the default),:yallopor:schaefer; seenew_visible_crescent/3.optionsis a keyword list of options.
Options
:visibleis the list of categories that count as a sighting. The default is[:A, :B, :C].:utc_offsetand:extinctionare as fornew_visible_crescent/4.
Returns
{:ok, date}, the evening of the first sighting.{:error, :no_visible_crescent}if a lunation's crescent is not seen within 30 evenings of its new moon, as at high latitudes.{:error, :invalid_location},{:error, :invalid_date},{:error, :invalid_method},{:error, :invalid_options},{:error, :invalid_visible}or{:error, :invalid_utc_offset}for an invalid argument or option.{:error, :year_out_of_range}ifdateis more than 50,000 years from J2000.
Accuracy
As for new_visible_crescent/3: the evening is the one the criterion
predicts, which can differ from an actual sighting by a day when the
crescent is near the criterion's boundary.
Examples
iex> Astro.first_visible_crescent_on_or_before({39.8262, 21.4225}, ~D[2024-12-20])
{:ok, ~D[2024-12-02]}Reference
Mohammad Sh. Odeh, New Criterion for Lunar Crescent Visibility, Experimental Astronomy 18 (2004), 39–64.
Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations,
phasis-on-or-before.
@spec hours_of_daylight(location(), Calendar.date()) :: {:ok, Time.t()} | {:error, atom()}
Returns the number of hours of daylight for a given location on a given date.
Daylight is the time the Sun is above the horizon during the local
calendar day, from the sunrise and sunset of sunrise/3 and
sunset/3 in the location's time zone. It is counted from the
events that fall within the day rather than as sunset less
sunrise, so a day near a polar circle on which the Sun sets just
after midnight and rises again a few hours later is handled. With
neither event, the day is all daylight or none, according to
whether the Sun is up.
duration_of_daylight/2 is the same calculation returning a
Duration.t/0, which can represent a full 24 hours of daylight.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first.dateis anyDate.t/0in the Gregorian calendar (for example,Calendar.ISO).
Returns
{:ok, time}wheretimeis aTime.t(). The maximum value is~T[23:59:59], representing 24 hours of daylight (aTime.t()cannot hold24:00:00).{:error, reason}for an invalid location or date, or if the time zone for the location cannot be resolved, as forsunrise/3.
Accuracy
It is taken from sunrise/3 and sunset/3, each within 1.6 s of
Skyfield for the same definition of the horizon, and is rounded
down to the second. Refraction in the real atmosphere moves both
events, by different amounts.
Examples
iex> Astro.hours_of_daylight({151.20666584, -33.8559799094}, ~D[2019-12-10])
{:ok, ~T[14:20:50]}
# No sunset in summer
iex> Astro.hours_of_daylight({-62.3481, 82.5018}, ~D[2019-06-07])
{:ok, ~T[23:59:59]}
# No sunrise in winter
iex> Astro.hours_of_daylight({-62.3481, 82.5018}, ~D[2019-12-07])
{:ok, ~T[00:00:00]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting).
Daytime, Midnight sun and Polar night, Wikipedia.
Returns the fraction of the Moon's disc that is illuminated at a date or date time.
The illuminated fraction k is the part of the Moon's apparent disc lit by the Sun, as seen from the centre of the Earth: 0 at new moon and 1 at full moon. It depends on the phase angle i, the angle at the Moon between the directions to the Sun and to the Earth:
k = (1 + cos i) / 2The phase angle is found from the Sun's and the Moon's apparent
positions (see sun_position_at/1 and moon_position_at/1), as in
Meeus, Astronomical Algorithms, chapter 48: their elongation ψ
from
cos ψ = sin δ₀ sin δ + cos δ₀ cos δ cos(α₀ − α)and then
tan i = R sin ψ / (Δ − R cos ψ)where α₀ and δ₀ are the Sun's right ascension and declination, α and δ the Moon's, R the Sun's distance and Δ the Moon's.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0. A date is taken at 00:00 UTC.
Returns
- a
floatvalue between0.0and1.0representing the fractional illumination of the moon.
Accuracy
For Meeus's example 48.a (1992 April 12, 0h TD) the result is 0.67856, against Meeus's 0.6786. Outside the ephemeris the series agree with JPL over 1900–2100 to 0.0000045 RMS and 0.000018 at most. The accuracy guide gives the details.
Examples
iex> fraction = Astro.illuminated_fraction_of_moon_at(~D[2017-03-16])
iex> Float.round(fraction, 4)
0.8884
iex> fraction = Astro.illuminated_fraction_of_moon_at(~D[1992-04-12])
iex> Float.round(fraction, 4)
0.6786Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 48 (illuminated fraction of the Moon's disk).
Phase angle and Lunar phase, Wikipedia.
Guards that a value is a lunar phase angle.
Arguments
phaseis the value to test.
Returns
trueifphaseis a number from 0.0 to 360.0 inclusive, otherwisefalse.
Examples
iex> require Astro
iex> Astro.is_lunar_phase(90.0)
true
iex> Astro.is_lunar_phase(400.0)
false
@spec lunar_eclipse_at(location(), Astro.Lunar.Eclipse.t() | date(), options()) :: {:ok, Astro.Lunar.Eclipse.Local.t()} | {:error, :not_visible | :invalid_location | :invalid_date | :year_out_of_range | :invalid_shadow | :invalid_options | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Returns the local circumstances of a lunar eclipse at a location: the eclipse's contacts and greatest eclipse, and the Moon's altitude at each.
A lunar eclipse is the Moon passing through the Earth's shadow, so
its contacts happen at the same instants for everyone who can see
the Moon; what depends on the place is whether the Moon is above the
horizon while it happens. The Moon's altitude is topocentric,
allowing for its parallax of up to about 1° from the observer's
position on the Earth, and geometric, without refraction.
Astro.Lunar.Eclipse.Local describes the calculation.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries a height in metres above the ellipsoid. Longitude comes first.eclipseis anAstro.Lunar.Eclipse.t/0, aslunar_eclipse_at_or_after/2returns it, or aDate.t/0orDateTime.t/0, or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, for the first lunar eclipse whose greatest eclipse is at or after it.optionsis a keyword list of options.
Options
:shadowis the convention for enlarging the Earth's shadow for its atmosphere,:danjon(the default) or:chauvenet, as forlunar_eclipse_at_or_after/2. An eclipse given aseclipseis found again with this convention.:time_zoneis the time zone of the returned date times::default, the time zone of the location, which is the default;:utc; or a time zone name.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, local}, anAstro.Lunar.Eclipse.Local.t/0with the fields ofAstro.Lunar.Eclipse.t/0, its instants as date times in the requested time zone, and the Moon's altitude in degrees at greatest eclipse and at each contact the eclipse has. A contact the Moon is below the horizon for has a negative altitude.{:error, :not_visible}if the Moon is below the horizon from the first penumbral contact to the last. The Moon has risen when its upper limb is above the horizon, allowing for standard refraction, as formoonrise/3.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifeclipseis neither an eclipse nor a valid date or date time in its calendar.{:error, :year_out_of_range}ifeclipseis a date more than 50,000 years from J2000.{:error, :invalid_shadow}if:shadowis not:danjonor:chauvenet.{:error, :invalid_options}ifoptionsis not a keyword list.The time zone errors of
sunrise/3.
Accuracy
The instants are those of lunar_eclipse_at_or_after/2. The Moon's
altitude at the instants of moonrise/3 and moonset/3 is the
horizon they use to within 0.00001°, and it is as accurate as the
Moon's position otherwise. Refraction near the horizon departs from
the standard 34′ with the weather, so whether a Moon that rises or
sets during an eclipse is seen at a contact within a few minutes of
it is uncertain by that much.
Examples
iex> {:ok, local} = Astro.lunar_eclipse_at({-96.80, 32.78}, ~D[2025-03-01], time_zone: "America/Chicago")
iex> {local.type, Float.round(local.moon_altitudes.greatest, 1)}
{:total, 59.0}
iex> DateTime.truncate(local.greatest, :second)
#DateTime<2025-03-14 01:58:46-05:00 CDT America/Chicago>
iex> Astro.lunar_eclipse_at({77.21, 28.61}, ~D[2025-03-01], time_zone: :utc)
{:error, :not_visible}Reference
Fred Espenak and Jean Meeus, Five Millennium Canon of Lunar Eclipses: −1999 to +3000, NASA/TP-2009-214172 (2009).
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 40 (correction for parallax).
@spec lunar_eclipse_at_or_after(date(), Keyword.t()) :: {:ok, Astro.Lunar.Eclipse.t()} | {:error, :invalid_date | :year_out_of_range | :invalid_shadow | :invalid_options}
Returns the first lunar eclipse whose greatest eclipse is at or after a date or date time.
An eclipse is found at a full moon near a node of the Moon's orbit.
At each full moon the Moon's angular distance from the axis of the
Earth's shadow, the point opposite the Sun, is compared with the
radius of the penumbra; where it can reach it, greatest eclipse is
the moment of least distance, found by golden section search, and
each contact the moment the Moon's limb meets a shadow's edge, found
by bisection. Astro.Lunar.Eclipse describes the geometry. The
positions come from the JPL DE440s ephemeris where the loaded kernel
covers the date, and from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, within 50,000 years of J2000.optionsis a keyword list of options.
Options
:shadowis the convention for enlarging the Earth's shadow for its atmosphere::danjon(the default, as NASA's eclipse catalogues use it) or:chauvenet(the older almanac convention, which makes the umbral magnitude about 0.006 larger).
Returns
{:ok, eclipse}, anAstro.Lunar.Eclipse.t/0with the type (:penumbral,:partialor:total), gamma, the penumbral and umbral magnitudes, greatest eclipse and the contacts as UTCDateTime.t/0s,nilfor the contacts the eclipse does not have, and its Saros series as NASA numbers it (Astro.Saros).{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000.{:error, :invalid_shadow}if:shadowis not:danjonor:chauvenet.{:error, :invalid_options}ifoptionsis not a keyword list.
Accuracy
Against NASA's catalogue for 1901–2100, compared in Terrestrial Time so that ΔT does not enter, greatest eclipse is within 3 s (0.5 s on average), gamma within 0.00015, both magnitudes within 0.0004, the partial and total phases' durations within 0.1 minute, and every eclipse's type is the same. Outside the ephemeris the series place greatest eclipse within 15 s of NASA's in the nineteenth century, 40 s in the thirtieth and two and a half minutes around 950 BCE, with magnitudes within 0.002 and the same types. In Universal Time every instant also carries ΔT's uncertainty; the accuracy guide gives it.
Examples
iex> {:ok, eclipse} = Astro.lunar_eclipse_at_or_after(~D[2025-03-01])
iex> {eclipse.type, DateTime.truncate(eclipse.greatest, :second)}
{:total, ~U[2025-03-14 06:58:46Z]}
iex> {:ok, eclipse} = Astro.lunar_eclipse_at_or_after(~D[2025-03-15])
iex> {eclipse.type, DateTime.to_date(eclipse.greatest)}
{:total, ~D[2025-09-07]}Reference
Fred Espenak and Jean Meeus, Five Millennium Canon of Lunar Eclipses: −1999 to +3000, NASA/TP-2009-214172 (2009), and the Five Millennium Catalog of Lunar Eclipses.
Enlargement of Earth's Shadows, NASA, on Danjon's and Chauvenet's conventions.
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 54 (eclipses).
@spec lunar_eclipse_before(date(), Keyword.t()) :: {:ok, Astro.Lunar.Eclipse.t()} | {:error, :invalid_date | :year_out_of_range | :invalid_shadow | :invalid_options}
Returns the last lunar eclipse whose greatest eclipse is before a date or date time.
An eclipse is found at a full moon near a node of the Moon's orbit.
At each full moon the Moon's angular distance from the axis of the
Earth's shadow, the point opposite the Sun, is compared with the
radius of the penumbra; where it can reach it, greatest eclipse is
the moment of least distance, found by golden section search, and
each contact the moment the Moon's limb meets a shadow's edge, found
by bisection. Astro.Lunar.Eclipse describes the geometry. The
positions come from the JPL DE440s ephemeris where the loaded kernel
covers the date, and from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, within 50,000 years of J2000.optionsis a keyword list of options.
Options
:shadowis the convention for enlarging the Earth's shadow for its atmosphere::danjon(the default, as NASA's eclipse catalogues use it) or:chauvenet(the older almanac convention, which makes the umbral magnitude about 0.006 larger).
Returns
{:ok, eclipse}, anAstro.Lunar.Eclipse.t/0with the type (:penumbral,:partialor:total), gamma, the penumbral and umbral magnitudes, greatest eclipse and the contacts as UTCDateTime.t/0s,nilfor the contacts the eclipse does not have, and its Saros series as NASA numbers it (Astro.Saros).{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000.{:error, :invalid_shadow}if:shadowis not:danjonor:chauvenet.{:error, :invalid_options}ifoptionsis not a keyword list.
Accuracy
Against NASA's catalogue for 1901–2100, compared in Terrestrial Time so that ΔT does not enter, greatest eclipse is within 3 s (0.5 s on average), gamma within 0.00015, both magnitudes within 0.0004, the partial and total phases' durations within 0.1 minute, and every eclipse's type is the same. Outside the ephemeris the series place greatest eclipse within 15 s of NASA's in the nineteenth century, 40 s in the thirtieth and two and a half minutes around 950 BCE, with magnitudes within 0.002 and the same types. In Universal Time every instant also carries ΔT's uncertainty; the accuracy guide gives it.
Examples
iex> {:ok, eclipse} = Astro.lunar_eclipse_before(~D[2025-03-14])
iex> {eclipse.type, DateTime.to_date(eclipse.greatest)}
{:partial, ~D[2024-09-18]}Reference
Fred Espenak and Jean Meeus, Five Millennium Canon of Lunar Eclipses: −1999 to +3000, NASA/TP-2009-214172 (2009), and the Five Millennium Catalog of Lunar Eclipses.
Enlargement of Earth's Shadows, NASA, on Danjon's and Chauvenet's conventions.
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 54 (eclipses).
Returns the lunar phase in degrees at a date or date time.
The lunar phase is the Moon's apparent ecliptic longitude less the
Sun's, from 0° to 360°: 0° at new moon, 90° at first quarter, 180°
at full moon and 270° at last quarter. It is how far the Moon has
moved ahead of the Sun around the sky since the last new moon, and
grows by about 12.2° a day. It differs from the phase angle that
sets the illuminated fraction (see illuminated_fraction_of_moon_at/1),
because the Moon's orbit is tilted about 5° to the ecliptic.
Both longitudes are apparent ecliptic longitudes of the date (see
sun_apparent_longitude/1 and moon_position_at/1): from the JPL
DE440s ephemeris where the loaded kernel covers the moment, and
from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0. A date is taken at 00:00 UTC.
Returns
- the lunar phase in degrees, from 0 to 360.
Accuracy
Outside the ephemeris the series agree with JPL over 1900–2100 to 2.8″ RMS and 11.5″ at most. The phase grows about 30″ a minute, so 2.8″ is about 6 s in the time a phase is reached. The accuracy guide gives the details.
Examples
iex> Astro.lunar_phase_at ~U[2021-08-22 12:02:02.816534Z]
180.00068383330358
iex> Astro.lunar_phase_at(~U[2021-07-10 01:16:37.026816Z])
359.9999651540469Outside the loaded ephemeris the phase comes from the analytical series:
iex> Astro.lunar_phase_at(~D[1000-01-01]) |> Float.round(2)
202.55Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 47 (position of the Moon) and chapter 49 (phases of the Moon).
Lunar phase, Wikipedia.
Returns the moon phase as a UTF8 binary representing an emoji of the moon phase.
Unicode has eight moon phase symbols, U+1F311 to U+1F318, from 🌑 new moon through 🌓 first quarter, 🌕 full moon and 🌗 last quarter. Each covers 45° of lunar phase centred on its own, so 🌑 is returned from 337.5° to 22.5° and 🌒 from 22.5° to 67.5°. The symbols show the Moon as it is seen from the northern hemisphere, lit on the right while it waxes.
Arguments
phaseis a moon phase between0.0and360.0, as returned bylunar_phase_at/1.
Returns
- A single grapheme string, one of the Unicode moon phase symbols.
Examples
iex> Astro.lunar_phase_emoji 0
"🌑"
iex> Astro.lunar_phase_emoji 45
"🌒"
iex> Astro.lunar_phase_emoji 90
"🌓"
iex> Astro.lunar_phase_emoji 135
"🌔"
iex> Astro.lunar_phase_emoji 180
"🌕"
iex> Astro.lunar_phase_emoji 245
"🌖"
iex> Astro.lunar_phase_emoji 270
"🌗"
iex> Astro.lunar_phase_emoji 320
"🌘"
iex> Astro.lunar_phase_emoji 360
"🌑"
iex> ~U[2021-08-22 12:02:02.816534Z]
...> |> Astro.lunar_phase_at()
...> |> Astro.lunar_phase_emoji()
"🌕"Reference
- The Unicode Consortium, Miscellaneous Symbols and Pictographs, the chart that holds the moon phase symbols.
@spec moon_position_at(date()) :: Geo.PointZ.t()
Returns the Moon's apparent right ascension, declination and distance at a date or date time.
This is the Moon's apparent geocentric position: where it is seen
from the centre of the Earth, referred to the true equator and
equinox of the date. The Moon is close enough that an observer on
the Earth's surface sees it displaced by up to about 1° from this
position (its parallax); moonrise/3, moonset/3 and the Odeh
crescent criterion correct for that.
Where the loaded JPL DE440s ephemeris covers the moment, the Earth-to-Moon vector is read from it 1.282 s earlier, the light time, and rotated from the ICRF to the true equator of date by IAU 1976 precession and IAU 1980 nutation. No annual aberration is applied, because the Moon travels around the Sun with the Earth. Elsewhere the Moon's ecliptic longitude, latitude and distance come from the periodic terms of Meeus, Astronomical Algorithms, chapter 47, abridged from the ELP-2000/82 lunar theory, with nutation in longitude added, and are converted to right ascension and declination with the true obliquity of the ecliptic (chapter 13).
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0. A date is taken at 00:00 UTC.
Returns
- a
Geo.PointZ.t/0struct with coordinates{right_ascension, declination, distance}with properties%{reference: :celestial, object: :moon}. Right ascension is in degrees from −180° to 180°, declination in degrees anddistancein metres.
Accuracy
Inside the ephemeris, for Meeus's example 47.a (1992 April 12, 0h TD) the result is within 2.1″ in right ascension, 0.3″ in declination and 4 km of Meeus's own series, about the accuracy of that series. Outside it the series agree with JPL over 1900–2100 to 2.7″ RMS in right ascension and 1.3″ in declination (10.1″ and 6.5″ at most), and to 2.9 km in distance. The Moon moves about 0.55″ a second, so far from the present the uncertainty of ΔT dominates: the 265 s uncertainty at the year 0 is about 2.4′ of position. The accuracy guide gives the details.
Examples
iex> Astro.moon_position_at(~D[1992-04-12]) |> Astro.Location.round(6)
%Geo.PointZ{
coordinates: {134.697251, 13.765115, 368404879.697206},
properties: %{object: :moon, reference: :celestial},
srid: nil
}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 47 (position of the Moon).
Ryan S. Park and others, The JPL Planetary and Lunar Ephemerides DE440 and DE441, The Astronomical Journal 161 (2021), 105.
Ephemeride Lunaire Parisienne and Orbit of the Moon, Wikipedia.
@spec moonrise(location(), date(), options()) :: {:ok, DateTime.t()} | {:error, :no_time | :invalid_location | :invalid_date | :invalid_options | :invalid_utc_offset | :invalid_limb | :invalid_interpolation | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Returns the moonrise for a given location and date.
Moonrise is the moment the upper limb of the Moon appears above the horizon.
Refraction lifts the Moon by a standard 34′ at the horizon, and its semi-diameter is 14.7′ to 16.8′ depending on its distance, so this is the moment the Moon's centre, computed without refraction, is 34′ plus its semi-diameter below the horizon.
The Moon is close enough that where it appears depends on where it is seen from: from the Earth's surface it is displaced by up to about 1°, its horizontal parallax, from where it is seen from the Earth's centre. Its altitude is therefore computed from its topocentric position, corrected for parallax in both right ascension and declination (Meeus, Astronomical Algorithms, chapter 40), and the local apparent sidereal time. The altitude is sampled every 24 minutes across the day to bracket the crossing, which is then narrowed by bisection to 0.01 s. The position comes from the JPL DE440s ephemeris where the loaded kernel covers the date, and from the analytical series elsewhere.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first. An elevation enters the parallax but does not lower the horizon.dateis aDate.t/0,NaiveDateTime.t/0orDateTime.t/0for the day of the moonrise.optionsis a keyword list of options.
Options
:limbis the part of the Moon's disk that defines the event.:upper, the default, puts the upper limb on the apparent horizon, the USNO standard. The event threshold is−(34′ refraction + semi-diameter).:centerputs the centre of the disk on the apparent horizon. The event threshold is−34′of refraction only.
:interpolationis how the Moon's position is evaluated while the event is bisected.:direct, the default, evaluates the JPL ephemeris at every step.:lagrangeinterpolates the geocentric position quadratically from three points, as Meeus Ch. 15 does.
:time_zoneis the time zone of the returned date time::default, the time zone of the location, which is the default;:utc; or a time zone name.:utc_offsetis a whole number of seconds from UTC, from −50,400 to 50,400. When it is given, the event is the one whose date at that offset isdate, whatever time zone it is returned in: the location's own day, for a calendar that keeps local mean time. Otherwise the date is taken in the requested time zone.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, date_time}wheredate_timeis the moonrise in the requested time zone.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_options}ifoptionsis not a list.{:error, :invalid_utc_offset}if:utc_offsetis not a whole number of seconds from −50,400 to 50,400.{:error, :no_time}if the Moon does not rise on that date at that location, as it can stay below the horizon for a whole day.{:error, :invalid_limb}if:limbis not:upperor:center.{:error, :invalid_interpolation}if:interpolationis not:director:lagrange.{:error, :time_zone_not_found}if the requested time zone is unknown.{:error, :time_zone_not_resolved}if no time zone can be resolved for the location, which happens when:tz_worldis not a dependency and no:time_zone_resolveris given, or when the resolver returns neither{:ok, time_zone}nor{:error, reason}.{:error, :tz_world_data_not_installed}if:tz_worldis a dependency but its data has not been installed. Runmix tz_world.updateto install it.{:error, :tz_world_backend_not_running}if:tz_worldis a dependency but none of its backends is running; seeAstro.Supervisor.{:error, :invalid_time_zone_resolver}if:time_zone_resolveris not a 1-arity function.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :utc_only_time_zone_database}if the moonrise is requested in a time zone other than UTC and no time zone database is configured.
Accuracy
Against Skyfield, using the same ephemeris, results agree to 0.08 s on average and 0.1 s at most. Against the USNO, which publishes to the minute, they agree to 15.5 s on average and 29 s at most in 70 cases. Outside the ephemeris the series move the result by 0.22 s RMS and 0.93 s at most. As for the Sun, the atmosphere matters more than any of these: refraction at the horizon departs from the standard 34′ with temperature and pressure, and a high observer sees a lower horizon, and neither is modelled. The accuracy guide gives the details.
Examples
iex> {:ok, date_time} = Astro.moonrise({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> date_time
#DateTime<2019-12-04 12:20:54.388717+11:00 AEDT Australia/Sydney>
iex> Astro.moonrise({-62.3481, 82.5018}, ~D[2024-01-01])
{:error, :no_time}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting) and chapter 40 (correction for parallax).
United States Naval Observatory, Rise, Set, and Twilight Definitions.
Moonrise and moonset and Lunar parallax, Wikipedia.
@spec moonset(location(), date(), options()) :: {:ok, DateTime.t()} | {:error, :no_time | :invalid_location | :invalid_date | :invalid_options | :invalid_utc_offset | :invalid_limb | :invalid_interpolation | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Returns the moonset for a given location and date.
Moonset is the moment the upper limb of the Moon disappears below the horizon.
Refraction lifts the Moon by a standard 34′ at the horizon, and its semi-diameter is 14.7′ to 16.8′ depending on its distance, so this is the moment the Moon's centre, computed without refraction, is 34′ plus its semi-diameter below the horizon.
The Moon is close enough that where it appears depends on where it is seen from: from the Earth's surface it is displaced by up to about 1°, its horizontal parallax, from where it is seen from the Earth's centre. Its altitude is therefore computed from its topocentric position, corrected for parallax in both right ascension and declination (Meeus, Astronomical Algorithms, chapter 40), and the local apparent sidereal time. The altitude is sampled every 24 minutes across the day to bracket the crossing, which is then narrowed by bisection to 0.01 s. The position comes from the JPL DE440s ephemeris where the loaded kernel covers the date, and from the analytical series elsewhere.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first. An elevation enters the parallax but does not lower the horizon.dateis aDate.t/0,NaiveDateTime.t/0orDateTime.t/0for the day of the moonset.optionsis a keyword list of options.
Options
:limbis the part of the Moon's disk that defines the event.:upper, the default, puts the upper limb on the apparent horizon, the USNO standard. The event threshold is−(34′ refraction + semi-diameter).:centerputs the centre of the disk on the apparent horizon. The event threshold is−34′of refraction only.
:interpolationis how the Moon's position is evaluated while the event is bisected.:direct, the default, evaluates the JPL ephemeris at every step.:lagrangeinterpolates the geocentric position quadratically from three points, as Meeus Ch. 15 does.
:time_zoneis the time zone of the returned date time::default, the time zone of the location, which is the default;:utc; or a time zone name.:utc_offsetis a whole number of seconds from UTC, from −50,400 to 50,400. When it is given, the event is the one whose date at that offset isdate, whatever time zone it is returned in: the location's own day, for a calendar that keeps local mean time. Otherwise the date is taken in the requested time zone.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, date_time}wheredate_timeis the moonset in the requested time zone.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_options}ifoptionsis not a list.{:error, :invalid_utc_offset}if:utc_offsetis not a whole number of seconds from −50,400 to 50,400.{:error, :no_time}if the Moon does not set on that date at that location, as it can stay above the horizon for a whole day.{:error, :invalid_limb}if:limbis not:upperor:center.{:error, :invalid_interpolation}if:interpolationis not:director:lagrange.{:error, :time_zone_not_found}if the requested time zone is unknown.{:error, :time_zone_not_resolved}if no time zone can be resolved for the location, which happens when:tz_worldis not a dependency and no:time_zone_resolveris given, or when the resolver returns neither{:ok, time_zone}nor{:error, reason}.{:error, :tz_world_data_not_installed}if:tz_worldis a dependency but its data has not been installed. Runmix tz_world.updateto install it.{:error, :tz_world_backend_not_running}if:tz_worldis a dependency but none of its backends is running; seeAstro.Supervisor.{:error, :invalid_time_zone_resolver}if:time_zone_resolveris not a 1-arity function.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :utc_only_time_zone_database}if the moonset is requested in a time zone other than UTC and no time zone database is configured.
Accuracy
Against Skyfield, using the same ephemeris, results agree to 0.08 s on average and 0.1 s at most. Against the USNO, which publishes to the minute, they agree to 15.5 s on average and 29 s at most in 70 cases. Outside the ephemeris the series move the result by 0.22 s RMS and 0.93 s at most. As for the Sun, the atmosphere matters more than any of these: refraction at the horizon departs from the standard 34′ with temperature and pressure, and a high observer sees a lower horizon, and neither is modelled. The accuracy guide gives the details.
Examples
iex> {:ok, date_time} = Astro.moonset({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> date_time
#DateTime<2019-12-04 01:13:25.822599+11:00 AEDT Australia/Sydney>
iex> Astro.moonset({-62.3481, 82.5018}, ~D[2024-01-01])
{:error, :no_time}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting) and chapter 40 (correction for parallax).
United States Naval Observatory, Rise, Set, and Twilight Definitions.
Moonrise and moonset and Lunar parallax, Wikipedia.
@spec new_visible_crescent(location(), date(), method()) :: {:ok, Astro.Lunar.CrescentVisibility.visibility()} | {:error, :no_sunset | :invalid_location | :invalid_date | :invalid_method}
Predicts the visibility of the new crescent moon at a given location on a given date using one of three published criteria.
A new crescent is first seen in the west after sunset, a day or two
after new moon. Whether it can be seen depends on how far the Moon
is from the Sun, how wide its lit crescent is, and how dark the sky
has become. Each criterion predicts this from the Sun's and the
Moon's positions on the evening of date, and classifies the
crescent into one of five categories.
Yallop and Odeh both work at the best time to look, sunset plus four-ninths of the interval from sunset to moonset. There they find the arc of vision ARCV, the Moon's altitude less the Sun's, and the width of the crescent W in arcminutes, and compare ARCV with a cubic in W fitted to recorded sightings:
q = (ARCV − (11.8371 − 6.3226 W + 0.7319 W² − 0.1018 W³)) / 10
V = ARCV − (7.1651 − 6.3226 W + 0.7319 W² − 0.1018 W³)Yallop's q uses the geocentric arc of vision and Odeh's V the topocentric one; Odeh also treats a Moon less than 6.4° from the Sun as invisible, the Danjon limit. The class follows from thresholds on q or V. Schaefer instead models the eye: from sunset to moonset it compares the brightness of the crescent against the twilight sky with the smallest contrast the eye can detect.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first.dateis aDate.t/0orDateTime.t/0indicating the evening on which crescent visibility is to be evaluated.methodselects the prediction criterion. Default:odeh.:odeh— Odeh (2004). Empirical criterion based on 737 observations. Uses topocentric ARCV with a Danjon limit of 6.4°. The most widely used modern criterion.:yallop— Yallop (1997). Empirical criterion based on 295 observations. Uses geocentric ARCV. The original single-parameter approach that Odeh later refined.:schaefer— Schaefer (1988/2000). Physics-based model computing the contrast between crescent brightness and twilight sky brightness against the human contrast detection threshold. The best observation time is found by scanning from sunset to moonset.
Options
When method is :schaefer, the following options are accepted as
an optional fourth argument (a keyword list):
:extinction— V-band zenith extinction coefficient. Default0.172(clean sea-level site). Typical values:0.12(high mountain),0.17(sea level),0.25(hazy conditions).
Returns
{:ok, visibility}wherevisibilityis one of::A— Visible to the naked eye.:B— Visible with optical aid.:C— May need optical aid.:D— Not visible with optical aid.:E— Not visible.
{:error, :no_sunset}if no sunset occurs on the given date at the given location (e.g. polar day).{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_method}ifmethodis not:yallop,:odehor:schaefer.
Method comparison
| Aspect | Yallop (1997) | Odeh (2004) | Schaefer (1988/2000) |
|---|---|---|---|
| Basis | Empirical polynomial | Empirical polynomial | Physical model |
| Observations | 295 | 737 | N/A (theory) |
| ARCV type | Geocentric | Topocentric | N/A |
| Best time | Sunset + 4/9 lag | Sunset + 4/9 lag | Scanned (max Rs) |
| Atmosphere | Not modelled | Not modelled | Extinction coefficient |
Accuracy
The categories depend on the Sun's and the Moon's positions far less than on the criteria: outside the ephemeris the series give the same category as JPL in 3,749 of 3,750 evenings. The criteria are fits to sightings made under real skies, and a crescent near the boundary of two categories is as likely to be decided by the evening's weather as by the calculation; Astro's predictions have not themselves been compared with a record of sightings. Far from the present, ΔT moves the Moon against the Sun by about 0.5″ for each second it is wrong, so the 265 s uncertainty at the year 0 changes ARCV by about 2′. The accuracy guide gives the details.
Examples
iex> location = {-0.1275, 51.5072}
iex> Astro.new_visible_crescent(location, ~D[2025-03-31])
{:ok, :A}
iex> location = {-0.1275, 51.5072}
iex> Astro.new_visible_crescent(location, ~D[2025-03-31], :yallop)
{:ok, :A}Reference
Bernard D. Yallop, A Method for Predicting the First Sighting of the New Crescent Moon, NAO Technical Note 69 (1997).
Mohammad Sh. Odeh, New Criterion for Lunar Crescent Visibility, Experimental Astronomy 18 (2004), 39–64.
Bradley E. Schaefer, Astronomy and the Limits of Vision, Vistas in Astronomy 36 (1993), 311–361.
@spec new_visible_crescent(location(), date(), method(), keyword()) :: {:ok, Astro.Lunar.CrescentVisibility.visibility()} | {:error, :no_sunset | :invalid_location | :invalid_date | :invalid_method | :invalid_options | :invalid_utc_offset}
Predicts the visibility of the new crescent moon, with options.
This is new_visible_crescent/3 with options: the evening of a
location's own day, and for Schaefer's method the clarity of the
atmosphere. It takes the same location, date and method and returns
the same classes; see new_visible_crescent/3 for how the methods
compare.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first.dateis aDate.t/0orDateTime.t/0indicating the evening on which crescent visibility is to be evaluated.methodis:odeh,:yallopor:schaefer.optionsis a keyword list of options.
Options
:utc_offsetis a whole number of seconds from UTC, from −50,400 to 50,400. When it is given, the evening is that ofdateat the offset, as forsunset/3; otherwise it is the evening of the UTC date.:extinctionis the V-band zenith extinction coefficient, for:schaefer. The default is0.172, a clean sea-level site. Typical values are0.12on a high mountain,0.17at sea level and0.25in hazy conditions.
Returns
{:ok, visibility}wherevisibilityis one of the classes:Ato:Edescribed innew_visible_crescent/3.{:error, :no_sunset}if no sunset occurs on the given date at the given location.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_method}ifmethodis not:yallop,:odehor:schaefer.{:error, :invalid_options}ifoptionsis not a list.{:error, :invalid_utc_offset}if:utc_offsetis not a whole number of seconds from −50,400 to 50,400.
Examples
iex> location = {39.8579, 21.3891}
iex> Astro.new_visible_crescent(location, ~D[2025-03-31], :schaefer, extinction: 0.25)
{:ok, :A}Reference
- Bradley E. Schaefer, Astronomy and the Limits of Vision, Vistas in Astronomy 36 (1993), 311–361.
@spec solar_eclipse_at(location(), Astro.Solar.Eclipse.t() | date(), options()) :: {:ok, Astro.Solar.Eclipse.Local.t()} | {:error, :not_visible | :invalid_location | :invalid_date | :year_out_of_range | :invalid_options | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Returns the local circumstances of a solar eclipse at a location: when it begins and ends there, its maximum, how much of the Sun is covered, and the Sun's altitude at each contact.
The observer is placed on the fundamental plane of the eclipse's
Besselian elements, from the observer's geodetic latitude, longitude
and height. Maximum eclipse is the moment the observer is nearest the
shadow axis, found by golden section search; the contacts are the
moments the observer enters and leaves the penumbra and, for a total
or annular eclipse there, the umbra or antumbra, found by bisection.
Astro.Solar.Eclipse.Local describes the geometry.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries a height in metres above the ellipsoid. Longitude comes first.eclipseis anAstro.Solar.Eclipse.t/0, assolar_eclipse_at_or_after/1returns it, or aDate.t/0orDateTime.t/0, or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, for the first solar eclipse whose greatest eclipse is at or after it.optionsis a keyword list of options.
Options
:time_zoneis the time zone of the returned date times::default, the time zone of the location, which is the default;:utc; or a time zone name.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, local}, anAstro.Solar.Eclipse.Local.t/0with the type there (:partial,:annularor:total), the contacts:partial_begins,:annular_beginsor:total_begins,:maximum,:total_endsor:annular_endsand:partial_endsas date times in the requested time zone (nilfor those the eclipse does not have there), the magnitude, the obscuration, and the Sun's geometric altitude in degrees at each contact. A contact the Sun is below the horizon for has a negative altitude.{:error, :not_visible}if the eclipse is not seen from the location: the location is outside the Moon's penumbra throughout, or the Sun is below the horizon from first contact to last.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifeclipseis neither an eclipse nor a valid date or date time in its calendar.{:error, :year_out_of_range}ifeclipseis a date more than 50,000 years from J2000.{:error, :invalid_options}ifoptionsis not a keyword list.The time zone errors of
sunrise/3.
Accuracy
Against the USNO's eclipse computer for 17 eclipses and places from
1999 to 2026, with the USNO's ΔT, the contacts agree within 2 s,
except where the observer is near the edge of an annular path and
the contacts of annularity change quickly with position (4 s at
Singapore in 2019). The partial contacts agree within 1.1 s. Totality
is about 2 s shorter, and annularity about 2 s longer, than the
USNO's, which takes a larger radius for the Moon than NASA's
k2 = 0.272281 used here. The magnitude agrees within 0.0005, the
obscuration within 0.0012, the Sun's altitudes within 0.05°, and the
type and visibility are the same. The USNO's maximum is 1 s earlier
than the midpoint of its own second and third contacts, where
Astro's is. In Universal Time every instant also carries ΔT's
uncertainty, which moves the shadow across the Earth: an error of a
second in ΔT moves every contact by about a second.
Examples
iex> dallas = {-96.80, 32.78}
iex> {:ok, local} = Astro.solar_eclipse_at(dallas, ~D[2024-04-01], time_zone: :utc)
iex> {local.type, DateTime.truncate(local.total_begins, :second)}
{:total, ~U[2024-04-08 18:40:43Z]}
iex> {Float.round(local.magnitude, 3), Float.round(local.sun_altitudes.maximum, 1)}
{1.015, 64.6}
iex> {:ok, eclipse} = Astro.solar_eclipse_at_or_after(~D[2024-04-01])
iex> {:ok, local} = Astro.solar_eclipse_at({-74.01, 40.71}, eclipse, time_zone: "America/New_York")
iex> {local.type, Float.round(local.obscuration, 3)}
{:partial, 0.899}
iex> DateTime.truncate(local.maximum, :second)
#DateTime<2024-04-08 15:25:35-04:00 EDT America/New_York>
iex> Astro.solar_eclipse_at({-0.13, 51.50}, ~D[2024-04-01], time_zone: :utc)
{:error, :not_visible}Reference
P. Kenneth Seidelmann (editor), Explanatory Supplement to the Astronomical Almanac (1992), chapter 8 (eclipses of the Sun and Moon).
Jean Meeus, Elements of Solar Eclipses 1951–2200 (1989), on local circumstances from Besselian elements.
United States Naval Observatory, Solar Eclipse Computer.
@spec solar_eclipse_at_or_after(date()) :: {:ok, Astro.Solar.Eclipse.t()} | {:error, :invalid_date | :year_out_of_range}
Returns the first solar eclipse whose greatest eclipse is at or after a date or date time.
An eclipse is found at a new moon near a node of the Moon's orbit. At
each new moon the Besselian elements of the Moon's shadow, computed
from the Sun's and the Moon's apparent positions, give the distance
of the shadow axis from the centre of the Earth; where the shadow can
reach the Earth, greatest eclipse is the moment of least distance,
found by golden section search. Where the axis meets the Earth the
eclipse is central, total or annular by the umbra at the surface, and
hybrid if a total eclipse's umbra falls short of the surface at
either end of the central line; otherwise the eclipse is partial, or
total or annular without a central line where the umbra still
reaches the limb. Astro.Solar.Eclipse describes the geometry. The
positions come from the JPL DE440s ephemeris where the loaded kernel
covers the date, and from the analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, within 50,000 years of J2000.
Returns
{:ok, eclipse}, anAstro.Solar.Eclipse.t/0with the type (:partial,:annular,:totalor:hybrid), greatest eclipse as a UTCDateTime.t/0, gamma and the magnitude: for a central eclipse the ratio of the Moon's apparent diameter to the Sun's at greatest eclipse, and otherwise the fraction of the Sun's diameter covered there; and its Saros series as NASA numbers it (Astro.Saros).{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000.
Accuracy
Against NASA's catalogue for 1901–2100, compared in Terrestrial Time so that ΔT does not enter, greatest eclipse is within 0.6 s, gamma within 0.0001, the magnitude within 0.0001, and every eclipse's type is the same, hybrids included. Outside the ephemeris the series place greatest eclipse within 9 s of NASA's in the nineteenth century, 35 s in the thirtieth and two and a quarter minutes around 950 BCE, with gamma within 0.001, magnitudes within 0.0015 and the same types. In Universal Time every instant also carries ΔT's uncertainty; the accuracy guide gives it.
Examples
iex> {:ok, eclipse} = Astro.solar_eclipse_at_or_after(~D[2024-04-01])
iex> {eclipse.type, DateTime.truncate(eclipse.greatest, :second)}
{:total, ~U[2024-04-08 18:17:20Z]}
iex> {Float.round(eclipse.gamma, 4), Float.round(eclipse.magnitude, 4)}
{0.3431, 1.0565}
iex> {:ok, eclipse} = Astro.solar_eclipse_at_or_after(~D[2024-04-09])
iex> {eclipse.type, DateTime.to_date(eclipse.greatest)}
{:annular, ~D[2024-10-02]}Reference
Fred Espenak and Jean Meeus, Five Millennium Canon of Solar Eclipses: −1999 to +3000, NASA/TP-2006-214141 (2006), and the Five Millennium Catalog of Solar Eclipses.
P. Kenneth Seidelmann (editor), Explanatory Supplement to the Astronomical Almanac (1992), chapter 8 (eclipses of the Sun and Moon).
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 54 (eclipses).
@spec solar_eclipse_before(date()) :: {:ok, Astro.Solar.Eclipse.t()} | {:error, :invalid_date | :year_out_of_range}
Returns the last solar eclipse whose greatest eclipse is before a date or date time.
An eclipse is found at a new moon near a node of the Moon's orbit. At
each new moon the Besselian elements of the Moon's shadow, computed
from the Sun's and the Moon's apparent positions, give the distance
of the shadow axis from the centre of the Earth; where the shadow can
reach the Earth, greatest eclipse is the moment of least distance,
found by golden section search. Astro.Solar.Eclipse describes the
geometry and the types. The positions come from the JPL DE440s
ephemeris where the loaded kernel covers the date, and from the
analytical series elsewhere.
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, within 50,000 years of J2000.
Returns
{:ok, eclipse}, anAstro.Solar.Eclipse.t/0with the type (:partial,:annular,:totalor:hybrid), greatest eclipse as a UTCDateTime.t/0, gamma and the magnitude: for a central eclipse the ratio of the Moon's apparent diameter to the Sun's at greatest eclipse, and otherwise the fraction of the Sun's diameter covered there; and its Saros series as NASA numbers it (Astro.Saros).{:error, :invalid_date}ifdate_timeis not a valid date or date time in its calendar.{:error, :year_out_of_range}ifdate_timeis more than 50,000 years from J2000.
Accuracy
As solar_eclipse_at_or_after/1: within 0.6 s of NASA's greatest
eclipse in Terrestrial Time over 1901–2100, gamma and the magnitude
within 0.0001, and every type the same.
Examples
iex> {:ok, eclipse} = Astro.solar_eclipse_before(~D[2024-04-01])
iex> {eclipse.type, DateTime.to_date(eclipse.greatest)}
{:annular, ~D[2023-10-14]}Reference
Fred Espenak and Jean Meeus, Five Millennium Canon of Solar Eclipses: −1999 to +3000, NASA/TP-2006-214141 (2006), and the Five Millennium Catalog of Solar Eclipses.
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 54 (eclipses).
@spec solar_eclipse_path(Astro.Solar.Eclipse.t() | date(), options()) :: {:ok, Astro.Solar.Eclipse.Path.t()} | {:error, :invalid_date | :year_out_of_range | :invalid_options | :invalid_interval}
Returns the path of a solar eclipse: its central line and the northern and southern limits of the band in which it is total or annular, and the bounds of the region in which it is seen as a partial eclipse.
The central line is where the shadow axis meets the Earth, and a
limit is where the edge of the umbra, or of the antumbra for an
annular eclipse, just grazes an observer. Each is computed from the
eclipse's Besselian elements at a sequence of moments while the
shadow is on the Earth. The partial eclipse is seen within the
penumbral limits, where the edge of the penumbra grazes an observer,
and the sunrise and sunset limits, where the eclipse ends just as the
Sun rises or begins just as it sets. Astro.Solar.Eclipse.Path
describes the geometry.
The lines are drawn on a map by joining their points in order, and
the band by joining the northern limit to the southern limit taken
backwards. Where a limit lies beyond the horizon, near the ends of the
path and on the open side of an eclipse whose axis misses the Earth,
its points follow the horizon, the places having their greatest
eclipse at sunrise or sunset, so the two limits begin together where
the shadow first touches the Earth and end together where it leaves.
Every location is a Geo.Point.t/0 with coordinates
{longitude, latitude} in degrees, as Astro takes locations, the
longitude from −180° to 180°, so a line that crosses the antimeridian
jumps from one end of that range to the other.
Arguments
eclipseis anAstro.Solar.Eclipse.t/0, assolar_eclipse_at_or_after/1returns it, or aDate.t/0orDateTime.t/0, or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0, for the first solar eclipse whose greatest eclipse is at or after it.optionsis a keyword list of options.
Options
:intervalis the time between points in seconds, at least 1. The default is 60. Points are at whole multiples of the interval in Universal Time, and at greatest eclipse.
Returns
{:ok, path}, anAstro.Solar.Eclipse.Path.t/0with:The eclipse's
:type.:central_line,:northern_limitand:southern_limit, lists of points in time order. The central line is empty for an eclipse whose shadow axis misses the Earth, and all three for a partial eclipse.:penumbral_northern_limitand:penumbral_southern_limit, lists of points in time order, empty where the limit is never on the Earth with the Sun up.:sunrise_limit,:sunset_limit,:maximum_at_sunriseand:maximum_at_sunset, each a list of lines in the order they begin, each line a list of points in time order: a curve on the horizon can have two branches at once.:first_contactand:last_contact, the points where and when the penumbra first touches the Earth and last leaves it.
Every point has its
:time, a UTCDateTime.t/0to the millisecond, and its:location, aGeo.Point.t/0. A point of the central line also has the eclipse there (:totalor:annular), the path's:widthin kilometres, the:durationof totality or annularity in seconds, and the Sun's geometric altitude in degrees.{:error, :invalid_date}ifeclipseis neither an eclipse nor a valid date or date time in its calendar.{:error, :year_out_of_range}ifeclipseis a date more than 50,000 years from J2000.{:error, :invalid_options}ifoptionsis not a keyword list.{:error, :invalid_interval}if:intervalis not a number of seconds of at least 1.
Accuracy
Against NASA's path tables for eight eclipses from 1999 to 2027 (total, annular, hybrid, one over Antarctica and one whose axis misses the Earth), 674 instants with NASA's ΔT, every point NASA gives is found. Across the path the central line and the limits lie within 0.7 km of NASA's where the Sun is 20° or more above the horizon, and within 2.6 km over Antarctica with the Sun lower, where the ground is tilted to the shadow. Along the path they are within 2 s of the shadow's motion. The differences follow those of the Besselian elements, which come from different ephemerides. The path width agrees within 1 km and 0.6% (a path 400 km wide curves with the Earth across its width, where the two measure it differently), the duration within 0.7 s, and the Sun's altitude within NASA's rounding to a degree. NASA names a hybrid eclipse's limits by the sign of the umbra's radius, so in the total part of its path NASA's northern limit is the southern one here. In Universal Time the path also carries ΔT's uncertainty: a second of ΔT moves it 0.46 km east or west at the equator.
First and last contact agree with NASA's, as EclipseWise publishes
them, within 0.3 s in Terrestrial Time for eight eclipses from 2014
to 2026, total, annular, hybrid and partial. Within each line of the
sunrise and sunset limits and of the curves of maximum eclipse on the
horizon, every point is where solar_eclipse_at/3 puts that contact,
or maximum, within 0.01 s and with the Sun on the horizon within
0.001°; and a point 0.05° inside a penumbral limit sees a partial
eclipse of magnitude under 0.003 where a point 0.05° outside sees
none. Where lines meet, at first and last contact and where a limit
reaches the horizon, the penumbra touches a place for an instant and
there is no eclipse to see.
Examples
iex> {:ok, path} = Astro.solar_eclipse_path(~D[2024-04-01])
iex> path.type
:total
iex> {length(path.central_line), length(path.northern_limit)}
{197, 198}
iex> point = Enum.find(path.central_line, &(&1.time == ~U[2024-04-08 18:17:00.000Z]))
iex> {longitude, latitude} = point.location.coordinates
iex> {Float.round(longitude, 2), Float.round(latitude, 2), Float.round(point.width, 0)}
{-104.24, 25.19, 198.0}A partial eclipse has no central line, but has a region in which it is seen:
iex> {:ok, path} = Astro.solar_eclipse_path(~D[2025-03-01])
iex> {path.type, path.central_line}
{:partial, []}
iex> DateTime.truncate(path.first_contact.time, :second)
~U[2025-03-29 08:50:43Z]Reference
P. Kenneth Seidelmann (editor), Explanatory Supplement to the Astronomical Almanac (1992), chapter 8 (eclipses of the Sun and Moon).
Jean Meeus, Elements of Solar Eclipses 1951–2200 (1989), on the central line and the limits from Besselian elements.
Fred Espenak, path tables of Central Solar Eclipses: 1991–2050, NASA Goddard Space Flight Center.
@spec solar_noon(location(), Calendar.date()) :: {:ok, DateTime.t()} | {:error, :invalid_location | :invalid_date | :incompatible_calendars}
Returns solar noon at a location on a date, as a UTC date time.
Solar noon is the moment the Sun crosses the local meridian, when it is due north or south and highest in the sky. It is rarely 12:00 on the clock: a mean Sun moving uniformly would cross the meridian at 12:00 local mean time, but the true Sun runs ahead of or behind it by the equation of time, up to about 16 minutes, because the Earth's orbit is elliptical and its axis is tilted.
Solar noon is found as the moment the Sun's local hour angle (the
local apparent sidereal time less the Sun's apparent right
ascension) is zero, by Newton's method from 12:00 local mean time;
see Astro.Solar.solar_noon/2. The Sun's position comes from the
JPL DE440s ephemeris where the loaded kernel covers the date, and
from the analytical series elsewhere.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0that also carries an elevation in metres. Longitude comes first.dateis anyCalendar.date/0. A date in a calendar other thanCalendar.ISOis converted to its ISO date first.
Returns
{:ok, datetime}, the UTC date time of solar noon at the given location on the given UTC date.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :incompatible_calendars}if its calendar cannot be converted toCalendar.ISO.
Accuracy
The hour angle is solved to a millisecond, so solar noon is as accurate as the Sun's right ascension and the sidereal time. Inside the ephemeris the right ascension is accurate to well under 0.01 s of time; outside it the series move solar noon by 0.06 s RMS and 0.15 s at most over 1900–2100. Sidereal time follows the Earth's rotation, UT1, and Astro takes UTC for it; the two differ by up to 0.9 s. The accuracy guide describes how ΔT limits results far from the present.
Examples
iex> Astro.solar_noon {151.20666584, -33.8559799094}, ~D[2019-12-06]
{:ok, ~U[2019-12-06 01:45:52.977018Z]}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting) and chapter 28 (equation of time).
Equation of time and Hour angle, Wikipedia.
@spec solstice(Calendar.year(), :june | :december, options()) :: {:ok, DateTime.t()} | {:error, :invalid_year | :invalid_event | :year_out_of_range | :time_zone_not_found | :utc_only_time_zone_database | :invalid_time_zone_database}
Returns the datetime of either the June or December solstice, in UTC or a requested time zone.
A solstice is the moment the Sun's apparent ecliptic longitude reaches 90° (the June solstice) or 270° (the December solstice). The Sun is then at its farthest north or south of the celestial equator, and the day is the longest of the year in one hemisphere and the shortest in the other.
The moment is found from the Sun's apparent ecliptic longitude
(see sun_apparent_longitude/1). Starting 20 days before the 1st
of the month, the Sun's mean motion of 360° a tropical year
estimates when it reaches the target longitude, and a ten-day
bracket around that estimate is bisected until it is narrower than
a millisecond. The longitude comes from the JPL DE440s ephemeris where the
loaded kernel covers the year, and from the analytical series for
any other year.
Arguments
yearis the Gregorian year for which the solstice is to be calculated, within 50,000 years of 2000.eventis either:juneor:decemberindicating which of the two annual solstice datetimes is required.optionsis a keyword list of options.
Options
:time_zoneis:utc(the default) or a time zone name such as"Asia/Tokyo". The solstice is one instant everywhere; the time zone decides the local date and time it is given in, and so the civil day it falls on.:time_zone_databaseis the module implementingCalendar.TimeZoneDatabasein which a named time zone is looked up. The default is the configured database,Calendar.get_time_zone_database/0. UTC needs none.
Returns
{:ok, datetime}, the solstice in the requested time zone.{:error, :time_zone_not_found}if the time zone is not known to the time zone database.{:error, :utc_only_time_zone_database}if a time zone other than UTC is requested and no time zone database is configured.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :invalid_year}ifyearis not an integer.{:error, :year_out_of_range}ifyearis more than 50,000 years from 2000.{:error, :invalid_event}ifeventis not:juneor:december.
Accuracy
The search ends within a millisecond of the crossing. For the June solstice of 1962, Meeus gives 21h24m42s TD from the full VSOP87 theory (Astronomical Algorithms, chapter 27); Astro gives 21:24:07.8 UTC, which is 21h24m42.0s TD. Outside the ephemeris the series put the events 14 s early on average over 1900–2100, 18 s RMS and 50 s at most. Far from the present the time is limited by ΔT, which is uncertain by minutes before about 1000 and after about 2500; the accuracy guide gives the details.
Examples
iex> {:ok, dt} = Astro.solstice 2019, :december
iex> DateTime.truncate(dt, :second)
~U[2019-12-22 04:19:24Z]
iex> {:ok, dt} = Astro.solstice 2019, :june
iex> DateTime.truncate(dt, :second)
~U[2019-06-21 15:54:13Z]
iex> {:ok, dt} = Astro.solstice 3500, :june
iex> DateTime.to_date(dt)
~D[3500-06-20]
# The June 2021 solstice is on the 21st in UTC but the 20th in Santiago
iex> {:ok, dt} = Astro.solstice(2021, :june, time_zone: "America/Santiago")
iex> DateTime.to_date(dt)
~D[2021-06-20]Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 27 (equinoxes and solstices).
United States Naval Observatory, Earth's Seasons.
Solstice and Tropical year, Wikipedia.
Returns the Sun's apparent ecliptic longitude at a date or date time.
The ecliptic longitude is the Sun's position along the ecliptic, the plane of the Earth's orbit, measured eastward from the equinox of the date. It is 0° at the March equinox, 90° at the June solstice, 180° at the September equinox and 270° at the December solstice, so it measures how far through the tropical year (the year of the seasons) the Earth has moved, without the irregularity of leap years. Solar terms, such as those of the Chinese calendar, are the moments it reaches multiples of 15°.
Where the loaded JPL DE440s ephemeris covers the moment, the
apparent position (see sun_position_at/1) is rotated from the
true equator to the ecliptic of date by the true obliquity.
Elsewhere it is the 49-term series of Bretagnon and Simon, with
aberration and nutation in longitude added.
Arguments
dateis aDate.t/0,NaiveDateTime.t/0orDateTime.t/0. A date is taken at 00:00 UTC.
Returns
- the apparent ecliptic longitude in degrees, from 0 to 360.
Accuracy
Outside the ephemeris the series agree with JPL over 1900–2100 to 0.75″ RMS and 2.0″ at most, with a mean bias of +0.56″. The Sun moves about 2.5″ a minute, so that bias puts the moment the Sun reaches a given longitude about 14 s early. The accuracy guide gives the details, and how the series and ΔT drift far from the present.
Examples
iex> Astro.sun_apparent_longitude ~D[2019-03-21]
0.0838600951350463
iex> Astro.sun_apparent_longitude ~D[2019-06-22]
90.32182153965914
iex> Astro.sun_apparent_longitude ~D[2019-09-23]
179.68053562968015
iex> Astro.sun_apparent_longitude ~D[2019-12-23]
270.8352772904792Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).
Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, for the series of Bretagnon and Simon.
Ecliptic coordinate system, Wikipedia.
@spec sun_azimuth_elevation(location(), Calendar.datetime()) :: {azimuth :: float(), altitude :: float()}
Returns the Sun's azimuth and elevation as seen from a location at a date time.
Azimuth and elevation are the Sun's horizontal coordinates: its
direction around the horizon and its height above it. They are
calculated from the Sun's apparent right ascension α and
declination δ (see sun_position_at/1) and the local hour angle
H, the local apparent sidereal time less α, by the transformation
of Meeus, Astronomical Algorithms, chapter 13:
tan A = sin H / (cos H sin φ − tan δ cos φ)
sin h = sin φ sin δ + cos φ cos δ cos Hwhere φ is the observer's latitude, A is measured from the south and is returned measured from the north, and h is the elevation.
The position is geocentric and airless: it omits the Sun's parallax (at most 8.8″) and atmospheric refraction, which raises the Sun by about 34′ at the horizon and by less than 1′ above an elevation of 45°.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0. Longitude comes first. An elevation does not change the result.date_timeis aDateTime.t/0, or any map that meetsCalendar.datetime/0.
Returns
{azimuth, elevation}in degrees. Azimuth is measured clockwise from north, from 0° to 360°, and elevation above the horizon, from −90° to 90°.
Accuracy
Inside the loaded JPL ephemeris the Sun's apparent position is accurate to a small fraction of an arcsecond, so the result is limited by what it leaves out: for the reference case of NREL's solar position algorithm (Golden, Colorado, 2003-10-17 19:30:30 UTC) it agrees to 0.05″ in azimuth and is 0.87′ lower, the refraction NREL includes. Outside the ephemeris the analytical series agree with JPL to 0.8″ RMS in azimuth and 0.6″ in elevation. The accuracy guide gives the details.
Examples
iex> {azimuth, elevation} =
...> Astro.sun_azimuth_elevation({151.20666584, -33.8559799094}, ~U[2019-12-04 02:00:00Z])
iex> {Float.round(azimuth, 4), Float.round(elevation, 4)}
{343.3379, 77.8643}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 12 (sidereal time) and chapter 13 (transformation of coordinates).
Ibrahim Reda and Afshin Andreas, Solar position algorithm for solar radiation applications, Solar Energy 76 (2004), 577–589.
Horizontal coordinate system, Wikipedia.
@spec sun_position_at(date()) :: Geo.PointZ.t()
Returns the Sun's apparent right ascension, declination and distance at a date or date time.
This is the Sun's apparent geocentric position: where it is seen from the centre of the Earth, referred to the true equator and equinox of the date. Right ascension is measured eastward along the celestial equator from the equinox and declination north of the equator.
Where the loaded JPL DE440s ephemeris covers the moment, the Earth-to-Sun vector is read from it at the moment less the light time, corrected for aberration, and rotated from the ICRF to the true equator of date by IAU 1976 precession and IAU 1980 nutation. Elsewhere the Sun's longitude comes from the 49-term series of Bretagnon and Simon, with aberration and nutation added, and is converted to right ascension and declination with the true obliquity of the ecliptic (Meeus, Astronomical Algorithms, chapter 13).
Arguments
date_timeis aDateTime.t/0or aDate.t/0or any struct that meets the requirements ofCalendar.date/0orCalendar.datetime/0. A date is taken at 00:00 UTC.
Returns
- a
Geo.PointZ.t/0struct with coordinates{right_ascension, declination, distance}with properties%{reference: :celestial, object: :sun}. Right ascension is in degrees from −180° to 180°, declination in degrees anddistancein metres.
Accuracy
Inside the ephemeris the reduction reproduces Meeus's full-precision example 25.b to 0.2″. Outside it the series agree with JPL over 1900–2100 to 0.76″ RMS in right ascension and 0.50″ in declination (2.2″ and 1.3″ at most), and to 4,300 km in distance. Far from the present the series drift, and ΔT, which places the moment in Terrestrial Time, becomes uncertain by minutes to hours; the accuracy guide gives the details.
Examples
iex> %Geo.PointZ{coordinates: {right_ascension, declination, distance}, properties: properties} =
...> Astro.sun_position_at(~D[1992-10-13])
iex> {Float.round(right_ascension, 6), Float.round(declination, 6), round(distance)}
{-161.621245, -7.784069, 149240324715}
iex> properties
%{object: :sun, reference: :celestial}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).
Ryan S. Park and others, The JPL Planetary and Lunar Ephemerides DE440 and DE441, The Astronomical Journal 161 (2021), 105.
Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, for the series of Bretagnon and Simon.
Position of the Sun, Wikipedia.
@spec sunrise(location(), date(), options()) :: {:ok, DateTime.t()} | {:error, :no_time | :invalid_location | :invalid_date | :invalid_options | :invalid_utc_offset | :invalid_limb | :invalid_solar_elevation | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Calculates the sunrise for a given location and date.
Sunrise is the moment when the upper limb of the sun appears on the horizon in the morning.
Refraction lifts the Sun by a standard 34′ at the horizon and its semi-diameter is about 16′, so this is the moment the Sun's centre, computed without refraction, is 50′ below the horizon. The twilights are the moments the centre is 6°, 12° or 18° below it.
The Sun's altitude is calculated from its apparent geocentric
position (see sun_position_at/1) and the local apparent sidereal
time; its parallax, at most 8.8″, is ignored. The altitude is
sampled every 24 minutes across the day to bracket the crossing,
which is then narrowed by bisection to 0.01 s. The position comes
from the JPL DE440s ephemeris where the loaded kernel covers the
date, and from the analytical series elsewhere.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0. Longitude comes first. An elevation does not change the result.dateis aDate.t/0,NaiveDateTime.t/0orDateTime.t/0for the day of the sunrise.optionsis a keyword list of options.
Options
:solar_elevationis the zenith angle of the sun, in degrees, that marks the sunrise, or one of the names below. The default is:geometric.:geometricis 90°, corrected for refraction and the sun's apparent radius so that it matches the moment the upper limb appears to touch the horizon.:civilis 96°. The sun is below the horizon but there is generally enough natural light for most outdoor activities.:nauticalis 102°. The horizon is barely visible, and the moon and stars can still be used for navigation.:astronomicalis 108°. Beyond this, astronomical observation becomes impractical.
:limbis:upper, the default, for the moment the Sun's upper limb is on the horizon, or:centerfor its centre (34′ of refraction alone). It applies to:geometriconly.:time_zoneis the time zone of the returned date time::default, the time zone of the location, which is the default;:utc; or a time zone name.:utc_offsetis a whole number of seconds from UTC, from −50,400 to 50,400. When it is given, the event is the one whose date at that offset isdate, whatever time zone it is returned in: the location's own day, for a calendar that keeps local mean time. Otherwise the date is taken in the requested time zone.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, date_time}wheredate_timeis the sunrise in the requested time zone.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_options}ifoptionsis not a list.{:error, :invalid_utc_offset}if:utc_offsetis not a whole number of seconds from −50,400 to 50,400.{:error, :invalid_limb}if:limbis not:upperor:center.{:error, :no_time}if there is no sunrise on that date at that location, as happens at very high latitudes in summer and winter.{:error, :invalid_solar_elevation}if:solar_elevationis neither a number nor one of the names above.{:error, :time_zone_not_found}if the requested time zone is unknown.{:error, :time_zone_not_resolved}if no time zone can be resolved for the location, which happens when:tz_worldis not a dependency and no:time_zone_resolveris given, or when the resolver returns neither{:ok, time_zone}nor{:error, reason}.{:error, :tz_world_data_not_installed}if:tz_worldis a dependency but its data has not been installed. Runmix tz_world.updateto install it.{:error, :tz_world_backend_not_running}if:tz_worldis a dependency but none of its backends is running; seeAstro.Supervisor.{:error, :invalid_time_zone_resolver}if:time_zone_resolveris not a 1-arity function.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :utc_only_time_zone_database}if the sunrise is requested in a time zone other than UTC and no time zone database is configured.
Accuracy
Against Skyfield, using the same ephemeris, results agree to 0.95 s on average and 1.6 s at most; Skyfield uses the Sun's apparent semi-diameter for the day rather than a fixed 16′. Against timeanddate.com, which publishes to the minute, they agree to 28.8 s on average and within a minute in 307 of 310 cases. Outside the ephemeris the series move the result by 0.07 s RMS and 0.21 s at most. The atmosphere matters more than any of these: refraction at the horizon departs from the standard 34′ with temperature and pressure, and a high observer sees a lower horizon, and neither is modelled. The accuracy guide gives the details.
Examples
iex> {:ok, date_time} = Astro.sunrise({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> date_time
#DateTime<2019-12-04 05:37:06.206432+11:00 AEDT Australia/Sydney>
iex> Astro.sunrise({-62.3481, 82.5018}, ~D[2019-12-04])
{:error, :no_time}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting) and chapter 16 (atmospheric refraction).
United States Naval Observatory, Rise, Set, and Twilight Definitions.
@spec sunset(location(), date(), options()) :: {:ok, DateTime.t()} | {:error, :no_time | :invalid_location | :invalid_date | :invalid_options | :invalid_utc_offset | :invalid_limb | :invalid_solar_elevation | :time_zone_not_found | :time_zone_not_resolved | :tz_world_data_not_installed | :tz_world_backend_not_running | :invalid_time_zone_resolver | :invalid_time_zone_database | :utc_only_time_zone_database}
Calculates the sunset for a given location and date.
Sunset is the moment when the upper limb of the sun disappears below the horizon in the evening.
Refraction lifts the Sun by a standard 34′ at the horizon and its semi-diameter is about 16′, so this is the moment the Sun's centre, computed without refraction, is 50′ below the horizon. The twilights are the moments the centre is 6°, 12° or 18° below it.
The Sun's altitude is calculated from its apparent geocentric
position (see sun_position_at/1) and the local apparent sidereal
time; its parallax, at most 8.8″, is ignored. The altitude is
sampled every 24 minutes across the day to bracket the crossing,
which is then narrowed by bisection to 0.01 s. The position comes
from the JPL DE440s ephemeris where the loaded kernel covers the
date, and from the analytical series elsewhere.
Arguments
locationis the observer's position as a{longitude, latitude}tuple in degrees, aGeo.Point.t/0, or aGeo.PointZ.t/0. Longitude comes first. An elevation does not change the result.dateis aDate.t/0,NaiveDateTime.t/0orDateTime.t/0for the day of the sunset.optionsis a keyword list of options.
Options
:solar_elevationis the zenith angle of the sun, in degrees, that marks the sunset, or one of the names below. The default is:geometric.:geometricis 90°, corrected for refraction and the sun's apparent radius so that it matches the moment the upper limb appears to touch the horizon.:civilis 96°. The sun is below the horizon but there is generally enough natural light for most outdoor activities.:nauticalis 102°. The horizon is barely visible, and the moon and stars can still be used for navigation.:astronomicalis 108°. Beyond this, astronomical observation becomes impractical.
:limbis:upper, the default, for the moment the Sun's upper limb is on the horizon, or:centerfor its centre (34′ of refraction alone). It applies to:geometriconly.:time_zoneis the time zone of the returned date time::default, the time zone of the location, which is the default;:utc; or a time zone name.:utc_offsetis a whole number of seconds from UTC, from −50,400 to 50,400. When it is given, the event is the one whose date at that offset isdate, whatever time zone it is returned in: the location's own day, for a calendar that keeps local mean time. Otherwise the date is taken in the requested time zone.:time_zone_databaseis the module implementing theCalendar.TimeZoneDatabasebehaviour. The default is the configured Elixir time zone database.:time_zone_resolveris a 1-arity function that receives a%Geo.Point{coordinates: {lng, lat}}and returns{:ok, time_zone_name}or{:error, reason}. The default isTzWorld.timezone_at/1when:tz_worldis a dependency.
Returns
{:ok, date_time}wheredate_timeis the sunset in the requested time zone.{:error, :invalid_location}iflocationis not a valid location.{:error, :invalid_date}ifdateis not a valid date in its calendar.{:error, :invalid_options}ifoptionsis not a list.{:error, :invalid_utc_offset}if:utc_offsetis not a whole number of seconds from −50,400 to 50,400.{:error, :invalid_limb}if:limbis not:upperor:center.{:error, :no_time}if there is no sunset on that date at that location, as happens at very high latitudes in summer and winter.{:error, :invalid_solar_elevation}if:solar_elevationis neither a number nor one of the names above.{:error, :time_zone_not_found}if the requested time zone is unknown.{:error, :time_zone_not_resolved}if no time zone can be resolved for the location, which happens when:tz_worldis not a dependency and no:time_zone_resolveris given, or when the resolver returns neither{:ok, time_zone}nor{:error, reason}.{:error, :tz_world_data_not_installed}if:tz_worldis a dependency but its data has not been installed. Runmix tz_world.updateto install it.{:error, :tz_world_backend_not_running}if:tz_worldis a dependency but none of its backends is running; seeAstro.Supervisor.{:error, :invalid_time_zone_resolver}if:time_zone_resolveris not a 1-arity function.{:error, :invalid_time_zone_database}if:time_zone_databaseis not a time zone database.{:error, :utc_only_time_zone_database}if the sunset is requested in a time zone other than UTC and no time zone database is configured.
Accuracy
Against Skyfield, using the same ephemeris, results agree to 0.95 s on average and 1.6 s at most; Skyfield uses the Sun's apparent semi-diameter for the day rather than a fixed 16′. Against timeanddate.com, which publishes to the minute, they agree to 28.8 s on average and within a minute in 307 of 310 cases. Outside the ephemeris the series move the result by 0.07 s RMS and 0.21 s at most. The atmosphere matters more than any of these: refraction at the horizon departs from the standard 34′ with temperature and pressure, and a high observer sees a lower horizon, and neither is modelled. The accuracy guide gives the details.
Examples
iex> {:ok, date_time} = Astro.sunset({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> date_time
#DateTime<2019-12-04 19:53:18.155710+11:00 AEDT Australia/Sydney>
iex> Astro.sunset({-62.3481, 82.5018}, ~D[2019-12-04])
{:error, :no_time}Reference
Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting) and chapter 16 (atmospheric refraction).
United States Naval Observatory, Rise, Set, and Twilight Definitions.