Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Currently available converters are "ISO8601" and "EDTF" and supported calendars.

### Calendars

All `Undate` objects are calendar aware, and date converters include support for parsing and working with dates from other calendars. The Gregorian calendar is used by default; currently `undate` supports the Hijri Islamic calendar and the Anno Mundi Hebrew calendar based on calendar convertion logic implemented in the [convertdate](https://convertdate.readthedocs.io/en/latest/)package.
All `Undate` objects are calendar aware, and date converters include support for parsing and working with dates from other calendars. The Gregorian calendar is used by default; currently `undate` supports the Islamic Hijri calendar and the Hebrew Anno Mundi calendar based on calendar conversion logic implemented in the [convertdate](https://convertdate.readthedocs.io/en/latest/) package.

Dates are stored with the year, month, day and appropriate precision for the original calendar; internally, earliest and latest dates are calculated in Gregorian / Proleptic Gregorian calendar for standardized comparison across dates from different calendars.

Expand Down
12 changes: 6 additions & 6 deletions docs/undate/converters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ Gregorian
.. automodule:: undate.converters.calendars.gregorian
:members:

Hijri (Islamic calendar)
^^^^^^^^^^^^^^^^^^^^^^^^
Hebrew Anno Mundi calendar
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. automodule:: undate.converters.calendars.hijri.converter
.. automodule:: undate.converters.calendars.hebrew.converter
:members:

Anno Mundi (Hebrew calendar)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Islamic Hijri calendar
^^^^^^^^^^^^^^^^^^^^^^^^

.. automodule:: undate.converters.calendars.hebrew.converter
.. automodule:: undate.converters.calendars.islamic.converter
:members:

6 changes: 3 additions & 3 deletions src/undate/converters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
implementing date converters, which can provide support for
parsing and generating dates in different formats.
The converter subclass :class:`undate.converters.BaseCalendarConverter`
provides additional functionaly needed for calendar conversion.
provides additional functionality needed for calendar conversion.

To add support for a new date converter:

Expand All @@ -23,10 +23,10 @@

- Create a new file under ``undate/converters/calendars/``
- For converters with sufficient complexity, you may want to create a submodule;
see ``undate.converters.calendars.hijri`` for an example.
see ``undate.converters.calendars.islamic`` for an example.
- Extend ``BaseCalendarConverter`` and implement ``parse`` and ``to_string``
formatter methods as desired/appropriate for your converter as well as the
additional methods for ``max_month``, ``max_day``, and convertion ``to_gregorian``
additional methods for ``max_month``, ``max_day``, and conversion ``to_gregorian``
calendar.
- Import your calendar in ``undate/converters/calendars/__init__.py`` and include in `__all__``
- Add unit tests for the new calendar logic under ``tests/test_converters/calendars/``
Expand Down
4 changes: 2 additions & 2 deletions src/undate/converters/calendars/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from undate.converters.calendars.gregorian import GregorianDateConverter
from undate.converters.calendars.hijri import HijriDateConverter
from undate.converters.calendars.hebrew import HebrewDateConverter
from undate.converters.calendars.islamic import IslamicDateConverter

__all__ = ["HijriDateConverter", "GregorianDateConverter", "HebrewDateConverter"]
__all__ = ["GregorianDateConverter", "HebrewDateConverter", "IslamicDateConverter"]
3 changes: 0 additions & 3 deletions src/undate/converters/calendars/hijri/__init__.py

This file was deleted.

9 changes: 0 additions & 9 deletions src/undate/converters/calendars/hijri/parser.py

This file was deleted.

3 changes: 3 additions & 0 deletions src/undate/converters/calendars/islamic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from undate.converters.calendars.islamic.converter import IslamicDateConverter

__all__ = ["IslamicDateConverter"]
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@

from undate import Undate, UndateInterval
from undate.converters.base import BaseCalendarConverter
from undate.converters.calendars.hijri.parser import hijri_parser
from undate.converters.calendars.hijri.transformer import HijriDateTransformer
from undate.converters.calendars.islamic.parser import islamic_parser
from undate.converters.calendars.islamic.transformer import IslamicDateTransformer


class HijriDateConverter(BaseCalendarConverter):
class IslamicDateConverter(BaseCalendarConverter):
"""
Converter for Hijri / Islamic calendar.
Converter for Islamic Hijri calendar.

Support for parsing Hijri dates and converting to Undate and UndateInterval
Support for parsing Islamic Hijri dates and converting to Undate and UndateInterval
objects in the Gregorian calendar.
"""

#: converter name: Hijri
name: str = "Hijri"
calendar_name: str = "Hijrī"
#: converter name: Islamic
name: str = "Islamic"
calendar_name: str = "Islamic"

def __init__(self):
self.transformer = HijriDateTransformer()
self.transformer = IslamicDateTransformer()

def max_day(self, year: int, month: int) -> int:
"""maximum numeric day for the specified year and month in this calendar"""
Expand All @@ -44,24 +44,24 @@ def to_gregorian(self, year: int, month: int, day: int) -> tuple[int, int, int]:

def parse(self, value: str) -> Union[Undate, UndateInterval]:
"""
Parse a Hijri date string and return an :class:`~undate.undate.Undate` or
Parse an Islamic/Hijri date string and return an :class:`~undate.undate.Undate` or
:class:`~undate.undate.UndateInterval`.
The Hijri date string is preserved in the undate label.
The Islamic/Hijri 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 Hijri date parser
parsetree = hijri_parser.parse(value)
# parse the string with our Islamic Hijri date parser
parsetree = islamic_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 Hijri date") from err
raise ValueError(f"Could not parse '{value}' as an Islamic date") from err

# do we need to support conversion the other direction?
# i.e., generate a Hijri date from an abitrary undate or undate interval?
# i.e., generate an Islamic Hijri date from an arbitrary undate or undate interval?
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// only support day month year format for now
// parser requires numeric day and year to be distinguished based on order
hijri_date: day month year | month year | year
islamic_date: day month year | month year | year

// TODO: handle date ranges?

Expand Down
9 changes: 9 additions & 0 deletions src/undate/converters/calendars/islamic/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import pathlib

from lark import Lark

grammar_path = pathlib.Path(__file__).parent / "islamic.lark"

with open(grammar_path) as grammar:
# NOTE: LALR parser is faster but can't be used due to ambiguity between years and days
islamic_parser = Lark(grammar.read(), start="islamic_date", strict=True)
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
from undate import Undate, Calendar


class HijriUndate(Undate):
"""Undate convience subclass; sets default calendar to Hijri."""
class IslamicUndate(Undate):
"""Undate convience subclass; sets default calendar to Islamic."""

calendar = Calendar.HIJRI
calendar = Calendar.ISLAMIC


class HijriDateTransformer(Transformer):
"""Transform a Hijri date parse tree and return an Undate or
class IslamicDateTransformer(Transformer):
"""Transform an Islamic Hijri date parse tree and return an Undate or
UndateInterval."""

def hijri_date(self, items):
def islamic_date(self, items):
parts = {}
for child in items:
if child.data in ["year", "month", "day"]:
Expand All @@ -24,7 +24,7 @@ def hijri_date(self, items):

# initialize and return an undate with islamic year, month, day and
# islamic calendar
return HijriUndate(**parts)
return IslamicUndate(**parts)

# year translation is not needed since we want a tree with name year
# this is equivalent to a no-op
Expand Down
2 changes: 1 addition & 1 deletion src/undate/undate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class Calendar(StrEnum):
"""Supported calendars"""

GREGORIAN = auto()
HIJRI = auto()
HEBREW = auto()
ISLAMIC = auto()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Calendar enum updated from HIJRI to ISLAMIC

This change is part of the broader effort to transition from "Hijri" to "Islamic" terminology throughout the codebase. Make sure all references to this enum are also updated to maintain consistency.


🏁 Script executed:

#!/bin/bash
# Find any remaining references to HIJRI that might need updating
rg -i "HIJRI|hijri" --type py

Length of output: 1166


Action Required: Update remaining 'Hijri' references to 'Islamic' across the codebase

The update to the enum in src/undate/undate.py (line 31) using ISLAMIC = auto() is correct. However, our search revealed several remaining references to "Hijri" in related modules and tests that need to be updated for a consistent terminology change:

  • Tests:
    • tests/test_converters/test_calendars/test_islamic/test_islamic_converter.py contains comments referring to "hijri dates".
  • Converters:
    • src/undate/converters/calendars/islamic/converter.py includes multiple docstrings and inline comments still using "Islamic Hijri" and "Hijri".
    • src/undate/converters/calendars/islamic/transformer.py similarly references "Islamic Hijri".

Please update these references (both in comments and docstrings) to align with the new "Islamic" terminology.


@staticmethod
def get_converter(calendar):
Expand Down
6 changes: 3 additions & 3 deletions tests/test_converters/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from undate.converters.calendars import (
GregorianDateConverter,
HebrewDateConverter,
HijriDateConverter,
IslamicDateConverter,
)


Expand Down Expand Up @@ -36,12 +36,12 @@ def test_parse_to_string(self):

def test_subclasses(self):
# define a nested subclass
class SubSubConverter(HijriDateConverter):
class SubSubConverter(IslamicDateConverter):
pass

subclasses = BaseDateConverter.subclasses()
assert BaseCalendarConverter not in subclasses
assert HijriDateConverter in subclasses
assert IslamicDateConverter in subclasses
assert HebrewDateConverter in subclasses
assert GregorianDateConverter in subclasses
assert SubSubConverter in subclasses
Expand Down
Loading