From 266120ff297e60ad5a8c074dc82af8248bbfc875 Mon Sep 17 00:00:00 2001 From: Peyman Daei Reaei Date: Sun, 10 May 2026 16:37:43 +0330 Subject: [PATCH 1/3] Update __init__.py --- jdatetime/__init__.py | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/jdatetime/__init__.py b/jdatetime/__init__.py index 015452a..8e9eb1f 100644 --- a/jdatetime/__init__.py +++ b/jdatetime/__init__.py @@ -20,6 +20,7 @@ MINYEAR = 1 MAXYEAR = 9377 + STRFTIME_MAPPING = { # A mapping between symbol to it's helper function and function kwargs # symbol: (helper_function_name, {kwargs}) @@ -123,6 +124,7 @@ def get_locale(): class date: + _days_in_month = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30] """date(year, month, day) --> date object""" j_months_en = [ @@ -282,6 +284,65 @@ def _is_fa_locale(self): """The smallest possible difference between non-equal date objects, timedelta(days=1).""" resolution = timedelta(1) + + @staticmethod + def calendar(year=None, month=None, locale=None): + """Display the calendar of a solar month in a weekly table. + If year and month are not given, it displays the current month. + If locale is given, the output will be in that language (fa or en), otherwise it uses the current locale. + Return: A (multi-line) string containing the calendar month. + """ + + j_days_in_month = date._days_in_month + + if year is None or month is None: + today = date.today() + year = today.year + month = today.month + else: + + if year < MINYEAR or year > MAXYEAR: + raise ValueError('year out of range') + if month < 1 or month > 12: + raise ValueError('month must be 1..12') + + + temp_date = date(year, month, 1, locale=locale) if locale else date(year, month, 1) + + + if month == 12 and temp_date.isleap(): + days_in_month = 30 + else: + days_in_month = date._days_in_month[month-1] + + + first_weekday = temp_date.weekday() + + + + calendar_days = [' '] * first_weekday + [f'{d:2d}' for d in range(1, days_in_month+1)] + + + weeks = [calendar_days[i:i+7] for i in range(0, len(calendar_days), 7)] + + + month_name = temp_date.jmonth() + title = f"{month_name} {year}".center(20) + + + if temp_date._is_fa_locale(): + weekday_headers = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'] + else: + weekday_headers = ['Sa', 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr'] + + header_line = ' '.join(weekday_headers) + + + lines = [title, header_line] + for week in weeks: + lines.append(' '.join(week)) + + return '\n'.join(lines) def isleap(self): """check if year is leap year @@ -603,6 +664,8 @@ def repl(match): def aslocale(self, locale): return date(self.year, self.month, self.day, locale=locale) + + """The earliest representable date, date(MINYEAR, 1, 1)""" @@ -1263,3 +1326,4 @@ def _strftime_z(self): def _strftime_cap_z(self): return self.tzname() or '' + From f7463d334b108b425c1a7902294e8acb6fd7552b Mon Sep 17 00:00:00 2001 From: Peyman Daei Reaei Date: Sun, 10 May 2026 16:40:02 +0330 Subject: [PATCH 2/3] Update __init__.py feat: add static calender method to jdatetime.date class From 78a0a0fa32c3c96901b45a916aa2d389aeb1e6b6 Mon Sep 17 00:00:00 2001 From: Peyman Date: Tue, 12 May 2026 15:31:47 +0430 Subject: [PATCH 3/3] upload new test jdate --- tests/test_jdate.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_jdate.py b/tests/test_jdate.py index d9ec0ab..453bef7 100644 --- a/tests/test_jdate.py +++ b/tests/test_jdate.py @@ -184,3 +184,38 @@ def test_fromisoformat(self): with self.assertRaises(TypeError, msg='fromisoformat: argument must be str'): jdatetime.date.fromisoformat(1) + def test_calendar_current_month(self): + cal = jdatetime.date.calendar() + self.assertIsInstance(cal, str) + self.assertTrue(len(cal) > 0) + + def test_calendar_specific_month(self): + cal = jdatetime.date.calendar(1405, 2) + self.assertIn('Ordibehesht', cal) + self.assertIn('1405', cal) + + def test_calendar_structure(self): + cal = jdatetime.date.calendar(1400, 1) + lines = cal.split('\n') + self.assertEqual(lines[1].strip(), 'Sa Su Mo Tu We Th Fr') + + def test_calendar_persian_locale(self): + jdatetime.set_locale('fa_IR') + cal = jdatetime.date.calendar(1405, 2) + jdatetime.set_locale(None) + self.assertIn('اردیبهشت', cal) + self.assertIn('ش ی د س چ پ ج', cal) + + def test_calendar_leap_year(self): + cal = jdatetime.date.calendar(1403, 12) + self.assertIn('30', cal) + + def test_calendar_invalid_input(self): + with self.assertRaises(ValueError): + jdatetime.date.calendar(1405, 13) + + with self.assertRaises(ValueError): + jdatetime.date.calendar(10000, 1) + +if __name__ == '__main__': + unittest.main()