Astronomy and locations

The astronomy layer computes the sun’s position and the day’s solar events for any location — in pure Python, with no dependencies and no network calls. It is the foundation for the religious times (Shabbat, zmanim) that arrive in a later phase.

Accuracy and range

Sunrise and sunset agree with NOAA / reference implementations to within about 15-20 seconds at mid latitudes (the target is one minute). Because the layer bridges to datetime, it is limited to the years datetime supports (1–9999).

Locations

A Location carries coordinates, an optional elevation and an IANA time-zone name. Latitude and longitude are decimal degrees; longitude is positive east.

>>> from hebrewcal.astro.location import Location
>>> nyc = Location(40.7128, -74.0060, timezone="America/New_York")
>>> jerusalem = Location(31.7683, 35.2137, elevation=754.0, timezone="Asia/Jerusalem")
>>> nyc.timezone
'America/New_York'

Out-of-range coordinates or an unknown time zone raise ValueError.

Sunrise, sunset and solar noon

The event functions take a GregorianDate and a Location and return a timezone-aware datetime in the location’s zone (or None at high latitudes where the event does not occur that day).

>>> from hebrewcal.astro.solar import sunrise, sunset, solar_noon
>>> from hebrewcal.calendars.gregorian import GregorianDate
>>> date = GregorianDate(2026, 6, 26)
>>> sunrise(date, nyc).strftime("%H:%M")
'05:26'
>>> sunset(date, nyc).strftime("%H:%M")
'20:31'
>>> solar_noon(date, nyc).strftime("%H:%M")
'12:58'

Elevation

By default sunrise/sunset use the sea-level horizon. Pass elevation=True to account for the observer’s elevation (Location.elevation), which lowers the visible horizon — sunrise earlier, sunset later. The dip uses the geometric formula acos(R / (R + h)), consistent with common zmanim software:

>>> jerusalem = Location(31.7683, 35.2137, elevation=754.0, timezone="Asia/Jerusalem")
>>> sunrise(GregorianDate(2026, 6, 26), jerusalem, elevation=True) < \
...     sunrise(GregorianDate(2026, 6, 26), jerusalem)
True

Events are absolute instants

The returned datetime is a true instant converted to the location’s zone, so its civil date may differ from the input date — for example a sunset just after midnight during polar summer. Compare instants, not wall-clock fields, when this matters.

Polar days and nights

When the sun does not rise or set on a given date, the function returns None:

>>> north = Location(89.9, 0.0, timezone="UTC")
>>> sunrise(GregorianDate(2026, 6, 21), north) is None   # midnight sun
True

Twilight

dawn and dusk take a solar depression angle below the horizon. The standard angles are provided as constants; the default is civil twilight.

>>> from hebrewcal.astro.solar import (
...     dawn, dusk, CIVIL_DEPRESSION, NAUTICAL_DEPRESSION, ASTRONOMICAL_DEPRESSION,
... )
>>> dawn(date, nyc).strftime("%H:%M")        # civil dawn (default)
'04:53'
>>> dusk(date, nyc, ASTRONOMICAL_DEPRESSION).strftime("%H:%M")
'22:37'

Constant

Depression

Meaning

CIVIL_DEPRESSION

civil twilight

NAUTICAL_DEPRESSION

12°

nautical twilight

ASTRONOMICAL_DEPRESSION

18°

astronomical twilight

The general form accepts any angle, which later phases reuse for several zmanim.

Lower-level solar terms

If you need the raw quantities, solar_declination and equation_of_time are available:

>>> from hebrewcal.astro.solar import solar_declination, equation_of_time
>>> round(solar_declination(2026, 6, 21), 2)   # near the June solstice
23.44
>>> round(equation_of_time(2026, 11, 3), 1)    # minutes, early November maximum
16.5

The molad as a civil instant

The molad is the calendar’s mean lunar conjunction. molad_moment returns it as a naive datetime in Jerusalem mean time (the molad “day” begins at 18:00 the previous evening); molad_breakdown gives the traditional day/hours/parts presentation.

>>> from hebrewcal.astro.molad import molad_moment, molad_breakdown
>>> molad_moment(5785, 7).strftime("%Y-%m-%d %H:%M")    # molad of Tishri 5785
'2024-10-03 09:21'
>>> day_index, hours, parts = molad_breakdown(5785, 7)
>>> hours, parts                                         # after 18:00
(15, 391)

Mean vs. true conjunction

The molad is a mean conjunction and can differ from the true astronomical new moon by up to ~14 hours. The interval between consecutive molads is exactly 29 days, 12 hours and 793 parts.

The true new moon

hebrewcal.astro.lunar computes the true lunar conjunction (Meeus, Astronomical Algorithms), for comparison with the molad and for academic use:

>>> from hebrewcal.astro.lunar import nth_new_moon, new_moon_at_or_after
>>> from hebrewcal import GregorianDate
>>> nth_new_moon(0).strftime("%Y-%m-%d %H:%M")        # Meeus lunation 0
'2000-01-06 18:14'
>>> new_moon_at_or_after(GregorianDate(2024, 12, 1)).strftime("%Y-%m-%d %H:%M")
'2024-12-01 06:22'

Accuracy and time scale

The returned datetimes are UTC-aware but the underlying value is in Terrestrial Time; the difference from UTC is ΔT (a few tens of seconds today). The implementation agrees with an independent ephemeris to about one minute.