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)