From e6159aecb804f3f723e363a48d617c83db201972 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Mon, 8 Apr 2019 21:01:02 +0300 Subject: [PATCH 1/8] Implement hebrew date formatter This is a WIP. It is supposed to mimc the behavior of the HebrewDateFormatter from the KosherJava version. After this commit 2 tests will be added. 1 passes. --- test/test_hebrew_date_formatter.py | 26 +++++++ .../hebrew_calendar/hebrew_date_formatter.py | 68 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/test_hebrew_date_formatter.py create mode 100644 zmanim/hebrew_calendar/hebrew_date_formatter.py diff --git a/test/test_hebrew_date_formatter.py b/test/test_hebrew_date_formatter.py new file mode 100644 index 0000000..f685a4e --- /dev/null +++ b/test/test_hebrew_date_formatter.py @@ -0,0 +1,26 @@ +"""Test formatting of the hebrew date""" + +import unittest +from datetime import datetime as dt + +from zmanim.hebrew_calendar.hebrew_date_formatter import HebrewDateFormatter +from zmanim.hebrew_calendar.jewish_calendar import JewishCalendar + + +class TestHebrewDateFormatter(unittest.TestCase): + def formatting_tests(self): + return ( + JewishCalendar(dt(2019, 4, 8)), + u"ג' ניסן ה' תשע\"ט", "3 Nissan, 5779") + + def testFormatHebrew(self): + """Test JewishDate with formatter set to Hebrew""" + formatter = HebrewDateFormatter(hebrew_format=True) + actual = formatter.format(self.formatting_tests()[0]) + self.assertEqual(actual, self.formatting_tests()[1]) + + def testFormatEnglish(self): + """Test JewishDate with formatter set to Hebrew""" + formatter = HebrewDateFormatter(hebrew_format=False) + actual = formatter.format(self.formatting_tests()[0]) + self.assertEqual(actual, self.formatting_tests()[2]) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py new file mode 100644 index 0000000..247e597 --- /dev/null +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -0,0 +1,68 @@ +"""String manipulation module for formatting the Hebrew dates""" + +from .jewish_date import JewishDate + + +class HebrewDateFormatter: + + TRANSLITERATED_MONTHS = [ + "Nissan", "Iyar", "Sivan", "Tammuz", "Av", "Elul", "Tishrei", + "Marcheshvan", "Kislev", "Teves", "Shevat", "Adar", "Adar II", + "Adar I"] + + HEBREW_MONTHS = [ + u"ניסן", u"אייר", u"סיון", u"תמוז", "אב", u"אלול", u"תשרי", u"מרחשון", + u"כסלו", u"טבת", u"שבט", u"אדר", u"אדר ב", u"אדר א"] + + def __init__(self, *args, **kwargs): + self.hebrew_format = True + if 'hebrew_format' in kwargs: + self.hebrew_format = kwargs.pop('hebrew_format') + + def format(self, jewish_date: JewishDate) -> str: + """ + Formats the Jewish date + + If the formatter is set to Hebrew, it will format in the form + "day Month year" for example כ"א שבט תשכ"ט, and the format + "21 Shevat, 5729" if not. + """ + if self.hebrew_format: + return (f"{self.format_hebrew_number(jewish_date.jewish_day)} " + f"{self.format_month(jewish_date)} " + f"{self.format_hebrew_number(jewish_date.jewish_year)}") + + return (f"{jewish_date.jewish_day} {self.format_month(jewish_date)}, " + f"{jewish_date.jewish_year}") + + def format_hebrew_number(self, number: int) -> str: + pass + + def format_month(self, jewish_date: JewishDate) -> str: + """ + Returns a string of the current Hebrew month + + If the formatter is set to Hebrew, it will return values such as + "אדר ב'" or "ניסן", otherwise it will return "Adar II" or "Nissan". + """ + month = jewish_date.jewish_month + if self.hebrew_format: + if (jewish_date.is_jewish_leap_year() and + JewishDate.MONTHS(month) == 'adar'): + # return Adar I, not Adar in a leap year + return (f"{self.HEBREW_MONTHS[13]}" + f"{self.GERESH if self.use_geresh_gershayim else ''}") + elif (jewish_date.is_jewish_leap_year() and + JewishDate.MONTHS(month) == 'adar_ii'): + return (f"{self.HEBREW_MONTHS[12]}" + f"{self.GERESH if self.use_geresh_gershayim else ''}") + else: + return self.HEBREW_MONTHS[month - 1] + + else: + if (jewish_date.is_jewish_leap_year() and + JewishDate.MONTHS(month) == 'adar'): + # return Adar I, not Adar in a leap year + return self.TRANSLITERATED_MONTHS[13] + else: + return self.TRANSLITERATED_MONTHS[month - 1] From b77cdb3a7e0ad40d4ee7ede3ebdce6cdb1983fa9 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Sun, 14 Apr 2019 17:49:17 +0300 Subject: [PATCH 2/8] Get all tests to pass --- .../hebrew_calendar/hebrew_date_formatter.py | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py index 247e597..470d315 100644 --- a/zmanim/hebrew_calendar/hebrew_date_formatter.py +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -14,8 +14,15 @@ class HebrewDateFormatter: u"ניסן", u"אייר", u"סיון", u"תמוז", "אב", u"אלול", u"תשרי", u"מרחשון", u"כסלו", u"טבת", u"שבט", u"אדר", u"אדר ב", u"אדר א"] + GERESH = '\'' + + GERSHAYIM = '"' + def __init__(self, *args, **kwargs): self.hebrew_format = True + self.use_long_hebrew_years = True + self.use_geresh_gershayim = True + if 'hebrew_format' in kwargs: self.hebrew_format = kwargs.pop('hebrew_format') @@ -36,7 +43,99 @@ def format(self, jewish_date: JewishDate) -> str: f"{jewish_date.jewish_year}") def format_hebrew_number(self, number: int) -> str: - pass + """ + Returns a Hebrew formatted string of a number. + + The method can calculate from 0 - 9999. + + * Single digit numbers such as 3, 30 and 100 will be returned with a + '(Geresh) += as at the end. For example ג', ל' and ק'. + * Multi digit numbers such as 21 and 769 will be returned with a + " (Gershayim) between the second to last and last letters. + For example כ"א, תשכ"ט + * 15 and 16 will be returned as ט"ו and ט"ז + * Single digit numbers (years assumed) such as 6000 (%1000=0) will be + returned as ו' אלפים + * 0 will return אפס + """ + if number < 0: + raise ValueError("negative numbers can't be formatted") + elif number > 9999: + raise ValueError("numbers > 9999 can't be formatted") + + ALAFIM = u"אלפים" + EFES = u"אפס" + HUNDREDS = ["", u"ק", u"ר", u"ש", u"ת", u"תק", u"תר", u"תש", u"תת", + u"תתק"] + TENS = ["", u"י", u"כ", u"ל", u"מ", u"נ", u"ס", u"ע", u"פ", u"צ"] + TEN_ENDS = ["", u"י", u"ך", u"ל", u"ם", u"ן", u"ס", u"ע", u"ף", u"ץ"] + TAV_TAZ = [u"טו", u"טז"] + ONES = ["", u"א", u"ב", u"ג", u"ד", u"ה", u"ו", u"ז", u"ח", u"ט"] + + if number == 0: # do we really need this? + return EFES + + short_number = number % 1000 # discard thousands + + # next check for all possible single Hebrew digit years + single_digit_number = ( + short_number < 11 or + (short_number < 100 and short_number % 10 == 0) or + (short_number <= 400 and short_number % 100 == 0)) + + thousands = number // 1000 # get thousands + + string_number = "" + + # append thousands to String + if number % 1000 == 0: # in year is 5000, 4000 etc + string_number += ONES[thousands] + if self.use_geresh_gershayim: + string_number += self.GERESH + + string_number += " " + string_number += ALAFIM # add # of thousands + word thousand + return string_number + + elif self.use_long_hebrew_years and number >= 1000: + # if alafim boolean display thousands + string_number += ONES[thousands] + if self.use_geresh_gershayim: + string_number += self.GERESH # append thousands quote + string_number += " " + + number = number % 1000 # remove 1000s + hundreds = number // 100 # # of hundreds + + string_number += HUNDREDS[hundreds] # add hundreds to String + number = number % 100 # remove 100s + + if number == 15: # special case 15 + string_number += TAV_TAZ[0] + elif number == 16: # special case 16 + string_number += TAV_TAZ[1] + else: + tens = number // 10 + if number % 10 == 0: # if evenly divisible by 10 + if not single_digit_number: + # end letters so years like 5750 will end with an end nun + string_number += TEN_ENDS[tens] + else: + # standard letters so years like 5050 will end with a + # regular nun + string_number += TENS[tens] + else: + string_number += TENS[tens] + number = number % 10 + string_number += ONES[number] + + if self.use_geresh_gershayim: + if single_digit_number: + string_number += self.GERESH # append single quote + else: # append double quote before last digit + string_number = string_number[:-1] + self.GERSHAYIM + \ + string_number[-1:] + return string_number def format_month(self, jewish_date: JewishDate) -> str: """ From be639889166adfb319633a2a7746ed8e058e52dc Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Sun, 14 Apr 2019 18:31:01 +0300 Subject: [PATCH 3/8] Add more test cases for format_month method Improve format_month coverage to 100% by adding test cases for both Adars. Bugfixes in comparison method. --- test/test_hebrew_date_formatter.py | 18 +++++++++++------- .../hebrew_calendar/hebrew_date_formatter.py | 14 +++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/test/test_hebrew_date_formatter.py b/test/test_hebrew_date_formatter.py index f685a4e..536667c 100644 --- a/test/test_hebrew_date_formatter.py +++ b/test/test_hebrew_date_formatter.py @@ -10,17 +10,21 @@ class TestHebrewDateFormatter(unittest.TestCase): def formatting_tests(self): return ( - JewishCalendar(dt(2019, 4, 8)), - u"ג' ניסן ה' תשע\"ט", "3 Nissan, 5779") + (dt(2019, 4, 8), u"ג' ניסן ה' תשע\"ט", "3 Nissan, 5779"), + (dt(2019, 4, 1), u"כ\"ה אדר ב' ה' תשע\"ט", "25 Adar II, 5779"), + (dt(2019, 3, 1), u"כ\"ד אדר א' ה' תשע\"ט", "24 Adar I, 5779"), + ) def testFormatHebrew(self): """Test JewishDate with formatter set to Hebrew""" formatter = HebrewDateFormatter(hebrew_format=True) - actual = formatter.format(self.formatting_tests()[0]) - self.assertEqual(actual, self.formatting_tests()[1]) + for case in self.formatting_tests(): + actual = formatter.format(JewishCalendar(case[0])) + self.assertEqual(actual, case[1]) def testFormatEnglish(self): - """Test JewishDate with formatter set to Hebrew""" + """Test JewishDate with formatter set to English""" formatter = HebrewDateFormatter(hebrew_format=False) - actual = formatter.format(self.formatting_tests()[0]) - self.assertEqual(actual, self.formatting_tests()[2]) + for case in self.formatting_tests(): + actual = formatter.format(JewishCalendar(case[0])) + self.assertEqual(actual, case[2]) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py index 470d315..c46334a 100644 --- a/zmanim/hebrew_calendar/hebrew_date_formatter.py +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -1,8 +1,13 @@ """String manipulation module for formatting the Hebrew dates""" +import logging + from .jewish_date import JewishDate +_LOGGER = logging.getLogger(__name__) + + class HebrewDateFormatter: TRANSLITERATED_MONTHS = [ @@ -145,14 +150,17 @@ def format_month(self, jewish_date: JewishDate) -> str: "אדר ב'" or "ניסן", otherwise it will return "Adar II" or "Nissan". """ month = jewish_date.jewish_month + + _LOGGER.debug("Formatting month %s", JewishDate.MONTHS(month)) + if self.hebrew_format: if (jewish_date.is_jewish_leap_year() and - JewishDate.MONTHS(month) == 'adar'): + JewishDate.MONTHS(month) == JewishDate.MONTHS.adar): # return Adar I, not Adar in a leap year return (f"{self.HEBREW_MONTHS[13]}" f"{self.GERESH if self.use_geresh_gershayim else ''}") elif (jewish_date.is_jewish_leap_year() and - JewishDate.MONTHS(month) == 'adar_ii'): + JewishDate.MONTHS(month) == JewishDate.MONTHS.adar_ii): return (f"{self.HEBREW_MONTHS[12]}" f"{self.GERESH if self.use_geresh_gershayim else ''}") else: @@ -160,7 +168,7 @@ def format_month(self, jewish_date: JewishDate) -> str: else: if (jewish_date.is_jewish_leap_year() and - JewishDate.MONTHS(month) == 'adar'): + JewishDate.MONTHS(month) == JewishDate.MONTHS.adar): # return Adar I, not Adar in a leap year return self.TRANSLITERATED_MONTHS[13] else: From ef3659fd111811c9f6d56be2af001b9504cf069c Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Sun, 14 Apr 2019 19:32:03 +0300 Subject: [PATCH 4/8] Complete coverage for testing numbers Add tests for testing numbers --- test/test_hebrew_date_formatter.py | 30 +++++++++++++++++++ .../hebrew_calendar/hebrew_date_formatter.py | 14 +++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/test/test_hebrew_date_formatter.py b/test/test_hebrew_date_formatter.py index 536667c..3a7d751 100644 --- a/test/test_hebrew_date_formatter.py +++ b/test/test_hebrew_date_formatter.py @@ -15,6 +15,18 @@ def formatting_tests(self): (dt(2019, 3, 1), u"כ\"ד אדר א' ה' תשע\"ט", "24 Adar I, 5779"), ) + def number_tests(self): + return ( + (0, u"אפס", u"אפס"), + (1, u"א'", u"א"), + (15, u"ט\"ו", u"טו"), + (16, u"ט\"ז", u"טז"), + (20, u"כ'", u"כ"), + (120, u"ק\"ך", u"קך"), + (1000, u"א' אלפים", u"א אלפים"), + (5780, u"ה' תש\"ף", u"ה תשף") + ) + def testFormatHebrew(self): """Test JewishDate with formatter set to Hebrew""" formatter = HebrewDateFormatter(hebrew_format=True) @@ -28,3 +40,21 @@ def testFormatEnglish(self): for case in self.formatting_tests(): actual = formatter.format(JewishCalendar(case[0])) self.assertEqual(actual, case[2]) + + def testFormatNumber(self): + formatter = HebrewDateFormatter() + for case in self.number_tests(): + actual = formatter.format_hebrew_number(case[0]) + self.assertEqual(actual, case[1]) + + def testFormatNumberNoGereshGershayim(self): + formatter = HebrewDateFormatter(use_geresh_gershayim=False) + for case in self.number_tests(): + actual = formatter.format_hebrew_number(case[0]) + self.assertEqual(actual, case[2]) + + def testFormatNumberOutOfBounds(self): + formatter = HebrewDateFormatter() + for number in [-1, 10000, 99999]: + self.assertRaises( + ValueError, formatter.format_hebrew_number, number) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py index c46334a..786bebc 100644 --- a/zmanim/hebrew_calendar/hebrew_date_formatter.py +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -28,8 +28,18 @@ def __init__(self, *args, **kwargs): self.use_long_hebrew_years = True self.use_geresh_gershayim = True - if 'hebrew_format' in kwargs: - self.hebrew_format = kwargs.pop('hebrew_format') + # If one of the above keys is passed along, allow to override the value + self.__dict__.update( + (key, val) for key, val in kwargs.items() if hasattr(self, key)) + + _LOGGER.debug( + "Formatter settings:\n" + "========================\n" + "hebrew_format =%s\n" + "use_long_hebrew_years=%s\n" + "use_geresh_gershayim =%s", + self.hebrew_format, self.use_long_hebrew_years, + self.use_geresh_gershayim) def format(self, jewish_date: JewishDate) -> str: """ From b0573add63669be0f264a01473b02dc0933d5213 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Mon, 29 Apr 2019 20:28:10 +0300 Subject: [PATCH 5/8] Add support for Omer count in Hebrew date formatting --- test/test_hebrew_date_formatter.py | 24 ++++++++++++ .../hebrew_calendar/hebrew_date_formatter.py | 37 +++++++++++++++---- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/test/test_hebrew_date_formatter.py b/test/test_hebrew_date_formatter.py index 3a7d751..cf0d27f 100644 --- a/test/test_hebrew_date_formatter.py +++ b/test/test_hebrew_date_formatter.py @@ -15,6 +15,13 @@ def formatting_tests(self): (dt(2019, 3, 1), u"כ\"ד אדר א' ה' תשע\"ט", "24 Adar I, 5779"), ) + def omer_tests(self): + return ( + (dt(2019, 4, 29), u"ט' בעומר", "Omer 9"), + (dt(2019, 4, 1), u"", ""), + (dt(2019, 5, 23), u"ל\"ג בעומר", "Lag BaOmer"), + ) + def number_tests(self): return ( (0, u"אפס", u"אפס"), @@ -42,19 +49,36 @@ def testFormatEnglish(self): self.assertEqual(actual, case[2]) def testFormatNumber(self): + """Test numbers formatting with geresh""" formatter = HebrewDateFormatter() for case in self.number_tests(): actual = formatter.format_hebrew_number(case[0]) self.assertEqual(actual, case[1]) def testFormatNumberNoGereshGershayim(self): + """Test numbers formatting without geresh""" formatter = HebrewDateFormatter(use_geresh_gershayim=False) for case in self.number_tests(): actual = formatter.format_hebrew_number(case[0]) self.assertEqual(actual, case[2]) def testFormatNumberOutOfBounds(self): + """Check for out of bounds number formatting""" formatter = HebrewDateFormatter() for number in [-1, 10000, 99999]: self.assertRaises( ValueError, formatter.format_hebrew_number, number) + + def testFormatOmerHebrew(self): + """Test Omer count in Hebrew""" + formatter = HebrewDateFormatter(hebrew_format=True) + for case in self.omer_tests(): + actual = formatter.format_omer(JewishCalendar(case[0])) + self.assertEqual(actual, case[1]) + + def testFormatOmerEnglish(self): + """Test Omer count in English""" + formatter = HebrewDateFormatter(hebrew_format=False) + for case in self.omer_tests(): + actual = formatter.format_omer(JewishCalendar(case[0])) + self.assertEqual(actual, case[2]) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py index 786bebc..bc3b8f6 100644 --- a/zmanim/hebrew_calendar/hebrew_date_formatter.py +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -3,6 +3,7 @@ import logging from .jewish_date import JewishDate +from .jewish_calendar import JewishCalendar _LOGGER = logging.getLogger(__name__) @@ -27,19 +28,14 @@ def __init__(self, *args, **kwargs): self.hebrew_format = True self.use_long_hebrew_years = True self.use_geresh_gershayim = True + self.hebrew_omer_prefix = u"ב" # If one of the above keys is passed along, allow to override the value self.__dict__.update( (key, val) for key, val in kwargs.items() if hasattr(self, key)) - _LOGGER.debug( - "Formatter settings:\n" - "========================\n" - "hebrew_format =%s\n" - "use_long_hebrew_years=%s\n" - "use_geresh_gershayim =%s", - self.hebrew_format, self.use_long_hebrew_years, - self.use_geresh_gershayim) + for key, val in self.__dict__.items(): + _LOGGER.debug("Formatter settings: %s: %s", key, val) def format(self, jewish_date: JewishDate) -> str: """ @@ -183,3 +179,28 @@ def format_month(self, jewish_date: JewishDate) -> str: return self.TRANSLITERATED_MONTHS[13] else: return self.TRANSLITERATED_MONTHS[month - 1] + + def format_omer(self, jewish_calendar: JewishCalendar) -> str: + """ + Returns a string of the Omer day + + The string is formatted in the form ל"ג בעומר if Hebrew format is set, + or "Omer X" or "Lag BaOmer" if not. + + If no Omer is counted, the string is empty. + + By default the value is prefixed by "ב", this can be changed to "ל" by + changing the hebrew_omer_prefix class variable. + """ + omer = jewish_calendar.day_of_omer() + if omer is None: + return "" + + if self.hebrew_format: + return (f"{self.format_hebrew_number(omer)} " + f"{self.hebrew_omer_prefix}עומר") + else: + if omer == 33: # if lag b'omer + return "Lag BaOmer" + else: + return f"Omer {omer}" From 01aab90955f7eb50db56579ede998ccbdad67df2 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Tue, 30 Apr 2019 13:22:09 +0300 Subject: [PATCH 6/8] Add yom tov name formatting So as to keep in line with the Java implemetation I changed the order of the enums to match the order used in the formatting --- test/test_hebrew_date_formatter.py | 35 ++++++++++--- .../hebrew_calendar/hebrew_date_formatter.py | 52 +++++++++++++++++++ zmanim/hebrew_calendar/jewish_calendar.py | 18 ++++--- 3 files changed, 91 insertions(+), 14 deletions(-) diff --git a/test/test_hebrew_date_formatter.py b/test/test_hebrew_date_formatter.py index cf0d27f..5bef4d3 100644 --- a/test/test_hebrew_date_formatter.py +++ b/test/test_hebrew_date_formatter.py @@ -15,13 +15,6 @@ def formatting_tests(self): (dt(2019, 3, 1), u"כ\"ד אדר א' ה' תשע\"ט", "24 Adar I, 5779"), ) - def omer_tests(self): - return ( - (dt(2019, 4, 29), u"ט' בעומר", "Omer 9"), - (dt(2019, 4, 1), u"", ""), - (dt(2019, 5, 23), u"ל\"ג בעומר", "Lag BaOmer"), - ) - def number_tests(self): return ( (0, u"אפס", u"אפס"), @@ -34,6 +27,20 @@ def number_tests(self): (5780, u"ה' תש\"ף", u"ה תשף") ) + def omer_tests(self): + return ( + (dt(2019, 4, 29), u"ט' בעומר", "Omer 9"), + (dt(2019, 4, 1), u"", ""), + (dt(2019, 5, 23), u"ל\"ג בעומר", "Lag BaOmer"), + ) + + def yom_tov_tests(self): + return ( + (dt(2019, 4, 1), u"", ""), + (dt(2019, 4, 26), u"פסח", "Pesach"), + (dt(2018, 12, 3), u"א' חנוכה", "Chanukah 1"), + ) + def testFormatHebrew(self): """Test JewishDate with formatter set to Hebrew""" formatter = HebrewDateFormatter(hebrew_format=True) @@ -82,3 +89,17 @@ def testFormatOmerEnglish(self): for case in self.omer_tests(): actual = formatter.format_omer(JewishCalendar(case[0])) self.assertEqual(actual, case[2]) + + def testFormatYomTovHebrew(self): + """Test Yom Tov in Hebrew""" + formatter = HebrewDateFormatter(hebrew_format=True) + for case in self.yom_tov_tests(): + actual = formatter.format_yom_tov(JewishCalendar(case[0])) + self.assertEqual(actual, case[1]) + + def testFormatYomTovEnglish(self): + """Test YomTov in English""" + formatter = HebrewDateFormatter(hebrew_format=False) + for case in self.yom_tov_tests(): + actual = formatter.format_yom_tov(JewishCalendar(case[0])) + self.assertEqual(actual, case[2]) diff --git a/zmanim/hebrew_calendar/hebrew_date_formatter.py b/zmanim/hebrew_calendar/hebrew_date_formatter.py index bc3b8f6..56c75fd 100644 --- a/zmanim/hebrew_calendar/hebrew_date_formatter.py +++ b/zmanim/hebrew_calendar/hebrew_date_formatter.py @@ -24,6 +24,28 @@ class HebrewDateFormatter: GERSHAYIM = '"' + HEBREW_HOLIDAYS = [ + u"", u"ערב פסח", u"פסח", u"חול המועד פסח", u"פסח שני", u"ערב שבועות", + u"שבועות", u"שבעה עשר בתמוז", u"תשעה באב", u"ט״ו באב", u"ערב ראש השנה", + u"ראש השנה", u"צום גדליה", u"ערב יום כיפור", u"יום כיפור", + u"ערב סוכות", u"סוכות", u"חול המועד סוכות", u"הושענא רבה", + u"שמיני עצרת", u"שמחת תורה", u"ערב חנוכה", u"חנוכה", u"עשרה בטבת", + u"ט״ו בשבט", u"תענית אסתר", u"פורים", u"פורים שושן", u"פורים קטן", + u"שושן פורים קטן" u"ראש חודש", u"יום השואה", u"יום הזיכרון", + u"יום העצמאות", u"יום ירושלים" + ] + + TRANSLITERATED_HOLIDAYS = [ + "", "Erev Pesach", "Pesach", "Chol Hamoed Pesach", "Pesach Sheni", + "Erev Shavuos", "Shavuos", "Seventeenth of Tammuz", "Tishah B'Av", + "Tu B'Av", "Erev Rosh Hashana", "Rosh Hashana", "Fast of Gedalyah", + "Erev Yom Kippur", "Yom Kippur", "Erev Succos", "Succos", + "Chol Hamoed Succos", "Hoshana Rabbah", "Shemini Atzeres", + "Simchas Torah", "Erev Chanukah", "Chanukah", "Tenth of Teves", + "Tu B'Shvat", "Fast of Esther", "Purim", "Shushan Purim", + "Purim Katan", "Shushan Purim Katan", "Rosh Chodesh", "Yom HaShoah", + "Yom Hazikaron", "Yom Ha'atzmaut", "Yom Yerushalayim"] + def __init__(self, *args, **kwargs): self.hebrew_format = True self.use_long_hebrew_years = True @@ -204,3 +226,33 @@ def format_omer(self, jewish_calendar: JewishCalendar) -> str: return "Lag BaOmer" else: return f"Omer {omer}" + + def format_yom_tov(self, jewish_calendar: JewishCalendar) -> str: + """ + Formats the Yom Tov (holiday) name + + Depndent on hebrew_format, returns the name in Hebrew or transliterated + Latin characters. + """ + yom_tov = jewish_calendar.significant_day() + + if yom_tov is None: + return "" + + index = jewish_calendar.SIGNIFICANT_DAYS[yom_tov].value + + _LOGGER.debug("Detected Yom Tov: %s (%d)", yom_tov, index) + + if index == JewishCalendar.SIGNIFICANT_DAYS.chanukah.value: + day_of_chanukah = jewish_calendar.day_of_chanukah() + + _LOGGER.debug("Day of Chanukah: %d", day_of_chanukah) + if self.hebrew_format: + return (f"{self.format_hebrew_number(day_of_chanukah)} " + f"{self.HEBREW_HOLIDAYS[index]}") + else: + return (f"{self.TRANSLITERATED_HOLIDAYS[index]} " + f"{day_of_chanukah}") + + return (self.HEBREW_HOLIDAYS[index] if self.hebrew_format else + self.TRANSLITERATED_HOLIDAYS[index]) diff --git a/zmanim/hebrew_calendar/jewish_calendar.py b/zmanim/hebrew_calendar/jewish_calendar.py index 47bf514..ad63a9e 100644 --- a/zmanim/hebrew_calendar/jewish_calendar.py +++ b/zmanim/hebrew_calendar/jewish_calendar.py @@ -9,13 +9,17 @@ class JewishCalendar(JewishDate): - SIGNIFICANT_DAYS = Enum('SignificantDays', 'erev_rosh_hashana rosh_hashana tzom_gedalyah erev_yom_kippur yom_kippur \ - erev_succos succos chol_hamoed_succos hoshana_rabbah shemini_atzeres simchas_torah \ - chanukah tenth_of_teves tu_beshvat \ - taanis_esther purim shushan_purim purim_katan shushan_purim_katan \ - erev_pesach pesach chol_hamoed_pesach pesach_sheni erev_shavuos shavuos \ - seventeen_of_tammuz tisha_beav tu_beav \ - yom_hashoah yom_hazikaron yom_haatzmaut yom_yerushalayim') + SIGNIFICANT_DAYS = Enum( + 'SignificantDays', 'erev_pesach pesach chol_hamoed_pesach pesach_sheni \ + erev_shavuos shavuos seventeen_of_tammuz \ + tisha_beav tu_beav erev_rosh_hashana rosh_hashana \ + tzom_gedalyah erev_yom_kippur yom_kippur \ + erev_succos succos chol_hamoed_succos \ + hoshana_rabbah shemini_atzeres simchas_torah \ + erev_chanukah chanukah tenth_of_teves tu_beshvat \ + taanis_esther purim shushan_purim purim_katan \ + shushan_purim_katan rosh_chodesh yom_hashoah \ + yom_hazikaron yom_haatzmaut yom_yerushalayim') def __init__(self, *args, **kwargs): in_israel = None From 9d85c3af60d3aa64cc07bf218e617e69da0f727a Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Tue, 30 Apr 2019 13:24:46 +0300 Subject: [PATCH 7/8] Match naming standard from Java implementation To keep in line with the KosherJava implementation, I changed the name of taanis_esther and tzom_gedalya to 'fast_of_*' --- test/test_jewish_calendar.py | 38 +++++++++++------------ zmanim/hebrew_calendar/jewish_calendar.py | 13 ++++---- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/test/test_jewish_calendar.py b/test/test_jewish_calendar.py index 514001d..0a21130 100644 --- a/test/test_jewish_calendar.py +++ b/test/test_jewish_calendar.py @@ -33,7 +33,7 @@ def chanukah_for_chaseirim(self): def leap_purim(self): return {'purim_katan': ['12-14'], 'shushan_purim_katan': ['12-15'], - 'taanis_esther': ['13-13'], + 'fast_of_esther': ['13-13'], 'purim': ['13-14'], 'shushan_purim': ['13-15']} @@ -50,7 +50,7 @@ def standard_significant_days(self): 'tu_beav': ['5-15'], 'erev_rosh_hashana': ['6-29'], 'rosh_hashana': ['7-1', '7-2'], - 'tzom_gedalyah': ['7-3'], + 'fast_of_gedalyah': ['7-3'], 'erev_yom_kippur': ['7-9'], 'yom_kippur': ['7-10'], 'erev_succos': ['7-14'], @@ -62,7 +62,7 @@ def standard_significant_days(self): 'chanukah': ['9-25', '9-26', '9-27', '9-28', '9-29', '9-30', '10-1', '10-2'], 'tenth_of_teves': ['10-10'], 'tu_beshvat': ['11-15'], - 'taanis_esther': ['12-13'], + 'fast_of_esther': ['12-13'], 'purim': ['12-14'], 'shushan_purim': ['12-15'] } @@ -112,13 +112,13 @@ def test_significant_days_for_standard_monday_chaseirim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.standard_significant_days(), 'chanukah': self.chanukah_for_chaseirim(), - 'taanis_esther': ['12-11']} + 'fast_of_esther': ['12-11']} self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_standard_significant_days(), 'chanukah': self.chanukah_for_chaseirim(), - 'taanis_esther': ['12-11']} + 'fast_of_esther': ['12-11']} self.assertEqual(israel_result, expected) def test_significant_days_for_standard_monday_shelaimim(self): @@ -148,7 +148,7 @@ def test_significant_days_for_standard_thursday_kesidran(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.standard_significant_days(), - 'tzom_gedalyah': ['7-4'], + 'fast_of_gedalyah': ['7-4'], 'seventeen_of_tammuz': ['4-18'], 'tisha_beav': ['5-10'] } @@ -156,7 +156,7 @@ def test_significant_days_for_standard_thursday_kesidran(self): israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_standard_significant_days(), - 'tzom_gedalyah': ['7-4'], + 'fast_of_gedalyah': ['7-4'], 'seventeen_of_tammuz': ['4-18'], 'tisha_beav': ['5-10'] } @@ -167,13 +167,13 @@ def test_significant_days_for_standard_thursday_shelaimim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.standard_significant_days(), - 'tzom_gedalyah': ['7-4'] + 'fast_of_gedalyah': ['7-4'] } self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_standard_significant_days(), - 'tzom_gedalyah': ['7-4'] + 'fast_of_gedalyah': ['7-4'] } self.assertEqual(israel_result, expected) @@ -197,13 +197,13 @@ def test_significant_days_for_standard_shabbos_shelaimim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.standard_significant_days(), - 'taanis_esther': ['12-11'] + 'fast_of_esther': ['12-11'] } self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_standard_significant_days(), - 'taanis_esther': ['12-11'] + 'fast_of_esther': ['12-11'] } self.assertEqual(israel_result, expected) @@ -261,14 +261,14 @@ def test_significant_days_for_leap_thursday_chaseirim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.leap_significant_days(), - 'tzom_gedalyah': ['7-4'], + 'fast_of_gedalyah': ['7-4'], 'chanukah': self.chanukah_for_chaseirim() } self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_leap_significant_days(), - 'tzom_gedalyah': ['7-4'], + 'fast_of_gedalyah': ['7-4'], 'chanukah': self.chanukah_for_chaseirim() } self.assertEqual(israel_result, expected) @@ -278,15 +278,15 @@ def test_significant_days_for_leap_thursday_shelaimim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.leap_significant_days(), - 'tzom_gedalyah': ['7-4'], - 'taanis_esther': ['13-11'] + 'fast_of_gedalyah': ['7-4'], + 'fast_of_esther': ['13-11'] } self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_leap_significant_days(), - 'tzom_gedalyah': ['7-4'], - 'taanis_esther': ['13-11'] + 'fast_of_gedalyah': ['7-4'], + 'fast_of_esther': ['13-11'] } self.assertEqual(israel_result, expected) @@ -296,14 +296,14 @@ def test_significant_days_for_leap_shabbos_chaseirim(self): result = test.test_helper.all_days_matching(year, lambda c: c.significant_day()) expected = {**self.leap_significant_days(), 'chanukah': self.chanukah_for_chaseirim(), - 'taanis_esther': ['13-11'] + 'fast_of_esther': ['13-11'] } self.assertEqual(result, expected) israel_result = test.test_helper.all_days_matching(year, lambda c: c.significant_day(), in_israel=True) expected = {**self.israel_leap_significant_days(), 'chanukah': self.chanukah_for_chaseirim(), - 'taanis_esther': ['13-11'] + 'fast_of_esther': ['13-11'] } self.assertEqual(israel_result, expected) diff --git a/zmanim/hebrew_calendar/jewish_calendar.py b/zmanim/hebrew_calendar/jewish_calendar.py index ad63a9e..316cbd0 100644 --- a/zmanim/hebrew_calendar/jewish_calendar.py +++ b/zmanim/hebrew_calendar/jewish_calendar.py @@ -13,11 +13,11 @@ class JewishCalendar(JewishDate): 'SignificantDays', 'erev_pesach pesach chol_hamoed_pesach pesach_sheni \ erev_shavuos shavuos seventeen_of_tammuz \ tisha_beav tu_beav erev_rosh_hashana rosh_hashana \ - tzom_gedalyah erev_yom_kippur yom_kippur \ + fast_of_gedalyah erev_yom_kippur yom_kippur \ erev_succos succos chol_hamoed_succos \ hoshana_rabbah shemini_atzeres simchas_torah \ erev_chanukah chanukah tenth_of_teves tu_beshvat \ - taanis_esther purim shushan_purim purim_katan \ + fast_of_esther purim shushan_purim purim_katan \ shushan_purim_katan rosh_chodesh yom_hashoah \ yom_hazikaron yom_haatzmaut yom_yerushalayim') @@ -90,8 +90,9 @@ def is_chol_hamoed(self) -> bool: return sd is not None and (sd.startswith('chol_hamoed_') or sd == 'hoshana_rabbah') def is_taanis(self) -> bool: - return self.significant_day() in ['seventeen_of_tammuz', 'tisha_beav', 'tzom_gedalyah', - 'yom_kippur', 'tenth_of_teves', 'taanis_esther'] + return self.significant_day() in [ + 'seventeen_of_tammuz', 'tisha_beav', 'fast_of_gedalyah', + 'yom_kippur', 'tenth_of_teves', 'fast_of_esther'] def is_rosh_chodesh(self) -> bool: return self.jewish_day == 30 or (self.jewish_day == 1 and self.jewish_month != 7) @@ -224,7 +225,7 @@ def _tishrei_significant_day(self) -> Optional[str]: return 'rosh_hashana' elif (self.jewish_day == 3 and self.day_of_week != 7) \ or (self.jewish_day == 4 and self.day_of_week == 1): - return 'tzom_gedalyah' + return 'fast_of_gedalyah' elif self.jewish_day == 9: return 'erev_yom_kippur' elif self.jewish_day == 10: @@ -278,7 +279,7 @@ def _adar_ii_significant_day(self) -> Optional[str]: def _purim_significant_day(self) -> Optional[str]: if (self.jewish_day == 13 and self.day_of_week != 7) \ or (self.jewish_day == 11 and self.day_of_week == 5): - return 'taanis_esther' + return 'fast_of_esther' elif self.jewish_day == 14: return 'purim' elif self.jewish_day == 15: From 5900720c41b44bdf476b669477f44874d955cc88 Mon Sep 17 00:00:00 2001 From: Tsvi Mostovicz Date: Tue, 30 Apr 2019 13:25:50 +0300 Subject: [PATCH 8/8] Small linting fixes --- zmanim/hebrew_calendar/jewish_calendar.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zmanim/hebrew_calendar/jewish_calendar.py b/zmanim/hebrew_calendar/jewish_calendar.py index 316cbd0..725c75e 100644 --- a/zmanim/hebrew_calendar/jewish_calendar.py +++ b/zmanim/hebrew_calendar/jewish_calendar.py @@ -165,7 +165,7 @@ def _nissan_significant_day(self) -> Optional[str]: return 'erev_pesach' elif self.jewish_day in pesach: return 'pesach' - elif self.jewish_day in range(16,21): + elif self.jewish_day in range(16, 21): return 'chol_hamoed_pesach' elif self.use_modern_holidays: if (self.jewish_day == 26 and self.day_of_week == 5) \ @@ -177,8 +177,10 @@ def _iyar_significant_day(self) -> Optional[str]: if self.jewish_day == 14: return 'pesach_sheni' elif self.use_modern_holidays: - # Note that this logic follows the current rules, which were last revised in 5764. - # The calculations for years prior may not reflect the actual dates observed at that time. + # Note that this logic follows the current rules, which were last + # revised in 5764. + # The calculations for years prior may not reflect the actual dates + # observed at that time. if (self.jewish_day in [2, 3] and self.day_of_week == 4) \ or (self.jewish_day == 4 and self.day_of_week == 3) \ or (self.jewish_day == 5 and self.day_of_week == 2): @@ -234,7 +236,7 @@ def _tishrei_significant_day(self) -> Optional[str]: return 'erev_succos' elif self.jewish_day in succos: return 'succos' - elif self.jewish_day in range(16,21): + elif self.jewish_day in range(16, 21): return 'chol_hamoed_succos' elif self.jewish_day == 21: return 'hoshana_rabbah' @@ -251,7 +253,7 @@ def _kislev_significant_day(self) -> Optional[str]: return 'chanukah' def _teves_significant_day(self) -> Optional[str]: - chanukah = [1,2] + chanukah = [1, 2] if self.is_kislev_short(): chanukah += [3] @@ -283,4 +285,4 @@ def _purim_significant_day(self) -> Optional[str]: elif self.jewish_day == 14: return 'purim' elif self.jewish_day == 15: - return 'shushan_purim' \ No newline at end of file + return 'shushan_purim'