Source code for pyluach.hebrewcal

from __future__ import unicode_literals
from __future__ import division

from collections import deque
from numbers import Number

from pyluach.dates import HebrewDate
from pyluach.utils import memoize


def _adjust_postponed(date):
    """Return actual date of fast day.

    For usual date of a fast day returns fast day adjusted for any
    postponements.
    """
    if date.weekday() == 7:
        if date.month in [12, 13]:
            date -= 2
        else:
            date += 1
    return date


@memoize(maxlen=50)
def _fast_day_table(year):
    table = dict()
    workingdate = _adjust_postponed(HebrewDate(year, 7, 3))
    table[workingdate] = 'Tzom Gedalia'

    workingdate = _adjust_postponed(HebrewDate(year, 10, 10))
    table[workingdate] = '10 of Teves'

    month = 13 if Year(year).leap else 12
    workingdate = _adjust_postponed(HebrewDate(year, month, 13))
    table[workingdate] = 'Taanis Esther'

    workingdate = _adjust_postponed(HebrewDate(year, 4, 17))
    table[workingdate] = '17 of Tamuz'

    workingdate = _adjust_postponed(HebrewDate(year, 5, 9))
    table[workingdate] = '9 of Av'

    return table


[docs]def holiday(date, israel=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 : boolian, optional ``True`` if you want the holidays according to the israel schedule. Defaults to ``False``. Returns ------- str or ``None`` The name of the holiday or ``None`` if the given date is not a Jewish holiday. """ date = date.to_heb() year = date.year month = date.month day = date.day table = _fast_day_table(year) if date in table: return table[date] if month == 7: if day in range(1, 3): return 'Rosh Hashana' elif day == 10: return 'Yom Kippur' elif day in range(15, 22): return 'Succos' elif day == 22: return 'Shmini Atzeres' elif day == 23 and israel == False: return 'Simchas Torah' elif( (month == 9 and day in range(25, 30)) or date in [(HebrewDate(year, 9, 29) + n) for n in range(1, 4)] ): return 'Chanuka' elif month == 11 and day == 15: return "Tu B'shvat" elif month == 12: leap = HebrewDate._is_leap(year) if day == 14: return 'Purim Katan' if leap else 'Purim' if day == 15 and not leap: return 'Shushan Purim' elif month == 13: if day == 14: return 'Purim' elif day == 15: return 'Shushan Purim' elif month == 1 and day in range(15, 22 if israel else 23): return 'Pesach' elif month == 2 and day == 18: return "Lag Ba'omer" elif month == 3 and (day == 6 if israel else day in (6, 7)): return 'Shavuos' elif month == 5 and day == 15: return "Tu B'av"
[docs]class Year(object): """ 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. ===================== ================================================ 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): """ The initializer for a Year object. """ if year < 1: raise ValueError('Year {0} is before creation.'.format(year)) self.year = year self.leap = HebrewDate._is_leap(year) def __repr__(self): return 'Year({0})'.format(self.year) def __len__(self): return HebrewDate._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.""" return Year(self.year + other) 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 AttributeError: raise TypeError('Only an int or another Year object can' ' be subtracted from a year.') 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
[docs] 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)
[docs] 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
[docs] 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)
[docs]class Month(object): """ A Month object represents a month of the Hebrew calendar. 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. """ _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 = HebrewDate._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._monthnames[12] = 'Adar Rishon' if leap else 'Adar' self.name = self._monthnames[self.month] def __repr__(self): return 'Month({0}, {1})'.format(self.year, self.month) def __len__(self): return HebrewDate._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:]) if other <= leftover_months: return Month(self.year, yearmonths[index + other]) return Month(self.year + 1, 7).__add__(other - 1 - leftover_months) 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''')
[docs] 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 = ( (235 * ((self.year-1) // 19)) + (12 * ((self.year-1) % 19)) + (7 * ((self.year-1) % 19) + 1) // 19 + yearmonths.index(self.month) ) return months_elapsed
[docs] 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)