Skip to content

Commit

Permalink
Differentiate min/max month from first/last month
Browse files Browse the repository at this point in the history
  • Loading branch information
rlskoeser committed Dec 6, 2024
1 parent 9137608 commit 920f736
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 29 deletions.
14 changes: 11 additions & 3 deletions src/undate/converters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,21 @@ class BaseCalendarConverter(BaseDateConverter):
name: str = "Base Calendar Converter"

def min_month(self) -> int:
"""First month for this calendar."""
"""Smallest numeric month for this calendar."""
raise NotImplementedError

def max_month(self) -> int:
"""Last month for this calendar."""
def max_month(self, year: int) -> int:
"""Maximum numeric month for this calendar"""
raise NotImplementedError

def first_month(self) -> int:
"""first month in this calendar; by default, returns :meth:`min_month`."""
return self.min_month()

def last_month(self, year: int) -> int:
"""last month in this calendar; by default, returns :meth:`max_month`."""
return self.max_month(year)

def max_day(self, year: int, month: int) -> int:
"""maximum numeric day for the specified year and month in this calendar"""
raise NotImplementedError
Expand Down
2 changes: 1 addition & 1 deletion src/undate/converters/calendars/gregorian.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def min_month(self) -> int:
"""First month for the Gregorian calendar."""
return 1

def max_month(self) -> int:
def max_month(self, year: int) -> int:
"""maximum numeric month for the specified year in the Gregorian calendar"""
return 12

Expand Down
19 changes: 13 additions & 6 deletions src/undate/converters/calendars/hebrew/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@ def __init__(self):
self.transformer = HebrewDateTransformer()

def min_month(self) -> int:
"""first numeric month for the specified year in this calendar"""
# hebrew calendar civil year starts in Tishri
"""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 max_month(self) -> int:
"""last numeric month for the specified year in this calendar"""
# hebrew calendar civil year starts in Tishri
# Elul is the month before 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:
Expand Down
6 changes: 3 additions & 3 deletions src/undate/converters/calendars/hijri/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ def max_day(self, year: int, month: int) -> int:
return islamic.month_length(year, month)

def min_month(self) -> int:
"""First month for this calendar."""
"""smallest numeric month for this calendar."""
return 1

def max_month(self) -> int:
"""maximum numeric month for the specified year in this calendar"""
def max_month(self, year: int) -> int:
"""maximum numeric month for this calendar"""
return 12

def to_gregorian(self, year: int, month: int, day: int) -> tuple[int, int, int]:
Expand Down
24 changes: 12 additions & 12 deletions src/undate/undate.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,24 @@ def calculate_earliest_latest(self, year, month, day):
if month == "XX":
month = None

# get first and last month from the calendar, since it is not
# always 1 and 12
# TODO need to differentiate between min/max and first/last!
# get first and last month from the calendar (not always 1 and 12)
# as well as min/max months
earliest_month = self.calendar_converter.first_month()
latest_month = self.calendar_converter.last_month(max_year)

min_month = self.calendar_converter.min_month()
max_month = self.calendar_converter.max_month()
max_month = self.calendar_converter.max_month(max_year)
if month is not None:
try:
# treat as an integer if we can
month = int(month)
# update initial value
self.initial_values["month"] = month
min_month = max_month = month
earliest_month = latest_month = month
except ValueError:
# if not, calculate min/max for missing digits
min_month, max_month = self._missing_digit_minmax(
str(month),
1,
12, # min_month, max_month
earliest_month, latest_month = self._missing_digit_minmax(
str(month), min_month, max_month
)
# similar to month above — unknown day, but day-level granularity
if day == "XX":
Expand All @@ -159,7 +159,7 @@ def calculate_earliest_latest(self, year, month, day):
rel_year = year if year and isinstance(year, int) else None
# use month if it is an integer; otherwise use previusly determined
# max month (which may not be 12 depending if partially unknown)
rel_month = month if month and isinstance(month, int) else max_month
rel_month = month if month and isinstance(month, int) else latest_month

max_day = self.calendar_converter.max_day(rel_year, rel_month)

Expand All @@ -175,10 +175,10 @@ def calculate_earliest_latest(self, year, month, day):
# convert to Gregorian calendar so earliest/latest can always
# be used for comparison
self.earliest = Date(
*self.calendar_converter.to_gregorian(min_year, min_month, min_day)
*self.calendar_converter.to_gregorian(min_year, earliest_month, min_day)
)
self.latest = Date(
*self.calendar_converter.to_gregorian(max_year, max_month, max_day)
*self.calendar_converter.to_gregorian(max_year, latest_month, max_day)
)

def set_calendar(self, calendar: Union[str, Calendar]):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,26 @@ def test_partially_known(self):
unknown_month = HebrewUndate(1243, "XX")
assert unknown_month.precision == DatePrecision.MONTH
assert unknown_month.earliest == Date(
*converter.to_gregorian(1243, converter.min_month(), 1)
*converter.to_gregorian(1243, converter.first_month(), 1)
)
max_month = converter.max_month()
last_month = converter.last_month(year=1243)
assert unknown_month.latest == Date(
*converter.to_gregorian(1243, max_month, converter.max_day(1243, max_month))
*converter.to_gregorian(
1243, last_month, converter.max_day(1243, last_month)
)
)

partially_unknown_month = HebrewUndate(1243, "1X")
assert partially_unknown_month.precision == DatePrecision.MONTH
assert partially_unknown_month.earliest == Date(
*converter.to_gregorian(1243, 10, 1)
)
# for unknown digit, assume largest possible value instead
# of last semantic monthin the year
last_month = converter.max_month(year=1243)
last_day = converter.max_day(1243, last_month)
assert partially_unknown_month.latest == Date(
*converter.to_gregorian(1243, 12, 30)
*converter.to_gregorian(1243, last_month, last_day)
)

# second month has 29 days
Expand Down

0 comments on commit 920f736

Please sign in to comment.