-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #107 from dh-tech/feature/convert-hijri
Add support for converting from Hijri calendar to undate and undate interval
- Loading branch information
Showing
31 changed files
with
1,265 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from undate.converters.calendars.gregorian import GregorianDateConverter | ||
from undate.converters.calendars.hijri import HijriDateConverter | ||
from undate.converters.calendars.hebrew import HebrewDateConverter | ||
|
||
__all__ = ["HijriDateConverter", "GregorianDateConverter", "HebrewDateConverter"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from calendar import monthrange | ||
|
||
from undate.converters.base import BaseCalendarConverter | ||
|
||
|
||
class GregorianDateConverter(BaseCalendarConverter): | ||
""" | ||
Calendar converter class for Gregorian calendar. | ||
""" | ||
|
||
#: converter name: Gregorian | ||
name: str = "Gregorian" | ||
#: calendar | ||
calendar_name: str = "Gregorian" | ||
|
||
#: known non-leap year | ||
NON_LEAP_YEAR: int = 2022 | ||
|
||
def min_month(self) -> int: | ||
"""First month for the Gregorian calendar.""" | ||
return 1 | ||
|
||
def max_month(self, year: int) -> int: | ||
"""maximum numeric month for the specified year in the Gregorian calendar""" | ||
return 12 | ||
|
||
def max_day(self, year: int, month: int) -> int: | ||
"""maximum numeric day for the specified year and month in this calendar""" | ||
# if month is known, use that to calculate | ||
if month: | ||
# if year is known, use it; otherwise use a known non-leap year | ||
# (only matters for February) | ||
year = year or self.NON_LEAP_YEAR | ||
|
||
# Use monthrange from python builtin calendar module. | ||
# returns first day of the month and number of days in the month | ||
# for the specified year and month. | ||
_, max_day = monthrange(year, month) | ||
else: | ||
# if year and month are unknown, return maximum possible | ||
max_day = 31 | ||
|
||
return max_day | ||
|
||
def to_gregorian(self, year, month, day) -> tuple[int, int, int]: | ||
"""Convert to Gregorian date. This returns the specified by year, month, | ||
and day unchanged, but is provided for consistency since all calendar | ||
converters need to support conversion to Gregorian calendar for | ||
a common point of comparison. | ||
""" | ||
return (year, month, day) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from undate.converters.calendars.hebrew.converter import HebrewDateConverter | ||
|
||
__all__ = ["HebrewDateConverter"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from typing import Union | ||
|
||
from convertdate import hebrew # type: ignore | ||
from lark.exceptions import UnexpectedCharacters | ||
|
||
from undate.converters.base import BaseCalendarConverter | ||
from undate.converters.calendars.hebrew.parser import hebrew_parser | ||
from undate.converters.calendars.hebrew.transformer import HebrewDateTransformer | ||
from undate.undate import Undate, UndateInterval | ||
|
||
|
||
class HebrewDateConverter(BaseCalendarConverter): | ||
""" | ||
Converter for Hebrew Anno Mundicalendar. | ||
Support for parsing Anno Mundi dates and converting to Undate and UndateInterval | ||
objects in the Gregorian calendar. | ||
""" | ||
|
||
#: converter name: Hebrew | ||
name: str = "Hebrew" | ||
calendar_name: str = "Anno Mundi" | ||
|
||
def __init__(self): | ||
self.transformer = HebrewDateTransformer() | ||
|
||
def min_month(self) -> int: | ||
"""Smallest numeric month for this calendar.""" | ||
return 1 | ||
|
||
def max_month(self, year: int) -> int: | ||
"""Maximum numeric month for this calendar. In Hebrew calendar, this is 12 or 13 | ||
depending on whether it is a leap year.""" | ||
return hebrew.year_months(year) | ||
|
||
def first_month(self) -> int: | ||
"""First month in this calendar. The Hebrew civil year starts in Tishri.""" | ||
return hebrew.TISHRI | ||
|
||
def last_month(self, year: int) -> int: | ||
"""Last month in this calendar. Hebrew civil year starts in Tishri, | ||
Elul is the month before Tishri.""" | ||
return hebrew.ELUL | ||
|
||
def max_day(self, year: int, month: int) -> int: | ||
"""maximum numeric day for the specified year and month in this calendar""" | ||
# NOTE: unreleased v2.4.1 of convertdate standardizes month_days to month_length | ||
return hebrew.month_days(year, month) | ||
|
||
def to_gregorian(self, year: int, month: int, day: int) -> tuple[int, int, int]: | ||
"""Convert a Hebrew date, specified by year, month, and day, | ||
to the Gregorian equivalent date. Returns a tuple of year, month, day. | ||
""" | ||
return hebrew.to_gregorian(year, month, day) | ||
|
||
def parse(self, value: str) -> Union[Undate, UndateInterval]: | ||
""" | ||
Parse a Hebrew date string and return an :class:`~undate.undate.Undate` or | ||
:class:`~undate.undate.UndateInterval`. | ||
The Hebrew date string is preserved in the undate label. | ||
""" | ||
if not value: | ||
raise ValueError("Parsing empty string is not supported") | ||
|
||
# parse the input string, then transform to undate object | ||
try: | ||
# parse the string with our Hebrew date parser | ||
parsetree = hebrew_parser.parse(value) | ||
# transform the parse tree into an undate or undate interval | ||
undate_obj = self.transformer.transform(parsetree) | ||
# set the original date as a label, with the calendar name | ||
undate_obj.label = f"{value} {self.calendar_name}" | ||
return undate_obj | ||
except UnexpectedCharacters as err: | ||
raise ValueError(f"Could not parse '{value}' as a Hebrew date") from err | ||
|
||
# do we need to support conversion the other direction? | ||
# i.e., generate a Hebrew date from an abitrary undate or undate interval? |
Oops, something went wrong.