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 |
|---|---|---|
|
6° |
civil twilight |
|
12° |
nautical twilight |
|
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.