pax_global_header00006660000000000000000000000064140602072560014514gustar00rootroot0000000000000052 comment=ceae91f389b6b534970ddff8d7bf18461389161d pyluach-1.3.0/000077500000000000000000000000001406020725600131625ustar00rootroot00000000000000pyluach-1.3.0/.gitignore000066400000000000000000000001021406020725600151430ustar00rootroot00000000000000.* !.gitignore *.pyc *~ ENV* *egg* *build* *.whl dist/* *htmlcov* pyluach-1.3.0/.travis.yml000066400000000000000000000003221406020725600152700ustar00rootroot00000000000000language: python python: - "3.4" - "3.5" - "3.6" - "3.7" - "3.8" - "3.9" install: - pip install coveralls pytest-cov script: pytest --cov-report= --cov=pyluach tests/ after_success: coveralls pyluach-1.3.0/CHANGELOG.rst000066400000000000000000000034611406020725600152070ustar00rootroot00000000000000========== Change Log ========== This document records all notable changes to `pyluach `_. This project adheres to `Semantic Versioning `_. `1.3.0`_ (2021-06-09) --------------------- * Added option to get parsha in Hebrew. * Added HebrewDate methods to get hebrew day, month, year, and date string in Hebrew. * Added method to get Month names in Hebrew. * Added methods to get Year and month strings in Hebrew. * Added classmethods to Year and Month to get objects from dates and pydates * Added methods to dates classes to get holidays, fast days and festivals. * Implemented more consistent Hebrew to English transliterations for parshios. `1.2.1`_ (2020-11-08) --------------------- * Fixed molad having weekday of `0` when it should be `7`. `1.2.0`_ (2020-10-28) --------------------- * Created isoweekday method for all date types. * Created fast_day and festival functions (`#11`_) * Added Pesach Sheni to festival. `1.1.1`_ (2020-08-14) --------------------- * Fixed error getting parsha of Shabbos on Rosh Hashana. `1.1.0`_ (2020-06-03) --------------------- * Redesigned documentation. * Added molad and molad_announcement methods to hebrewcal.Month. * Stopped supporting python < 3.4 and modernized code. `1.0.1`_ (2019-03-02) --------------------- * Initial public release .. _`1.3.0`: https://github.com/simlist/pyluach/compare/v1.2.1...v1.3.0 .. _`1.2.1`: https://github.com/simlist/pyluach/compare/v1.2.0...v1.2.1 .. _`1.2.0`: https://github.com/simlist/pyluach/compare/v1.1.1...v1.2.0 .. _`1.1.1`: https://github.com/simlist/pyluach/compare/v1.1.0...v1.1.1 .. _`1.1.0`: https://github.com/simlist/pyluach/compare/v1.0.1...v1.1.0 .. _`1.0.1`: https://github.com/simlist/pyluach/releases/tag/v1.0.1 .. _`#11`: https://github.com/simlist/pyluach/issues/11pyluach-1.3.0/README.rst000066400000000000000000000052221406020725600146520ustar00rootroot00000000000000pyluach ======= .. image:: https://readthedocs.org/projects/pyluach/badge/?version=latest :target: http://pyluach.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://travis-ci.org/simlist/pyluach.svg?branch=master :target: https://travis-ci.org/simlist/pyluach .. image:: https://coveralls.io/repos/github/simlist/pyluach/badge.svg?branch=master :target: https://coveralls.io/github/simlist/pyluach?branch=master Pyluach is a Python package for manipulating Hebrew (Jewish) calendar dates and Hebrew-Gregorian conversions. Features --------- * Conversion between Hebrew and Gregorian dates * Finding the difference between two dates * Finding a date at a given duration from the given date * Rich comparisons between dates * Finding the weekday of a given date * Finding the weekly Parsha reading of a given date Installation ------------- Use ``pip install pyluach``. Documentation ------------- Documentation for pyluach can be found at https://readthedocs.org/projects/pyluach/. Examples ------------ :: >>> from pyluach import dates, hebrewcal, parshios >>> today = dates.HebrewDate.today() >>> lastweek_gregorian = (today - 7).to_greg() >>> lastweek_gregorian < today True >>> today - lastweek_gregorian 7 >>> greg = GregorianDate(1986, 3, 21) >>> heb = HebrewDate(5746, 13, 10) >>> greg == heb True >>> purim = HebrewDate(5781, 12, 14) >>> purim.hebrew_day() 'י״ד' >>> purim.hebrew_date_string() 'י״ד אדר תשפ״א' >>> purim.hebrew_date_string(True) 'י״ד אדר ה׳תשפ״א' >>> rosh_hashana = dates.HebrewDate(5782, 7, 1) >>> rosh_hashana.holiday() 'Rosh Hashana' >>> rosh_hashana.holiday(hebrew=True) 'ראש השנה' >>> (rosh_hashana + 3).holiday() None >>> month = hebrewcal.Month(5781, 10) >>> month.month_name() 'Teves' >>> month.month_name(True) 'טבת' >>> month + 3 Month(5781, 1) >>> for month in hebrewcal.Year(5774).itermonths(): ... print(month.name) Tishrei Cheshvan ... >>> date = dates.GregorianDate(2010, 10, 6) >>> parshios.getparsha(date) [0] >>> parshios.getparsha_string(date, israel=True) 'Beraishis' >>> parshios.getparsha_string(date, hebrew=True) 'בראשית' >>> new_date = dates.GregorianDate(2021, 3, 10) >>> parshios.getparsha_string(new_date) 'Vayakhel, Pekudei' >>> parshios.getparsha_string(new_date, hebrew=True) 'ויקהל, פקודי' Contact -------- For questions and comments feel free to contact me at simlist@gmail.com. License -------- Pyluach is licensed under the MIT license. pyluach-1.3.0/docs/000077500000000000000000000000001406020725600141125ustar00rootroot00000000000000pyluach-1.3.0/docs/Makefile000066400000000000000000000011721406020725600155530ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) pyluach-1.3.0/docs/conf.py000066400000000000000000000044631406020725600154200ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) import sphinx_rtd_theme # -- Project information ----------------------------------------------------- project = 'pyluach' copyright = '2016, MS List' author = 'MS List' # The full version, including alpha/beta/rc tags release = '1.3.0' # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.autosummary', 'sphinx.ext.napoleon', 'sphinx_rtd_theme', ] # Napolean settings napoleon_include_special_with_doc = False # Autodoc settings autodoc_default_options = { 'members': True, 'inherited-members': True, 'show-inheritance': True } autodoc_member_order = 'bysource' # Autosummary settings autosummary_generate = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static']pyluach-1.3.0/docs/dates.rst000066400000000000000000000000701406020725600157410ustar00rootroot00000000000000dates module ------------ .. automodule:: pyluach.datespyluach-1.3.0/docs/hebrewcal.rst000066400000000000000000000001141406020725600165740ustar00rootroot00000000000000hebrewcal module ------------------------ .. automodule:: pyluach.hebrewcalpyluach-1.3.0/docs/index.rst000066400000000000000000000005411406020725600157530ustar00rootroot00000000000000Welcome to pyluach's documentation! =================================== .. automodule:: pyluach :members: .. include:: ../README.rst .. toctree:: :hidden: self .. toctree:: :maxdepth: 1 :caption: Contents: dates hebrewcal parshios Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pyluach-1.3.0/docs/make.bat000066400000000000000000000013701406020725600155200ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd pyluach-1.3.0/docs/parshios.rst000066400000000000000000000001111406020725600164650ustar00rootroot00000000000000parshios module ----------------------- .. automodule:: pyluach.parshiospyluach-1.3.0/docs/requirements.txt000066400000000000000000000000571406020725600174000ustar00rootroot00000000000000sphinxcontrib-napoleon sphinx_rtd_theme pyluachpyluach-1.3.0/license.txt000066400000000000000000000021431406020725600153450ustar00rootroot00000000000000The MIT License (MIT) [OSI Approved License] The MIT License (MIT) Copyright (c) 2014 Meir S. List Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.pyluach-1.3.0/pyluach/000077500000000000000000000000001406020725600146275ustar00rootroot00000000000000pyluach-1.3.0/pyluach/__init__.py000066400000000000000000000001521406020725600167360ustar00rootroot00000000000000"""Pyluach is a Python package for manipulating Hebrew calendar dates and Hebrew-Gregorian conversions."""pyluach-1.3.0/pyluach/dates.py000066400000000000000000000560541406020725600163130ustar00rootroot00000000000000"""The dates module implements classes for representing and manipulating several date types. Contents -------- * :class:`~pyluach.dates.BaseDate` * :class:`~pyluach.dates.CalendarDateMixin` * :class:`~pyluach.dates.JulianDay` * :class:`~pyluach.dates.GregorianDate` * :class:`~pyluach.dates.HebrewDate` Note ---- All instances of the classes in this module should be treated as read only. No attributes should be changed once they're created. """ from datetime import date from numbers import Number from functools import lru_cache from pyluach import utils from pyluach import gematria class BaseDate: """BaseDate is a base class for all date types. It provides the following arithmetic and comparison operators common to all child date types. =================== ============================================= Operation Result =================== ============================================= d2 = date1 + int New date ``int`` days after date1 d2 = date1 - int New date ``int`` days before date1 int = date1 - date2 Integer equal to the absolute value of the difference between date1 and date2 date1 > date2 True if date1 occurs later than date2 date1 < date2 True if date1 occurs earlier than date2 date1 == date2 True if date1 occurs on the same day as date2 date1 != date2 True if ``date1 == date2`` is False date1 >=, <= date2 True if both are True =================== ============================================= Any child of BaseDate that implements a `jd` attribute representing the Julian Day of that date can be compared to and diffed with any other valid date type. """ def __hash__(self): return hash(self.jd) def __add__(self, other): try: return JulianDay(self.jd + other)._to_x(self) except TypeError: raise TypeError('You can only add a number to a date.') def __sub__(self, other): try: if isinstance(other, Number): return JulianDay(self.jd - other)._to_x(self) return abs(self.jd - other.jd) except (AttributeError, TypeError): raise TypeError("""You can only subtract a number or another date that has a "jd" attribute from a date""") def __eq__(self, other): try: if self.jd == other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def __ne__(self, other): try: if self.jd != other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def __lt__(self, other): try: if self.jd < other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def __gt__(self, other): try: if self.jd > other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def __le__(self, other): try: if self.jd <= other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def __ge__(self, other): try: if self.jd >= other.jd: return True return False except AttributeError: raise TypeError(self._error_string) def shabbos(self): """Return the Shabbos on or following the date. Returns ------- ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` `self` if the date is Shabbos or else the following Shabbos as the same date type as operated on. Examples -------- >>> heb_date = HebrewDate(5781, 3, 29) >>> greg_date = heb_date.to_greg() >>> heb_date.shabbos() HebrewDate(5781, 4, 2) >>> greg_date.shabbos() GregorianDate(2021, 6, 12) """ return self + (7 - self.weekday()) def fast_day(self, hebrew=False): """Return name of fast day of date. Parameters ---------- hebrew : bool, optional ``True`` if you want the fast day name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the fast day or ``None`` if the date is not a fast day. """ return utils._fast_day_string(self, hebrew) def festival(self, israel=False, hebrew=False): """Return name of Jewish festival of date. This method will return all major and minor religous Jewish holidays not including fast days. Parameters ---------- israel : bool, optional ``True`` if you want the holidays according to the Israel schedule. Defaults to ``False``. hebrew : bool, optional ``True`` if you want the festival name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the festival or ``None`` if the given date is not a Jewish festival. """ return utils._festival_string(self, israel, hebrew) def holiday(self, israel=False, hebrew=False): """Return name of Jewish holiday of the date. The holidays include the major and minor religious Jewish holidays including fast days. Parameters ---------- israel : bool, optional ``True`` if you want the holidays according to the Israel schedule. Defaults to ``False``. hebrew : bool, optional ``True`` if you want the holiday name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the holiday or ``None`` if the given date is not a Jewish holiday. """ return utils._holiday(self, israel, hebrew) def isoweekday(self): """Return the day of the week corresponding to the iso standard. Returns ------- int An integer representing the day of the week where Monday is 1 and and Sunday is 7. """ weekday = self.weekday() if weekday == 1: return 7 return weekday - 1 class CalendarDateMixin: """CalendarDateMixin is a mixin for Hebrew and Gregorian dates. Parameters ---------- year : int month : int day : int Attributes ---------- year : int month : int day : int jd : float The equivelant Julian day at midnight. """ def __init__(self, year, month, day, jd=None): """Initialize a calendar date.""" self.year = year self.month = month self.day = day self._jd = jd self. _error_string = ('''Only a date with a "jd" attribute can be compared to a {0}'''.format( self.__class__.__name__) ) def __repr__(self): return '{0}({1}, {2}, {3})'.format(self.__class__.__name__, self.year, self.month, self.day) def __str__(self): return '{0:04d}-{1:02d}-{2:02d}'.format(self.year, self.month, self.day) def __iter__(self): yield self.year yield self.month yield self.day def weekday(self): """Return day of week as an integer. Returns ------- int An integer representing the day of the week with Sunday as 1 through Saturday as 7. """ return int(self.jd+.5+1) % 7 + 1 def tuple(self): """Return date as tuple. Returns ------- tuple of ints A tuple of ints in the form ``(year, month, day)``. """ return (self.year, self.month, self.day) def dict(self): """Return the date as a dictionary. Returns ------- Dict A dictionary in the form ``{'year': int, 'month': int, 'day': int}``. """ return {'year': self.year, 'month': self.month, 'day': self.day} class JulianDay(BaseDate): """A JulianDay object represents a Julian Day at midnight. Parameters ---------- day : float or int The julian day. Note that Julian days start at noon so day number 10 is represented as 9.5 which is day 10 at midnight. Attributes ---------- day : float The Julian Day Number at midnight (as *n*.5) jd : float Alias for day. """ def __init__(self, day): """Initialize a JulianDay instance.""" if day-int(day) < .5: self.day = int(day) - .5 else: self.day = int(day) + .5 self.jd = self.day self._error_string = """Only a date with a "jd" attribute can be compared to a Julian Day instance.""" def __repr__(self): return 'JulianDay({0})'.format(self.day) def __str__(self): return str(self.day) def weekday(self): """Return weekday of date. Returns ------- int The weekday with Sunday as 1 through Saturday as 7. """ return (int(self.day+.5) + 1) % 7 + 1 @staticmethod def from_pydate(pydate): """Return a `JulianDay` from a python date object. Parameters ---------- pydate : datetime.date A python standard library ``datetime.date`` instance Returns ------- JulianDay """ return GregorianDate.from_pydate(pydate).to_jd() @staticmethod def today(): """Return instance of current Julian day from timestamp. Extends the built-in ``datetime.date.today()``. Returns ------- JulianDay A JulianDay instance representing the current Julian day from the timestamp. """ return GregorianDate.today().to_jd() def to_greg(self): """Convert JulianDay to a Gregorian Date. Returns ------- GregorianDate The equivalent Gregorian date instance. Notes ----- This method uses the Fliegel-Van Flandern algorithm. """ jd = int(self.day + .5) L = jd + 68569 n = 4*L // 146097 L = L - (146097*n + 3) // 4 i = (4000 * (L+1)) // 1461001 L = L - ((1461*i) // 4) + 31 j = (80*L) // 2447 day = L - 2447*j // 80 L = j // 11 month = j + 2 - 12*L year = 100 * (n-49) + i + L if year < 1: year -= 1 return GregorianDate(year, month, day, self.day) def to_heb(self): """ Convert to a Hebrew date. Returns ------- HebrewDate The equivalent Hebrew date instance. """ if self.day <= 347997: raise ValueError('Date is before creation') jd = int(self.day + .5) # Try to account for half day jd -= 347997 year = int(jd//365) + 2 ## try that to debug early years first_day = utils._elapsed_days(year) while first_day > jd: year -= 1 first_day = utils._elapsed_days(year) months = [7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6] if not utils._is_leap(year): months.remove(13) days_remaining = jd - first_day for month in months: if days_remaining >= utils._month_length(year, month): days_remaining -= utils._month_length(year, month) else: return HebrewDate(year, month, days_remaining + 1, self.day) def _to_x(self, type_): """Return a date object of the given type.""" if isinstance(type_, GregorianDate): return self.to_greg() elif isinstance(type_, HebrewDate): return self.to_heb() elif isinstance(type_, JulianDay): return self def to_pydate(self): """Convert to a datetime.date object. Returns ------- datetime.date A standard library ``datetime.date`` instance. """ return self.to_greg().to_pydate() class GregorianDate(BaseDate, CalendarDateMixin): """A GregorianDate object represents a Gregorian date (year, month, day). This is an idealized date with the current Gregorian calendar infinitely extended in both directions. Parameters ---------- year : int month : int day : int jd : float, optional This parameter should not be assigned manually. Attributes ---------- year : int month : int day : int jd : float(property) The corresponding Julian Day Number at midnight (as *n*.5). Warnings -------- Although B.C.E. dates are allowed, they should be treated as approximations as they may return inconsistent results when converting between date types and using arithmetic and comparison operators! """ def __init__(self, year, month, day, jd=None): """Initialize a GregorianDate. This initializer extends the CalendarDateMixin initializer adding in date validation specific to Gregorian dates. """ if month < 1 or month > 12: raise ValueError('{0} is an invalid month.'.format(str(month))) monthlength = self._monthlength(year, month) if day < 1 or day > monthlength: raise ValueError('Given month has {0} days.'.format(monthlength)) super().__init__(year, month, day, jd) @property def jd(self): """Return the corresponding Julian day number. This property retrieves the corresponding Julian Day as a float if it was passed into the init method or already calculated, and if it wasn't, it calculates it and saves it for later retrievals and returns it. Returns ------- float The Julian day number at midnight. """ if self._jd is None: year = self.year month = self.month day = self.day if year < 0: year += 1 if month < 3: year -= 1 month += 12 month += 1 a = year // 100 b = 2 - a + a//4 self._jd = (int(365.25*year) + int(30.6001*month) + b + day + 1720994.5) return self._jd @classmethod def from_pydate(cls, pydate): """Return a `GregorianDate` instance from a python date object. Parameters ---------- pydate : datetime.date A python standard library ``datetime.date`` instance. Returns ------- GregorianDate """ return cls(*pydate.timetuple()[:3]) @staticmethod def today(): """Return a GregorianDate instance for the current day. This static method wraps the Python standard library's date.today() method to get the date from the timestamp. Returns ------- GregorianDate The current Gregorian date from the computer's timestamp. """ return GregorianDate.from_pydate(date.today()) @staticmethod def _is_leap(year): """Return True if year of date is a leap year, otherwise False.""" if year < 0: year += 1 if( (year % 4 == 0) and not (year % 100 == 0 and year % 400 != 0) ): return True return False def is_leap(self): """Return if the date is in a leap year Returns ------- bool True if the date is in a leap year, False otherwise. """ return self._is_leap(self.year) @classmethod def _monthlength(cls, year, month): if month in [1, 3, 5, 7, 8, 10, 12]: return 31 elif month != 2: return 30 else: return 29 if cls._is_leap(year) else 28 def to_jd(self): """Convert to a Julian day. Returns ------- JulianDay The equivalent JulianDay instance. """ return JulianDay(self.jd) def to_heb(self): """Convert to Hebrew date. Returns ------- HebrewDate The equivalent HebrewDate instance. """ return self.to_jd().to_heb() def to_pydate(self): """Convert to a standard library date. Returns ------- datetime.date The equivalent datetime.date instance. """ return date(*self.tuple()) class HebrewDate(BaseDate, CalendarDateMixin): """A class for manipulating Hebrew dates. Parameters ---------- year : int The Hebrew year. If the year is less than 1 it will raise a ValueError. month : int The Hebrew month starting with Nissan as 1 (and Tishrei as 7). If there is a second Adar in the year it is represented as 13. A month below 1 or above the last month will raise a ValueError. day : int The Hebrew day of the month. An invalid day will raise a ValueError. jd : float, optional This parameter should not be assigned manually. Attributes ---------- year : int month : int The Hebrew month starting with Nissan as 1 (and Tishrei as 7). If there is a second Adar it is represented as 13. day : int The day of the month. """ def __init__(self, year, month, day, jd=None): """Initialize a HebrewDate instance. This initializer extends the CalendarDateMixin adding validation specific to hebrew dates. """ if year < 1: raise ValueError('Date supplied is before creation.') if month < 1 or month > 13: raise ValueError('{0} is an invalid month.'.format(str(month))) if (not utils._is_leap(year)) and month == 13: raise ValueError('{0} is not a leap year'.format(year)) monthlength = utils._month_length(year, month) if day < 1 or day > monthlength: raise ValueError('Given month has {0} days.'.format(monthlength)) super().__init__(year, month, day, jd) @property def jd(self): """Return the corresponding Julian day number. This property retrieves the corresponding Julian Day as a float if it was passed into the init method or already calculated, and if it wasn't, it calculates it, saves it for later retrievals, and returns it. Returns ------- float The Julian day number at midnight. """ if self._jd is None: months = [7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6] if not utils._is_leap(self.year): months.remove(13) jd = utils._elapsed_days(self.year) for m in months: if m != self.month: jd += utils._month_length(self.year, m) else: self._jd = jd + (self.day-1) + 347996.5 return self._jd @staticmethod def from_pydate(pydate): """Return a `HebrewDate` from a python date object. Parameters ---------- pydate : datetime.date A python standard library ``datetime.date`` instance Returns ------- HebrewDate """ return GregorianDate.from_pydate(pydate).to_heb() @staticmethod def today(): """Return HebrewDate instance for the current day. This static method wraps the Python standard library's ``date.today()`` method to get the date from the timestamp. Returns ------- HebrewDate The current Hebrew date from the computer's timestamp. Notes ----- This method coverts the Gregorian date from the time stamp to a Hebrew date, so if it is after nightfall but before midnight you will have to add one day, ie. ``today = HebrewDate.today() + 1``. """ return GregorianDate.today().to_heb() def to_jd(self): """Convert to a Julian day. Returns ------- JulianDay The equivalent JulianDay instance. """ return JulianDay(self.jd) def to_greg(self): """Convert to a Gregorian date. Returns ------- GregorianDate The equivalent GregorianDate instance. """ return self.to_jd().to_greg() def to_pydate(self): """Convert to a standard library date. Returns ------- datetime.date The equivalent datetime.date instance. """ return self.to_greg().to_pydate() def to_heb(self): return self def month_name(self, hebrew=False): """ Return the name of the month. Parameters ---------- hebrew : bool, optional ``True`` if the month name should be in Hebrew characters. Default is ``False`` which returns the month name transliterated into English. Returns ------- str """ return utils._month_name(self.year, self.month, hebrew) def hebrew_day(self): """Return the day of the month in Hebrew letters. Returns ------- str The day of the month in Hebrew letters. For example 'א׳' for 1, 'ט״ו' for 15. """ return gematria._num_to_str(self.day) def hebrew_year(self, thousands=False): """Return the year in Hebrew letters. Parameters ---------- thousands : bool ``True`` to prefix the year with a letter for the thousands place, ie. 'ה׳תשפ״א'. Default is ``False``. Returns ------- str """ return gematria._num_to_str(self.year, thousands) def hebrew_date_string(self, thousands=False): """Return a Hebrew string representation of the date. The date is in the form ``f'{day} {month} {year}'``. Parameters ---------- thousands : bool ``True`` to have the thousands include in the year. Default is ``False``. Returns ------- str Examples -------- >>> date = HebrewDate(5781, 9, 25) >>> date.hebrew_date_string() 'כ״ה כסלו תשפ״א' >>> date.hebrew_date_string(True) 'כ״ה כסלו ה׳תשפ״א' """ return '{} {} {}'.format( self.hebrew_day(), self.month_name(True), self.hebrew_year(thousands) )pyluach-1.3.0/pyluach/gematria.py000066400000000000000000000032421406020725600167730ustar00rootroot00000000000000_GEMATRIOS = { 1: 'א', 2: 'ב', 3: 'ג', 4: 'ד', 5: 'ה', 6: 'ו', 7: 'ז', 8: 'ח', 9: 'ט', 10: 'י', 20: 'כ', 30: 'ל', 40: 'מ', 50: 'נ', 60: 'ס', 70: 'ע', 80: 'פ', 90: 'צ', 100: 'ק', 200: 'ר', 300: 'ש', 400: 'ת' } def _stringify_gematria(letters): """Insert geresh or gershayim symbols into gematria.""" length = len(letters) if length > 1: return '{}״{}'.format(letters[:-1], letters[-1]) if length == 1: return '{}׳'.format(letters) return '' def _get_letters(num): """Convert numbers under 1,000 into raw letters.""" ones = num % 10 tens = num % 100 - ones hundreds = num % 1000 - tens - ones four_hundreds = ''.join(['ת' for i in range(hundreds // 400)]) ones = _GEMATRIOS.get(ones, '') tens = _GEMATRIOS.get(tens, '') hundreds = _GEMATRIOS.get(hundreds % 400, '') letters = '{}{}{}{}'.format(four_hundreds, hundreds, tens, ones) return letters.replace('יה', 'טו').replace('יו', 'טז') def _num_to_str(num, thousands=False): """Return gematria string for number. Parameters ---------- num : int The number to get the Hebrew letter representation thousands : bool, optional True if the hebrew returned should include a letter for the thousands place ie. 'ה׳' for five thousand. Returns ------- str The Hebrew representation of the number. """ letters = _stringify_gematria(_get_letters(num)) if thousands: thousand = _get_letters(num // 1000) + '׳' letters = thousand + letters return letterspyluach-1.3.0/pyluach/hebrewcal.py000066400000000000000000000414161406020725600171430ustar00rootroot00000000000000"""The hebrewcal module implements classes for representing a Hebrew year and month. It also has functions for getting the holiday or fast day for a given date. """ from collections import deque from numbers import Number from functools import lru_cache from pyluach.dates import GregorianDate, HebrewDate from pyluach import utils from pyluach.gematria import _num_to_str from pyluach.utils import _holiday, _fast_day_string, _festival_string def fast_day(date, hebrew=False): """Return name of fast day or None. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. hebrew : bool, optional ``True`` if you want the fast_day name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the fast day or ``None`` if the given date is not a fast day. """ return _fast_day_string(date, hebrew) def festival(date, israel=False, hebrew=False): """Return Jewish festival of given day. This method will return all major and minor religous Jewish holidays not including fast days. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. israel : bool, optional ``True`` if you want the festivals according to the Israel schedule. Defaults to ``False``. hebrew : bool, optional ``True`` if you want the festival name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the festival or ``None`` if the given date is not a Jewish festival. """ return _festival_string(date, israel, hebrew) def holiday(date, israel=False, hebrew=False): """Return Jewish holiday of given date. The holidays include the major and minor religious Jewish holidays including fast days. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. israel : bool, optional ``True`` if you want the holidays according to the israel schedule. Defaults to ``False``. hebrew : bool, optional ``True`` if you want the holiday name in Hebrew letters. Default is ``False``, which returns the name transliterated into English. Returns ------- str or ``None`` The name of the holiday or ``None`` if the given date is not a Jewish holiday. """ return _holiday(date, israel, hebrew) class Year: """A Year object represents a Hebrew calendar year. It provided the following operators: ===================== ================================================ Operation Result ===================== ================================================ year2 = year1 + int New ``Year`` ``int`` days after year1. year2 = year1 - int New ``Year`` ``int`` days before year1. int = year1 - year2 ``int`` equal to the absolute value of the difference between year2 and year1. bool = year1 == year2 True if year1 represents the same year as year2. bool = year1 > year2 True if year1 is later than year2. bool = year1 >= year2 True if year1 is later or equal to year2. bool = year1 < year2 True if year 1 earlier than year2. bool = year1 <= year2 True if year 1 earlier or equal to year 2. ===================== ================================================ Parameters ---------- year : int A Hebrew year. Attributes ---------- year : int The hebrew year. leap : bool True if the year is a leap year else false. """ def __init__(self, year): if year < 1: raise ValueError('Year {0} is before creation.'.format(year)) self.year = year self.leap = utils._is_leap(year) def __repr__(self): return 'Year({0})'.format(self.year) def __len__(self): return utils._days_in_year(self.year) def __eq__(self, other): if isinstance(other, Year) and self.year == other.year: return True return False def __add__(self, other): """Add int to year.""" try: return Year(self.year + other) except TypeError: raise TypeError('You can only add a number to a year.') def __sub__(self, other): """Subtract int or Year from Year. If other is an int return a new Year other before original year. If other is a Year object, return delta of the two years as an int. """ if isinstance(other, Year): return abs(self.year - other.year) else: try: return Year(self.year - other) except TypeError: raise TypeError('Only an int or another Year object can' ' be subtracted from a year.') def __gt__(self, other): if self.year > other.year: return True return False def __ge__(self, other): if self == other or self > other: return True return False def __lt__(self, other): if self.year < other.year: return True return False def __le__(self, other): if self < other or self == other: return True return False def __iter__(self): """Yield integer for each month in year.""" months = [7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6] if not self.leap: months.remove(13) for month in months: yield month def itermonths(self): """Yield Month instance for each month of the year. Yields ------ ``Month`` The next month in the Hebrew calendar year as a ``luachcal.hebrewcal.Month`` instance beginning with Tishrei and ending with Elul. """ for month in self: yield Month(self.year, month) def iterdays(self): """Yield integer for each day of the year. Yields ------ int An integer beginning with 1 representing the next day of the year. """ for day in range(1, len(self) + 1): yield day def iterdates(self): """Yield HebrewDate instance for each day of the year. Yields ------ HebrewDate The next date of the Hebrew calendar year starting with the first of Tishrei. """ for month in self.itermonths(): for day in month: yield HebrewDate(self.year, month.month, day) @classmethod def from_date(cls, date): """Return Year object that given date occurs in. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any one of the ``pyluach`` date types. Returns ------- Year """ return cls(date.to_heb().year) @classmethod def from_pydate(cls, pydate): """Return Year object from python date object. Parameters ---------- pydate : ``datetime.date`` A python standard library date object Returns ------- Year The Hebrew year the given date occurs in """ return cls.from_date(HebrewDate.from_pydate(pydate)) def year_string(self, thousands=False): """Return year as a Hebrew string. Parameters ---------- thousands: bool, optional ``True`` to prefix the year with the thousands place. default is ``False``. Examples -------- >>> year = Year(5781) >>> year.year_string() תשפ״א >>> year.year_string(True) ה׳תשפ״א """ return _num_to_str(self.year, thousands) class Month: """A Month object represents a month of the Hebrew calendar. It provides the same operators as a `Year` object. Parameters ---------- year : int month : int The month as an integer starting with 7 for Tishrei through 13 if necessary for Adar Sheni and then 1-6 for Nissan - Elul. Attributes ---------- year : int The Hebrew year. month : int The month as an integer starting with 7 for Tishrei through 13 if necessary for Adar Sheni and then 1-6 for Nissan - Elul. name : str The name of the month. .. deprecated:: 1.3.0 `name` attribute will be removed in pyluach 2.0.0, it is replaced by `month_name` method, because the latter allows a `hebrew` parameter. The month_name also uses slightly different transliteration. """ _monthnames = {7: 'Tishrei', 8: 'Cheshvan', 9: 'Kislev', 10: 'Teves', 11: 'Shvat', 13:'Adar Sheni', 1: 'Nissan', 2: 'Iyar', 3: 'Sivan', 4: 'Tamuz', 5: 'Av', 6: 'Elul'} def __init__(self, year, month): if year < 1: raise ValueError('Year is before creation.') self.year = year leap = utils._is_leap(self.year) yearlength = 13 if leap else 12 if month < 1 or month > yearlength: raise ValueError('''Month must be between 1 and 12 for a normal year and 13 for a leap year.''') self.month = month self.name = utils._month_name(self.year, self.month, False) def __repr__(self): return 'Month({0}, {1})'.format(self.year, self.month) def __len__(self): return utils._month_length(self.year, self.month) def __iter__(self): for day in range(1, len(self) + 1): yield day def __eq__(self, other): if( isinstance(other, Month) and self.year == other.year and self.month == other.month): return True return False def __add__(self, other): yearmonths = list(Year(self.year)) index = yearmonths.index(self.month) leftover_months = len(yearmonths[index + 1:]) try: if other <= leftover_months: return Month(self.year, yearmonths[index + other]) return Month(self.year + 1, 7).__add__(other - 1 - leftover_months) except (AttributeError, TypeError): raise TypeError('You can only add a number to a year.') def __sub__(self, other): if isinstance(other, Number): yearmonths = list(Year(self.year)) index = yearmonths.index(self.month) leftover_months = index if other <= leftover_months: return Month(self.year, yearmonths[index - other]) return Month(self.year - 1, deque(Year(self.year - 1), maxlen=1).pop()).__sub__( other - 1 - leftover_months ) # Recursive call on the last month of the previous year. try: return abs(self._elapsed_months() - other._elapsed_months()) except AttributeError: raise TypeError('''You can only subtract a number or a month object from a month.''') def __gt__(self, other): if ( self.year > other.year or (self.year == other.year and self.month > other.month) ): return True return False def __ge__(self, other): if self > other or self == other: return True return False def __lt__(self, other): if ( self.year < other.year or (self.year == other.year and self.month < other.month) ): return True return False def __le__(self, other): if self < other or self == other: return True return False @classmethod def from_date(cls, date): """Return Month object that given date occurs in. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any ``pyluach`` date type Returns ------- Month The Hebrew month the given date occurs in """ heb = date.to_heb() return Month(heb.year, heb.month) @classmethod def from_pydate(cls, pydate): """Return Month object from python date object. Parameters ---------- pydate : ``datetime.date`` A python standard library date object Returns ------- Month The Hebrew month the given date occurs in """ return cls.from_date(HebrewDate.from_pydate(pydate)) def month_name(self, hebrew=False): """Return the name of the month. Replaces `name` attribute. Parameters ---------- hebrew : bool, optional `True` if the month name should be written with Hebrew letters and False to be transliterated into English using the Ashkenazic pronunciation. Default is `False`. Returns ------- str """ return utils._month_name(self.year, self.month, hebrew) def month_string(self, thousands=False): """Return month and year in Hebrew. Parameters ---------- thousands : bool, optional ``True`` to prefix year with thousands place. Default is ``False``. Returns ------- str The month and year in Hebrew in the form ``f'{month} {year}'``. """ return '{} {}'.format( self.month_name(True), _num_to_str(self.year, thousands) ) def starting_weekday(self): """Return first weekday of the month. Returns ------- int The weekday of the first day of the month starting with Sunday as 1 through Saturday as 7. """ return HebrewDate(self.year, self.month, 1).weekday() def _elapsed_months(self): '''Return number of months elapsed from beginning of calendar''' yearmonths = tuple(Year(self.year)) months_elapsed = ( utils._elapsed_months(self.year) + yearmonths.index(self.month) ) return months_elapsed def iterdates(self): """Return iterator that yields an instance of HebrewDate. Yields ------ ``HebrewDate`` The next Hebrew Date of the year starting the first day of Tishrei through the last day of Ellul. """ for day in self: yield HebrewDate(self.year, self.month, day) def molad(self): """Return the month's molad. Returns ------- dict A dictionary in the form {weekday: int, hours: int, parts: int} Note ----- This method does not return the molad in the form that is traditionally announced in the shul. This is the molad in the form used to calculate the length of the year. See Also -------- molad_announcement: The molad as it is traditionally announced. """ months = self._elapsed_months() parts = 204 + months*793 hours = 5 + months*12 + parts//1080 days = 2 + months*29 + hours//24 weekday = days % 7 or 7 return {'weekday': weekday, 'hours': hours % 24, 'parts': parts % 1080} def molad_announcement(self): """Return the months molad in the announcement form. Returns a dictionary in the form that the molad is traditionally announced. The weekday is adjusted to change at midnight and the hour of the day and minutes are given as traditionally announced. Note that the hour is given as in a twenty four hour clock ie. 0 for 12:00 AM through 23 for 11:00 PM. Returns ------- dict A dictionary in the form:: { weekday: int, hour: int, minutes: int, parts: int } """ molad = self.molad() weekday = molad['weekday'] hour = 18 + molad['hours'] if hour < 24: if weekday != 1: weekday -= 1 else: weekday = 7 else: hour -= 24 minutes = molad['parts'] // 18 parts = molad['parts'] % 18 return { 'weekday': weekday, 'hour': hour, 'minutes': minutes, 'parts': parts }pyluach-1.3.0/pyluach/parshios.py000066400000000000000000000163271406020725600170420ustar00rootroot00000000000000"""The parshios module has functions to find the weekly parasha. Examples -------- >>> from pyluach import dates, parshios >>> date = dates.HebrewDate(5781, 10, 5) >>> parshios.getparsha(date) 'Vayigash' >>> parshios.getparsha_string(date, hebrew=True) 'ויגש' >>> parshios.getparsha_string(dates.GregorianDate(2021, 3, 7), hebrew=True) 'ויקהל, פקודי' Note ---- The algorithm is based on Dr. Irv Bromberg's, University of Toronto at http://individual.utoronto.ca/kalendis/hebrew/parshah.htm All parsha names are transliterated into the American Ashkenazik pronunciation. Attributes ---------- PARSHIOS : list of str A list of the parshios transliterated into English. PARSHIOS_HEBREW : list of str A list of the parshios in Hebrew. """ from collections import deque, OrderedDict from functools import lru_cache from pyluach.dates import HebrewDate from pyluach.utils import _is_leap PARSHIOS = [ 'Bereishis', 'Noach', 'Lech Lecha', 'Vayeira', 'Chayei Sarah', 'Toldos', 'Vayeitzei', 'Vayishlach', 'Vayeishev', 'Mikeitz', 'Vayigash', 'Vayechi', 'Shemos', "Va'eira", 'Bo', 'Beshalach', 'Yisro', 'Mishpatim', 'Terumah', 'Tetzaveh', 'Ki Sisa', 'Vayakhel', 'Pekudei', 'Vayikra', 'Tzav', 'Shemini', 'Tazria', 'Metzora', 'Acharei Mos', 'Kedoshim', 'Emor', 'Behar', 'Bechukosai', 'Bamidbar', 'Nasso', "Beha'aloscha", 'Shelach', 'Korach', 'Chukas', 'Balak', 'Pinchas', 'Mattos', 'Masei', 'Devarim', "Va'eschanan", 'Eikev', "Re'eh", 'Shoftim', 'Ki Seitzei', 'Ki Savo', 'Nitzavim', 'Vayeilech', 'Haazinu', 'Vezos Haberachah' ] PARSHIOS_HEBREW = [ 'בראשית', 'נח', 'לך לך', 'וירא', 'חיי שרה', 'תולדות', 'ויצא', 'וישלח', 'וישב', 'מקץ', 'ויגש', 'ויחי', 'שמות', 'וארא', 'בא', 'בשלח', 'יתרו', 'משפטים', 'תרומה', 'תצוה', 'כי תשא', 'ויקהל', 'פקודי', 'ויקרא', 'צו', 'שמיני', 'תזריע', 'מצורע', 'אחרי מות', 'קדושים', 'אמור', 'בהר', 'בחוקותי', 'במדבר', 'נשא', 'בהעלותך', 'שלח', 'קרח', 'חקת', 'בלק', 'פינחס', 'מטות', 'מסעי', 'דברים', 'ואתחנן', 'עקב', 'ראה', 'שופטים', 'כי תצא', 'כי תבא', 'נצבים', 'וילך', 'האזינו', 'וזאת הברכה' ] def _parshaless(date, israel=False): if israel and date.tuple()[1:] in [(7,23), (1,22), (3,7)]: return False if date.month == 7 and date.day in ([1,2,10] + list(range(15, 24))): return True if date.month == 1 and date.day in range(15, 23): return True if date.month == 3 and date.day in [6, 7]: return True return False @lru_cache(maxsize=50) def _gentable(year, israel=False): """Return OrderedDict mapping date of Shabbos to list of parsha numbers. The numbers start with Beraishis as 0. Double parshios are represented as a list of the two numbers. If there is no Parsha the value is None. """ parshalist = deque([51, 52] + list(range(52))) table = OrderedDict() leap = _is_leap(year) pesachday = HebrewDate(year, 1, 15).weekday() rosh_hashana = HebrewDate(year, 7, 1) shabbos = rosh_hashana.shabbos() if rosh_hashana.weekday() > 4: parshalist.popleft() while shabbos.year == year: if _parshaless(shabbos, israel): table[shabbos] = None else: parsha = parshalist.popleft() table[shabbos] = [parsha,] if( (parsha == 21 and (HebrewDate(year, 1, 14)-shabbos) // 7 < 3) or (parsha in [26, 28] and not leap) or (parsha == 31 and not leap and ( not israel or pesachday != 7 )) or (parsha == 38 and not israel and pesachday == 5) or (parsha == 41 and (HebrewDate(year, 5, 9)-shabbos) // 7 < 2) or (parsha == 50 and HebrewDate(year+1, 7, 1).weekday() > 4) ): # If any of that then it's a double parsha. table[shabbos].append(parshalist.popleft()) shabbos += 7 return table def getparsha(date, israel=False): """Return the parsha for a given date. Returns the parsha for the Shabbos on or following the given date. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` This date does not have to be a Shabbos. israel : bool, optional ``True`` if you want the parsha according to the Israel schedule (with only one day of Yom Tov). Defaults to ``False``. Returns ------- list of ints or ``None`` A list of the numbers of the parshios for the Shabbos of the given date, beginning with 0 for Beraishis, or ``None`` if the Shabbos doesn't have a parsha (i.e. it's on Yom Tov). """ shabbos = date.to_heb().shabbos() table = _gentable(shabbos.year, israel) return table[shabbos] def getparsha_string(date, israel=False, hebrew=False): """Return the parsha as a string for the given date. This function wraps ``getparsha`` returning the parsha name. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` This date does not have to be a Shabbos. israel : bool, optional ``True`` if you want the parsha according to the Israel schedule (with only one day of Yom Tov). Defaults to ``False``. hebrew : bool, optional ``True`` if you want the name of the parsha in Hebrew. Defaults to ``False``. Returns ------- str or ``None`` The name of the parsha separated by a comma and space if it is a double parsha or ``None`` if there is no parsha that Shabbos (ie. it's yom tov). """ parsha = getparsha(date, israel) if parsha is None: return None if not hebrew: name = [PARSHIOS[n] for n in parsha] else: name = [PARSHIOS_HEBREW[n] for n in parsha] return ', '.join(name) def iterparshios(year, israel=False): """Generate all the parshios in the year. Parameters ---------- year : int The Hebrew year to get the parshios for. israel : bool, optional ``True`` if you want the parsha according to the Israel schedule (with only one day of Yom Tov). Defaults to ``False`` Yields ------ list of ints or ``None`` A list of the numbers of the parshios for the next Shabbos in the given year. Yields ``None`` for a Shabbos that doesn't have its own parsha (i.e. it occurs on a yom tov). """ table = _gentable(year, israel) for shabbos in table.values(): yield shabbos def parshatable(year, israel=False): """Return a table of all the Shabbosos in the year Parameters ---------- year : int The Hebrew year to get the parshios for. israel : bool, optional ``True`` if you want the parshios according to the Israel schedule (with only one day of Yom Tov). Defaults to ``False``. Returns ------- OrderedDict An ordered dictionary with the date of each Shabbos as the key mapped to the parsha as a list of ints, or ``None`` for a Shabbos with no parsha. """ return _gentable(year, israel)pyluach-1.3.0/pyluach/utils.py000066400000000000000000000167131406020725600163510ustar00rootroot00000000000000from functools import lru_cache MONTH_NAMES = [ 'Nissan', 'Iyar', 'Sivan', 'Tammuz', 'Av', 'Elul', 'Tishrei', 'Cheshvan', 'Kislev', 'Teves', 'Shevat', 'Adar', 'Adar 1', 'Adar 2' ] MONTH_NAMES_HEBREW = [ 'ניסן', 'אייר', 'סיון', 'תמוז', 'אב', 'אלול', 'תשרי', 'חשון', 'כסלו', 'טבת', 'שבט', 'אדר', 'אדר א׳', 'אדר ב׳' ] FAST_DAYS = [ 'Tzom Gedalia', '10 of Teves', 'Taanis Esther', '17 of Tamuz', '9 of Av' ] FAST_DAYS_HEBREW = [ 'צום גדליה', 'י׳ בטבת', 'תענית אסתר', 'י״ז בתמוז', 'ט׳ באב' ] FESTIVALS = [ 'Rosh Hashana', 'Yom Kippur', 'Succos', 'Shmini Atzeres', 'Simchas Torah', 'Chanuka', "Tu B'shvat", 'Purim Katan', 'Purim', 'Shushan Purim', 'Pesach', 'Pesach Sheni', "Lag Ba'omer", 'Shavuos', "Tu B'av" ] FESTIVALS_HEBREW = [ 'ראש השנה', 'יום כיפור', 'סוכות', 'שמיני עצרת', 'שמחת תורה', 'חנוכה', 'ט״ו בשבט', 'פורים קטן', 'פורים', 'שושן פורים', 'פסח', 'פסח שני', 'ל״ג בעומר', 'שבועות', 'ט״ו באב' ] def _is_leap(year): if (((7*year) + 1) % 19) < 7: return True return False def _elapsed_months(year): return (235 * year - 234) // 19 @lru_cache(maxsize=100) def _elapsed_days(year): months_elapsed = _elapsed_months(year) parts_elapsed = 204 + 793*(months_elapsed%1080) hours_elapsed = (5 + 12*months_elapsed + 793*(months_elapsed//1080) + parts_elapsed//1080) conjunction_day = 1 + 29*months_elapsed + hours_elapsed//24 conjunction_parts = 1080 * (hours_elapsed%24) + parts_elapsed%1080 if ( (conjunction_parts >= 19440) or ( (conjunction_day % 7 == 2) and (conjunction_parts >= 9924) and (not _is_leap(year)) ) or ( (conjunction_day % 7 == 1) and conjunction_parts >= 16789 and _is_leap(year - 1))): # if all that alt_day = conjunction_day + 1 else: alt_day = conjunction_day if (alt_day % 7) in (0, 3, 5): alt_day += 1 return alt_day def _days_in_year(year): return _elapsed_days(year + 1) - _elapsed_days(year) def _long_cheshvan(year): """Returns True if Cheshvan has 30 days""" return _days_in_year(year) % 10 == 5 def _short_kislev(year): """Returns True if Kislev has 29 days""" return _days_in_year(year) % 10 == 3 def _month_length(year, month): """Months start with Nissan (Nissan is 1 and Tishrei is 7)""" if month in [1, 3, 5, 7, 11]: return 30 elif month in [2, 4, 6, 10, 13]: return 29 elif month == 12: return 30 if _is_leap(year) else 29 elif month == 8: # if long Cheshvan return 30, else return 29 return 30 if _long_cheshvan(year) else 29 elif month == 9: # if short Kislev return 29, else return 30 return 29 if _short_kislev(year) else 30 def _month_name(year, month, hebrew): index = month if month < 12 or not _is_leap(year): index -=1 if hebrew: return MONTH_NAMES_HEBREW[index] return MONTH_NAMES[index] def _fast_day(date): """Return name of fast day or None. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. Returns ------- str or ``None`` The name of the fast day or ``None`` if the given date is not a fast day. """ date = date.to_heb() year = date.year month = date.month day = date.day weekday = date.weekday() adar = 13 if _is_leap(year) else 12 if month == 7: if (weekday == 1 and day == 4) or (weekday != 7 and day == 3): return 0 elif month == 10 and day == 10: return 1 elif month == adar: if (weekday == 5 and day == 11) or weekday != 7 and day == 13: return 2 elif month == 4: if (weekday == 1 and day == 18) or (weekday != 7 and day == 17): return 3 elif month == 5: if (weekday == 1 and day == 10) or (weekday != 7 and day == 9): return 4 return None def _fast_day_string(date, hebrew=False): fast = _fast_day(date) if fast is None: return None if hebrew: return FAST_DAYS_HEBREW[fast] return FAST_DAYS[fast] def _festival(date, israel=False): """Return Jewish festival of given day. This method will return all major and minor religous Jewish holidays not including fast days. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. israel : bool, optional ``True`` if you want the holidays according to the Israel schedule. Defaults to ``False``. Returns ------- str or ``None`` The name of the festival or ``None`` if the given date is not a Jewish festival. """ date = date.to_heb() year = date.year month = date.month day = date.day if month == 7: if day in [1, 2]: return 0 elif day == 10: return 1 elif day in range(15, 22): return 2 elif day == 22: return 3 elif day == 23 and israel == False: return 4 elif(month in [9, 10]): kislev_length = _month_length(year, 9) if ( month == 9 and day in range(25, kislev_length + 1) or month == 10 and day in range(1, 8 - (kislev_length - 25)) ): return 5 elif month == 11 and day == 15: return 6 elif month == 12: leap = _is_leap(year) if day == 14: return 7 if leap else 8 if day == 15 and not leap: return 9 elif month == 13: if day == 14: return 8 elif day == 15: return 9 elif month == 1 and day in range(15, 22 if israel else 23): return 10 elif month == 2 and day == 14: return 11 elif month == 2 and day == 18: return 12 elif month == 3 and (day == 6 if israel else day in (6, 7)): return 13 elif month == 5 and day == 15: return 14 return None def _festival_string(date, israel=False, hebrew=False): festival = _festival(date, israel) if festival is None: return None if hebrew: return FESTIVALS_HEBREW[festival] return FESTIVALS[festival] def _holiday(date, israel=False, hebrew=False): """Return Jewish holiday of given date. The holidays include the major and minor religious Jewish holidays including fast days. Parameters ---------- date : ``HebrewDate``, ``GregorianDate``, or ``JulianDay`` Any date that implements a ``to_heb()`` method which returns a ``HebrewDate`` can be used. israel : bool, optional ``True`` if you want the holidays according to the israel schedule. Defaults to ``False``. hebrew : bool, optional ``True`` if you want the holiday name in Hebrew letters. Default is ``False``. Returns ------- str or ``None`` The name of the holiday or ``None`` if the given date is not a Jewish holiday. """ festival = _festival_string(date, israel, hebrew) if festival is not None: return festival fast = _fast_day_string(date, hebrew) return fastpyluach-1.3.0/requirements.txt000066400000000000000000000021761406020725600164540ustar00rootroot00000000000000alabaster==0.7.12 atomicwrites==1.4.0 attrs==19.3.0 Babel==2.8.0 backcall==0.2.0 bleach==3.3.0 build==0.4.0 certifi==2020.4.5.1 cffi==1.14.5 chardet==3.0.4 colorama==0.4.3 cryptography==3.4.6 decorator==4.4.2 docutils==0.16 idna==2.9 imagesize==1.2.0 importlib-metadata==4.5.0 ipython==7.21.0 ipython-genutils==0.2.0 jedi==0.18.0 jeepney==0.6.0 Jinja2==3.0.1 keyring==21.2.1 MarkupSafe==2.0.1 more-itertools==8.3.0 packaging==20.4 parso==0.8.1 pep517==0.10.0 pexpect==4.8.0 pickleshare==0.7.5 pkginfo==1.5.0.1 pluggy==0.13.1 prompt-toolkit==3.0.18 ptyprocess==0.7.0 py==1.10.0 pycparser==2.20 Pygments==2.9.0 pyparsing==2.4.7 pytest==5.4.2 pytz==2020.1 pywin32-ctypes==0.2.0 readme-renderer==26.0 requests==2.25.1 requests-toolbelt==0.9.1 rfc3986==1.5.0 SecretStorage==3.3.1 six==1.15.0 snowballstemmer==2.0.0 Sphinx==3.0.3 sphinx-rtd-theme==0.4.3 sphinxcontrib-applehelp==1.0.2 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==1.0.3 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.4 toml==0.10.2 tqdm==4.46.0 traitlets==5.0.5 twine==3.4.1 urllib3==1.26.5 wcwidth==0.1.9 webencodings==0.5.1 zipp==3.1.0 pyluach-1.3.0/setup.py000066400000000000000000000027061406020725600147010ustar00rootroot00000000000000from setuptools import setup import io with open('README.rst', 'r', encoding='utf-8') as readme: long_description = readme.read() setup(name='pyluach', version='1.3.0', author='MS List', author_email='simlist@gmail.com', packages=['pyluach', ], url='https://github.com/simlist/pyluach', license='MIT', description=("""Pyluach is a Python package for manipulating Hebrew dates, Gregorian-Hebrew calendar conversions, getting the weekly parsha, and other Jewish calendar related calculations.""" ), long_description=long_description, python_requires=">=3.4", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', ], keywords=['hebrew', 'calendar', 'jewish', 'luach', 'gregorian', 'julian', 'days', 'dates', 'date', 'conversion', 'parsha', 'holiday'] ) pyluach-1.3.0/tests/000077500000000000000000000000001406020725600143245ustar00rootroot00000000000000pyluach-1.3.0/tests/__init__.py000066400000000000000000000000001406020725600164230ustar00rootroot00000000000000pyluach-1.3.0/tests/test_dates.py000066400000000000000000000220611406020725600170360ustar00rootroot00000000000000import pytest from operator import gt, lt, eq, ne, ge, le, add, sub import datetime from pyluach import dates from pyluach.dates import HebrewDate, GregorianDate, JulianDay KNOWN_VALUES = {(2009, 8, 21): (5769, 6, 1), (2009, 9, 30): (5770, 7, 12), (2009, 11, 13): (5770, 8, 26), (2010, 1, 21): (5770, 11, 6), (2010, 5, 26): (5770, 3, 13), (2013, 11, 17): (5774, 9, 14), (2014, 3, 12): (5774, 13, 10), (2014, 6, 10): (5774, 3, 12), (2016, 2, 10): (5776, 12, 1) } @pytest.fixture(scope='module') def datetypeslist(): datetypes = [dates.HebrewDate, dates.GregorianDate] return datetypes class TestClassesSanity: def test_greg_sanity(self): for i in range(347998, 2460000, 117): jd = dates.JulianDay(i) conf = jd.to_greg().to_jd() if jd >= dates.GregorianDate(1, 1, 1): assert jd.day == conf.day else: assert abs(jd.day - conf.day) <= 1 def test_heb_sanity(self): for i in range(347998, 2460000, 117): jd = dates.JulianDay(i) conf = jd.to_heb().to_jd() assert jd.day == conf.day class TestClassesConversion: def test_from_greg(self): for date in KNOWN_VALUES: heb = dates.GregorianDate(*date).to_heb().tuple() assert KNOWN_VALUES[date] == heb def test_from_heb(self): for date in KNOWN_VALUES: greg = dates.HebrewDate(*KNOWN_VALUES[date]).to_greg().tuple() assert date == greg @pytest.fixture def setup(scope='module'): caltypes = [GregorianDate, HebrewDate, JulianDay] deltas = [0, 1, 29, 73, 1004] return {'caltypes': caltypes, 'deltas': deltas} class TestOperators: def test_add(self, setup): for cal in setup['caltypes']: for delta in setup['deltas']: date = cal.today() date2 = date + delta assert date.jd + delta == date2.jd def test_min_int(self, setup): '''Test subtracting a number from a date''' for cal in setup['caltypes']: for delta in setup['deltas']: date = cal.today() date2 = date - delta assert date.jd - delta == date2.jd def test_min_date(self, setup): '''Test subtracting one date from another This test loops through subtracting the current date of each calendar from a date of each calendar at intervals from the current date. ''' for cal in setup['caltypes']: for cal2 in setup['caltypes']: for delta in setup['deltas']: today = cal.today() difference = (cal2.today() - delta) - today assert delta == difference class TestComparisons: """In ComparisonTests, comparisons are tested. Every function tests one test case comparing a date from each calendar type to another date from each calendar type. """ def test_gt(self, setup): """Test all comparers when one date is greater.""" for cal in setup['caltypes']: today = cal.today() for cal2 in setup['caltypes']: yesterday = cal2.today() - 1 for comp in [gt, ge, ne]: assert comp(today, yesterday) for comp in [eq, lt, le]: assert comp(today, yesterday) is False def test_lt(self, setup): """Test all comparers when one date is less than another.""" for cal in setup['caltypes']: today = cal.today() for cal2 in setup['caltypes']: tomorrow = cal2.today() + 1 for comp in [lt, le, ne]: assert comp(today, tomorrow) for comp in [gt, ge, eq]: assert comp(today, tomorrow) is False def test_eq(self, setup): """Test all comparers when the dates are equal.""" for cal in setup['caltypes']: today = cal.today() for cal2 in setup['caltypes']: today2 = cal2.today() for comp in [eq, ge, le]: assert comp(today, today2) for comp in [gt, lt, ne]: assert comp(today, today2) is False class TestErrors: def test_too_low_heb(self): with pytest.raises(ValueError): dates.HebrewDate(0, 7, 1) with pytest.raises(ValueError): dates.HebrewDate(-1, 7, 1) def test_comparer_errors(self): day1 = dates.HebrewDate(5777, 12, 10) for date in [day1, day1.to_greg(), day1.to_jd()]: for comparer in [gt, lt, eq, ne, ge, le]: for value in [1, 0, 'hello', None, '']: with pytest.raises(TypeError): comparer(date, value) def test_operator_errors(self): day = dates.GregorianDate(2016, 11, 20) for operator in [add, sub]: for value in ['Hello', '', None]: with pytest.raises(TypeError): operator(day, value) with pytest.raises(TypeError): day + (day+1) def test_HebrewDate_errors(self): with pytest.raises(ValueError): HebrewDate(0, 6, 29) for datetuple in [(5778, 0, 5), (5779, -1, 7), (5759, 14, 8), (5778, 13, 20)]: with pytest.raises(ValueError): HebrewDate(*datetuple) for datetuple in [(5778, 6, 0), (5779, 8, 31), (5779, 10, 30)]: with pytest.raises(ValueError): HebrewDate(*datetuple) def test_GregorianDate_errors(self): for datetuple in [(2018, 0, 3), (2018, -2, 8), (2018, 13, 9), (2018, 2, 0), (2018, 2, 29), (2012, 2, 30)]: with pytest.raises(ValueError): GregorianDate(*datetuple) def test_JD_errors(self): with pytest.raises(ValueError): JulianDay(-1).to_heb() class TestReprandStr: def test_repr(self, datetypeslist): for datetype in datetypeslist: assert eval(repr(datetype.today())) == datetype.today() jd = JulianDay.today() assert eval(repr(jd)) == jd def test_jd_str(self): assert str(JulianDay(550.5)) == '550.5' assert str(JulianDay(1008)) == '1007.5' def test_greg_str(self): date = GregorianDate(2018, 8, 22) assert str(date) == '2018-08-22' assert str(GregorianDate(2008, 12, 2)) == '2008-12-02' assert str(GregorianDate(1, 1, 1)) == '0001-01-01' def test_weekday(): assert GregorianDate(2017, 8, 7).weekday() == 2 assert HebrewDate(5777, 6, 1).weekday() == 4 assert JulianDay(2458342.5).weekday() == 1 def test_isoweekday(): assert GregorianDate(2020, 9, 20).isoweekday() == 7 assert GregorianDate(2020, 10, 3).isoweekday() == 6 assert GregorianDate(2020, 10, 5).isoweekday() == 1 assert JulianDay(2458342.5).isoweekday() == 7 class TestMixinMethods: @pytest.fixture def date(self): return dates.GregorianDate(2017, 10, 31) def test_str(self, date): assert str(date) == '2017-10-31' def test_dict(self, date): assert date.dict() == {'year': 2017, 'month': 10, 'day': 31} def test_iter(self, date): assert list(date) == [date.year, date.month, date.day] class TestHolidayMethods: def test_fast_day(self): date = dates.HebrewDate(5781, 7, 3) assert date.holiday() == 'Tzom Gedalia' assert date.holiday(False, True) == 'צום גדליה' assert date.fast_day() == 'Tzom Gedalia' assert date.fast_day(True) == 'צום גדליה' def test_festival(self): date = dates.GregorianDate(2020, 12, 11) assert date.holiday() == 'Chanuka' assert date.holiday(hebrew=True) == 'חנוכה' assert date.festival() == 'Chanuka' assert date.festival(hebrew=True) == 'חנוכה' def test_to_pydate(): day = HebrewDate(5778, 6, 1) jd = day.to_jd() for day_type in [day, jd]: assert day_type.to_pydate() == datetime.date(2018, 8, 12) def test_from_pydate(): date = datetime.date(2018, 8, 27) assert date == GregorianDate.from_pydate(date).to_jd().to_pydate() assert date == HebrewDate.from_pydate(date).to_pydate() assert date == JulianDay.from_pydate(date).to_pydate() def test_is_leap(): assert GregorianDate(2020, 10, 26).is_leap() == True assert GregorianDate(2021, 10, 26).is_leap() == False def test_hebrew_date_string(): date = HebrewDate(5782, 7, 1) assert date.hebrew_date_string() == 'א׳ תשרי תשפ״ב' assert date.hebrew_date_string(True) == 'א׳ תשרי ה׳תשפ״ב' def test_month_name(): date = HebrewDate(5781, 12, 14) assert date.month_name() == 'Adar' assert date.month_name(True) == 'אדר' date2 = HebrewDate(5782, 12, 14) assert date2.month_name() == 'Adar 1' assert date2.month_name(True) == 'אדר א׳'pyluach-1.3.0/tests/test_gematria.py000066400000000000000000000014761406020725600175360ustar00rootroot00000000000000from pyluach.gematria import _num_to_str def test_one_letter(): assert _num_to_str(5) == 'ה׳' assert _num_to_str(10) == 'י׳' assert _num_to_str(200) == 'ר׳' def test_two_letters(): assert _num_to_str(18) == 'י״ח' assert _num_to_str(15) == 'ט״ו' assert _num_to_str(16) == 'ט״ז' assert _num_to_str(101) == 'ק״א' def test_three_letters(): assert _num_to_str(127) == 'קכ״ז' assert _num_to_str(489) == 'תפ״ט' assert _num_to_str(890) == 'תת״צ' def test_four_letters(): assert _num_to_str(532) == 'תקל״ב' def test_five_letters(): assert _num_to_str(916) == 'תתקט״ז' def test_thousands(): assert _num_to_str(5781, True) == 'ה׳תשפ״א' assert _num_to_str(10000, True) == 'י׳' assert _num_to_str(12045, True) == 'יב׳מ״ה'pyluach-1.3.0/tests/test_hebrewcal.py000066400000000000000000000306231406020725600176750ustar00rootroot00000000000000import datetime from copy import copy from pytest import fixture, raises from pyluach import dates, hebrewcal from pyluach.hebrewcal import Year, Month, holiday, festival, fast_day class TestYear: def test_repryear(self): year = Year(5777) assert eval(repr(year)) == year def test_iteryear(self): assert list(Year(5777)) == [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6] assert list(Year(5776)) == [7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6] def test_equalyear(self): year1 = hebrewcal.Year(5777) year2 = hebrewcal.Year(5777) assert year1 == year2 def test_addtoyear(self): year = Year(5777) assert year + 2 == Year(5779) assert year + 0 == year with raises(TypeError): year + year with raises(TypeError): year + 'str' def test_subtractintfromyear(self): year = Year(5777) assert year - 0 == year assert year - 3 == Year(5774) with raises(TypeError): year - 'str' def test_subtractyearfromyear(self): year = Year(5777) assert year - year == 0 assert year - (year - 1) == 1 assert year - (year + 2) == 2 def test_iterdays(self): year = Year(5778) yearlist = list(year.iterdays()) assert len(yearlist) == len(year) assert yearlist[0] == 1 assert yearlist[-1] == len(year) def test_iterdates(self): year = 5778 workingdate = dates.HebrewDate(year, 7, 1) for date in Year(year).iterdates(): assert workingdate == date workingdate += 1 def test_errors(self): with raises(ValueError): Year(0) def test_year_string(self): year = Year(5781) assert year.year_string() == 'תשפ״א' assert year.year_string(True) == 'ה׳תשפ״א' def test_from_date(self): date = dates.GregorianDate(2021, 6, 7) year = Year.from_date(date) assert year == Year(date.to_heb().year) def test_from_pydate(self): pydate = datetime.date(2021, 6, 7) date = dates.HebrewDate.from_pydate(pydate) assert Year.from_pydate(pydate) == Year(date.year) @fixture def years(): year1 = Year(5778) year2 = Year(5780) return {1: year1, 2: year2} class TestYearComparisons: def test_year_equals(self, years): assert years[1] == copy(years[1]) assert (years[1] == years[2]) is False assert years[2] != years[1] assert (copy(years[2]) != years[2]) is False def test_year_gt(self, years): assert years[2] > years[1] assert (years[1] > years[1]) is False def test_years_ge(self, years): assert copy(years[1]) >= years[1] assert years[2] >= years[1] assert (years[1] >= years[2]) is False def test_years_lt(self, years): assert years[1] < years[2] assert (copy(years[2]) < years[2]) is False assert (years[2] < years[1]) is False def test_years_le(self, years): assert copy(years[1]) <= years[1] assert years[1] <= years[2] assert (years[2] <= years[1]) is False class TestMonth: def test_reprmonth(self): month = Month(5777, 10) assert eval(repr(month)) == month def test_equalmonth(self): month1 = hebrewcal.Month(5777, 12) month2 = hebrewcal.Month(5777, 12) assert month1 == month2 assert not month1 == (month2 + 1) def test_addinttomonth(self): month = hebrewcal.Month(5777, 12) assert month + 0 == month assert month + 1 == hebrewcal.Month(5777, 1) assert month + 6 == hebrewcal.Month(5777, 6) assert month + 7 == hebrewcal.Month(5778, 7) assert month + 35 == hebrewcal.Month(5780, 10) with raises(TypeError): month + month with raises(TypeError): month + 'str' def test_subtract_month(self): month1 = hebrewcal.Month(5775, 10) month2 = hebrewcal.Month(5776, 10) month3 = hebrewcal.Month(5777, 10) assert month1 - month2 == 12 assert month3 - month1 == 25 def test_subtractintfrommonth(self): month = hebrewcal.Month(5778, 9) assert month - 2 == hebrewcal.Month(5778, 7) assert month - 3 == hebrewcal.Month(5777, 6) assert month - 30 == hebrewcal.Month(5775, 4) with raises(TypeError): month - 'str' def test_startingweekday(self): assert Month(5778, 8).starting_weekday() == 7 assert Month(5778, 9).starting_weekday() == 1 def test_iterdate(self): year = 5770 workingdate = dates.HebrewDate(year, 7 ,1) for month in (list(range(7, 13)) + list(range(1, 7))): for date in Month(year, month).iterdates(): assert date == workingdate workingdate += 1 def test_molad(self): month = Month(5779, 7) assert month.molad() == {'weekday': 2, 'hours':14, 'parts': 316} month = Month(5779, 5) assert month.molad() == {'weekday':5, 'hours': 10, 'parts': 399} def test_molad_announcement(self): month = Month(5780, 3) assert month.molad_announcement() == { 'weekday': 6, 'hour': 11, 'minutes':42, 'parts': 13 } month = Month(5780, 2) assert month.molad_announcement() == { 'weekday': 4, 'hour': 22, 'minutes': 58, 'parts': 12 } month = Month(5780, 8) assert month.molad_announcement() == { 'weekday': 2, 'hour': 18, 'minutes': 34, 'parts': 6 } month = Month(5780, 12) assert month.molad_announcement() == { 'weekday': 1, 'hour':21, 'minutes': 30, 'parts': 10 } month = Month(5781, 1) assert month.molad_announcement() == { 'weekday': 7, 'hour': 19, 'minutes': 3, 'parts': 5 } month = Month(5781, 8) assert month.molad_announcement() == { 'weekday': 7, 'hour': 3, 'minutes': 23, 'parts': 0 } def test_month_name(self): month = Month(5781, 9) assert month.month_name() == 'Kislev' assert month.month_name(hebrew=True) == 'כסלו' adar = Month(5781, 12) assert adar.month_name() == 'Adar' adar_bais = Month(5782, 13) assert adar_bais.month_name() == 'Adar 2' def test_month_string(self): month = Month(5781, 3) assert month.month_string() == 'סיון תשפ״א' assert month.month_string(True) == 'סיון ה׳תשפ״א' def test_errors(self): with raises(ValueError): Month(-1, 1) with raises(ValueError): Month(5781, 13) def test_from_date(self): date = dates.HebrewDate(5781, 7, 10) assert Month.from_date(date) == Month(date.year, date.month) def test_from_pydate(self): pydate = datetime.date(2021, 6, 7) date = dates.HebrewDate.from_pydate(pydate) assert Month.from_pydate(pydate) == Month(date.year, date.month) @fixture def months(): month1 = Month(5780, 3) month2 = Month(5780, 4) month3 = Month(5781, 3) return {1: month1, 2: month2, 3: month3} class TestCompareMonth: def test_month_gt(self, months): assert months[2] > months[1] assert (months[1] > months[2]) is False assert months[3] > months[1] assert (months[2] > months[3]) is False def test_month_ge(self, months): assert copy(months[1]) >= months[1] assert months[2] >= months[1] assert (months[2] >= months[3]) is False def test_month_lt(self, months): assert (copy(months[2]) < months[2]) is False assert months[1] < months[2] assert months[2] < months[3] assert (months[3] < months[1]) is False def test_month_le(self, months): assert copy(months[2]) <= months[2] assert months[1] <= months[2] assert (months[3] <= months[2]) is False def rest_month_ne(self, months): assert months[2] != months[1] assert months[3] != months[1] assert (copy(months[1]) != months[1]) is False class TestHoliday: def test_roshhashana(self): roshhashana = dates.HebrewDate(5779, 7, 1) assert all([holiday(day, location) == 'Rosh Hashana' for day in[roshhashana, roshhashana + 1] for location in [True, False] ]) def test_yomkippur(self): yom_kippur = dates.HebrewDate(5775, 7, 10) assert holiday(yom_kippur) == 'Yom Kippur' assert holiday(yom_kippur, hebrew=True) == 'יום כיפור' def test_succos(self): day = dates.HebrewDate(5778, 7, 18) assert festival(day) == 'Succos' day2 = dates.HebrewDate(5778, 7, 23) assert festival(day2, israel=True, hebrew=True) is None def test_shmini(self): shmini = dates.HebrewDate(5780, 7, 22) assert holiday(shmini, True) == 'Shmini Atzeres' assert holiday(shmini) == 'Shmini Atzeres' assert holiday(shmini + 1) == 'Simchas Torah' assert holiday(shmini + 1, True) is None def test_chanuka(self): for year in [5778, 5787]: chanuka = dates.HebrewDate(year, 9, 25) for i in range(8): assert holiday(chanuka + i) == 'Chanuka' assert holiday(chanuka + 8) is None def test_tubshvat(self): assert holiday(dates.HebrewDate(5779, 11, 15)) == "Tu B'shvat" def test_purim(self): purims = [dates.HebrewDate(5778, 12, 14), dates.HebrewDate(5779, 13, 14)] for purim in purims: assert holiday(purim, hebrew=True) == 'פורים' assert holiday(purim + 1) == 'Shushan Purim' assert holiday(dates.HebrewDate(5779, 12, 14)) == 'Purim Katan' def test_pesach(self): pesach = dates.HebrewDate(5778, 1, 15) for i in range (6): assert ( holiday(pesach + i, True) == 'Pesach' and holiday(pesach + i) == 'Pesach' ) eighth = pesach + 7 assert holiday(eighth) == 'Pesach' and holiday(eighth, True) is None assert holiday(eighth + 1) is None def test_pesach_sheni(self): ps = dates.HebrewDate(5781, 2, 14) assert holiday(ps) == 'Pesach Sheni' assert holiday(ps + 1) is None def test_lagbaomer(self): lag_baomer = dates.GregorianDate(2018, 5, 3) assert festival(lag_baomer) == "Lag Ba'omer" assert festival(lag_baomer, hebrew=True) == 'ל״ג בעומר' def test_shavuos(self): shavuos = dates.HebrewDate(5778, 3, 6) assert all([holiday(day) == 'Shavuos' for day in [shavuos, shavuos + 1]]) assert holiday(shavuos, True) == 'Shavuos' assert holiday(shavuos + 1, True) is None def test_tubeav(self): assert holiday(dates.HebrewDate(5779, 5, 15)) == "Tu B'av" class TestFasts: def test_gedalia(self): assert fast_day(dates.HebrewDate(5779, 7, 3)) == 'Tzom Gedalia' assert holiday(dates.HebrewDate(5778, 7, 3)) is None assert holiday(dates.HebrewDate(5778, 7, 4), hebrew=True) == 'צום גדליה' def test_asara(self): ten_of_teves = dates.GregorianDate(2018, 12, 18) assert holiday(ten_of_teves) == '10 of Teves' assert fast_day(ten_of_teves, hebrew=True) == 'י׳ בטבת' def test_esther(self): fasts = [ dates.HebrewDate(5778, 12, 13), dates.HebrewDate(5776, 13, 13), dates.HebrewDate(5777, 12, 11), #nidche dates.HebrewDate(5784, 13, 11) #ibbur and nidche ] for fast in fasts: assert holiday(fast) == 'Taanis Esther' non_fasts = [ dates.HebrewDate(5776, 12, 13), dates.HebrewDate(5777, 12, 13), dates.HebrewDate(5784, 12, 11), dates.HebrewDate(5784, 13, 13) ] for non in non_fasts: assert holiday(non) is None def test_tamuz(self): fasts = [dates.HebrewDate(5777, 4, 17), dates.HebrewDate(5778, 4, 18)] for fast in fasts: assert holiday(fast) == '17 of Tamuz' assert holiday(dates.HebrewDate(5778, 4, 17)) is None def test_av(self): fasts = [dates.HebrewDate(5777, 5, 9), dates.HebrewDate(5778, 5, 10)] for fast in fasts: assert holiday(fast) == '9 of Av' assert holiday(dates.HebrewDate(5778, 5, 9)) is None pyluach-1.3.0/tests/test_parshios.py000066400000000000000000000050601406020725600175660ustar00rootroot00000000000000import pytest from pyluach import parshios, dates KNOWN_VALUES = { (2016, 1, 7): [13,], (2017, 3, 21): [21, 22], (2017, 9, 26): None, (2020, 9, 19): None, } KNOWN_VALUES_STRINGS = { (2016, 1, 7): "Va'eira", (2017, 3, 21): "Vayakhel, Pekudei", (2017, 9, 26): None } class TestGetParsha: def test_getparsha(self): for key in KNOWN_VALUES: assert (parshios.getparsha(dates.GregorianDate(*key)) == KNOWN_VALUES[key]) def test_getparsha_string(self): for key in KNOWN_VALUES_STRINGS: assert (parshios.getparsha_string(dates.GregorianDate(*key)) == KNOWN_VALUES_STRINGS[key]) def test_chukas_balak(self): chukas_balak = dates.HebrewDate(5780, 4, 12) assert parshios.getparsha(chukas_balak) == [38, 39] assert parshios.getparsha(chukas_balak, True) == [39, ] assert parshios.getparsha(chukas_balak - 8) == [37, ] assert parshios.getparsha(chukas_balak - 13, True) == [38, ] shavuos = dates.HebrewDate(5780, 3, 6) assert parshios.getparsha_string(shavuos, True) == 'Nasso' assert parshios.getparsha_string(shavuos) is None assert parshios. getparsha_string(shavuos + 7, True) == "Beha'aloscha" assert parshios.getparsha_string(shavuos + 7) == 'Nasso' def test_eighth_day_pesach(self): eighth_day_pesach = dates.HebrewDate(5779, 1, 22) reunion_shabbos = dates.HebrewDate(5779, 5, 2) assert parshios.getparsha_string(eighth_day_pesach) is None assert parshios.getparsha_string(eighth_day_pesach, True) == 'Acharei Mos' assert parshios.getparsha(eighth_day_pesach + 7) == [28,] assert parshios.getparsha(eighth_day_pesach + 7, True) == [29,] assert parshios.getparsha_string(reunion_shabbos) == "Mattos, Masei" assert parshios.getparsha_string(reunion_shabbos, True) == 'Masei' def test_parshatable(): assert parshios.parshatable(5777) == parshios._gentable(5777) assert parshios.parshatable(5778, True) == parshios._gentable(5778, True) def test_iterparshios(): year = 5776 parshalist = list(parshios.parshatable(year).values()) index = 0 for p in parshios.iterparshios(year): assert p == parshalist[index] index += 1 def test_get_parshastring_hebrew(): date = dates.HebrewDate(5781, 3, 28) assert parshios.getparsha_string(date, hebrew=True) == 'קרח' date2 = dates.GregorianDate(2021, 7, 10) assert parshios.getparsha_string(date2, hebrew=True) == 'מטות, מסעי'