Skip to content

Commit c5c1cc8

Browse files
committed
Support duration for unknown hebrew & islamic years
1 parent b436f33 commit c5c1cc8

File tree

4 files changed

+30
-7
lines changed

4 files changed

+30
-7
lines changed

src/undate/converters/base.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ class BaseCalendarConverter(BaseDateConverter):
148148
#: Converter name. Subclasses must define a unique name.
149149
name: str = "Base Calendar Converter"
150150

151+
#: arbitrary known non-leap year
152+
NON_LEAP_YEAR: int
153+
#: arbitrary known leap year
154+
LEAP_YEAR: int
155+
156+
# minimum year for this calendar, if there is one
157+
MIN_YEAR: None | int = None
158+
# maximum year for this calendar, if there is one
159+
MAX_YEAR: None | int = None
160+
151161
def min_month(self) -> int:
152162
"""Smallest numeric month for this calendar."""
153163
raise NotImplementedError

src/undate/converters/calendars/islamic/converter.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class IslamicDateConverter(BaseCalendarConverter):
2626
#: arbitrary known leap year
2727
LEAP_YEAR: int = 1458
2828

29+
# minimum year for islamic calendar is 1 AH, does not go negative
30+
MIN_YEAR: None | int = 1
31+
# convertdate gives a month 34 for numpy max year 2.5^16, so scale it back a bit
32+
MAX_YEAR = int(2.5e12)
33+
2934
def __init__(self):
3035
self.transformer = IslamicDateTransformer()
3136

@@ -71,6 +76,9 @@ def to_gregorian(self, year: int, month: int, day: int) -> tuple[int, int, int]:
7176
"""Convert a Hijri date, specified by year, month, and day,
7277
to the Gregorian equivalent date. Returns a tuple of year, month, day.
7378
"""
79+
print(
80+
f"islamic.to_gregorian(year={year}, month={month}, day={day}) = {islamic.to_gregorian(year, month, day)}"
81+
)
7482
return islamic.to_gregorian(year, month, day)
7583

7684
def parse(self, value: str) -> Union[Undate, UndateInterval]:

src/undate/undate.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ def calculate_earliest_latest(self, year, month, day):
128128
min_year = int(str(year).replace(self.MISSING_DIGIT, "0"))
129129
max_year = int(str(year).replace(self.MISSING_DIGIT, "9"))
130130
else:
131-
# use the configured min/max allowable years if we
132-
# don't have any other bounds
133-
# TODO: make calendar-specific? these are min/max for gregorian
134-
min_year = self.MIN_ALLOWABLE_YEAR
135-
max_year = self.MAX_ALLOWABLE_YEAR
131+
# if we don't have any other bounds,
132+
# use calendar-specific min year if there is one, otherwise use
133+
# the configured min/max allowable years
134+
min_year = self.calendar_converter.MIN_YEAR or self.MIN_ALLOWABLE_YEAR
135+
max_year = self.calendar_converter.MAX_YEAR or self.MAX_ALLOWABLE_YEAR
136136

137137
# if month is passed in as a string but completely unknown,
138138
# treat as unknown/none (date precision already set in init)
@@ -192,6 +192,9 @@ def calculate_earliest_latest(self, year, month, day):
192192
self.earliest = Date(
193193
*self.calendar_converter.to_gregorian(min_year, earliest_month, min_day)
194194
)
195+
print(
196+
f"initializing latest, year={max_year} month={latest_month} day={max_day}"
197+
)
195198
self.latest = Date(
196199
*self.calendar_converter.to_gregorian(max_year, latest_month, max_day)
197200
)

tests/test_undate.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,13 @@ def test_partiallyknownyear_duration(self):
467467
# year duration logic should work in other calendars
468468
# islamic
469469
assert Undate("108X", calendar="Islamic").duration().days == UnInt(354, 355)
470-
# NOTE: completely unknown years is not yet supported for other calendars, will cause an error
471-
# assert Undate("XXXX", calendar="Islamic").duration().days == UnInt(354, 355)
470+
# completely unknown years is calculated based on representative years
471+
assert Undate("XXXX", calendar="Islamic").duration().days == UnInt(354, 355)
472472
assert Undate("536X", calendar="Hebrew").duration().days == UnInt(353, 385)
473473
# different set of years could vary
474474
assert Undate("53X2", calendar="Hebrew").duration().days == UnInt(354, 385)
475+
# fully unknown year also works for Hebrew calendar
476+
assert Undate("XXX", calendar="Hebrew").duration().days == UnInt(353, 385)
475477

476478
def test_known_year(self):
477479
assert Undate(2022).known_year is True

0 commit comments

Comments
 (0)