Source code for hebrewcal.calendars_alt.qumran

"""The Qumran / Jubilees 364-day solar calendar.

The calendar found in the Dead Sea Scrolls and the Book of Jubilees has a fixed
364-day year: four quarters of 91 days, each quarter being three months of 30, 30
and 31 days (so months 3, 6, 9 and 12 have 31 days). Because 364 = 52 weeks
exactly, every year — and every festival — falls on the same weekday each year.
The calendar has no intercalation, so it drifts against the seasons over time.

The epoch is conventional: year 1, month 1, day 1 is anchored to the Wednesday on
or after the proleptic Gregorian March equinox of year 1 (the Jubilees New Year is
traditionally a Wednesday, the day the luminaries were created). Year numbering is
therefore nominal; the calendar's defining properties are its 364-day structure
and fixed weekday alignment, both of which are exact.
"""

from __future__ import annotations

from dataclasses import dataclass

from hebrewcal.calendars.gregorian import GregorianDate
from hebrewcal.core.rata_die import weekday_from_rd

_WEDNESDAY = 3
# Month lengths: 30, 30, 31 repeated for each of the four quarters.
_MONTH_LENGTHS = (30, 30, 31, 30, 30, 31, 30, 30, 31, 30, 30, 31)
_YEAR_LENGTH = sum(_MONTH_LENGTHS)  # 364


def _first_weekday_on_or_after(rd: int, weekday: int) -> int:
    return rd + (weekday - weekday_from_rd(rd)) % 7


# Conventional epoch: the Wednesday on or after the proleptic Gregorian March
# equinox of year 1.
QUMRAN_EPOCH: int = _first_weekday_on_or_after(GregorianDate(1, 3, 21).to_rd(), _WEDNESDAY)


[docs] def days_in_year() -> int: """Return the (constant) length of a Qumran year, 364 days.""" return _YEAR_LENGTH
[docs] def last_day_of_month(month: int) -> int: """Return the number of days in ``month`` (30, or 31 for months 3, 6, 9, 12).""" return _MONTH_LENGTHS[month - 1]
[docs] @dataclass(frozen=True, order=True) class QumranDate: """A date in the Qumran / Jubilees 364-day calendar.""" year: int month: int day: int def __post_init__(self) -> None: if not 1 <= self.month <= 12: raise ValueError(f"month out of range: {self.month}") if not 1 <= self.day <= last_day_of_month(self.month): raise ValueError(f"day out of range: {self.day}")
[docs] def to_rd(self) -> int: """Return the Rata Die day count for this date.""" days_before = sum(_MONTH_LENGTHS[: self.month - 1]) return QUMRAN_EPOCH + (self.year - 1) * _YEAR_LENGTH + days_before + self.day - 1
[docs] @classmethod def from_rd(cls, rd: int) -> QumranDate: """Reconstruct a Qumran date from an RD value.""" offset = rd - QUMRAN_EPOCH year = offset // _YEAR_LENGTH + 1 day_of_year = offset % _YEAR_LENGTH month = 1 while day_of_year >= _MONTH_LENGTHS[month - 1]: day_of_year -= _MONTH_LENGTHS[month - 1] month += 1 return cls(year, month, day_of_year + 1)