persiantools 5.1.1__tar.gz → 5.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {persiantools-5.1.1 → persiantools-5.2.0}/LICENSE +1 -1
  2. {persiantools-5.1.1/persiantools.egg-info → persiantools-5.2.0}/PKG-INFO +1 -1
  3. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools/__init__.py +1 -1
  4. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools/characters.py +6 -3
  5. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools/jdatetime.py +130 -101
  6. {persiantools-5.1.1 → persiantools-5.2.0/persiantools.egg-info}/PKG-INFO +1 -1
  7. {persiantools-5.1.1 → persiantools-5.2.0}/tests/test_characters.py +10 -0
  8. {persiantools-5.1.1 → persiantools-5.2.0}/tests/test_digits.py +3 -0
  9. {persiantools-5.1.1 → persiantools-5.2.0}/tests/test_jalalidate.py +44 -8
  10. {persiantools-5.1.1 → persiantools-5.2.0}/MANIFEST.in +0 -0
  11. {persiantools-5.1.1 → persiantools-5.2.0}/README.md +0 -0
  12. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools/digits.py +0 -0
  13. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools/utils.py +0 -0
  14. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools.egg-info/SOURCES.txt +0 -0
  15. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools.egg-info/dependency_links.txt +0 -0
  16. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools.egg-info/not-zip-safe +0 -0
  17. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools.egg-info/requires.txt +0 -0
  18. {persiantools-5.1.1 → persiantools-5.2.0}/persiantools.egg-info/top_level.txt +0 -0
  19. {persiantools-5.1.1 → persiantools-5.2.0}/pyproject.toml +0 -0
  20. {persiantools-5.1.1 → persiantools-5.2.0}/setup.cfg +0 -0
  21. {persiantools-5.1.1 → persiantools-5.2.0}/setup.py +0 -0
  22. {persiantools-5.1.1 → persiantools-5.2.0}/tests/test_jalalidatetime.py +0 -0
  23. {persiantools-5.1.1 → persiantools-5.2.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2016-2024 Majid Hajiloo
3
+ Copyright (c) 2016-2025 Majid Hajiloo
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: persiantools
3
- Version: 5.1.1
3
+ Version: 5.2.0
4
4
  Summary: Jalali date and datetime with other tools
5
5
  Home-page: https://github.com/majiidd/persiantools
6
6
  Author: Majid Hajiloo
@@ -7,7 +7,7 @@
7
7
 
8
8
  __title__ = "persiantools"
9
9
  __url__ = "https://github.com/majiidd/persiantools"
10
- __version__ = "5.1.1"
10
+ __version__ = "5.2.0"
11
11
  __build__ = __version__
12
12
  __author__ = "Majid Hajiloo"
13
13
  __author_email__ = "majid.hajiloo@gmail.com"
@@ -1,4 +1,4 @@
1
- from persiantools.utils import replace
1
+ import re
2
2
 
3
3
  CHARACTER_MAP_AR_TO_FA = {
4
4
  "دِ": "د",
@@ -14,6 +14,9 @@ CHARACTER_MAP_AR_TO_FA = {
14
14
 
15
15
  CHARACTER_MAP_FA_TO_AR = {"ی": "ي", "ک": "ك"}
16
16
 
17
+ AR_TO_FA_PATTERN = re.compile("|".join(map(re.escape, CHARACTER_MAP_AR_TO_FA.keys())))
18
+ FA_TO_AR_PATTERN = re.compile("|".join(map(re.escape, CHARACTER_MAP_FA_TO_AR.keys())))
19
+
17
20
 
18
21
  def ar_to_fa(string: str) -> str:
19
22
  """
@@ -35,7 +38,7 @@ def ar_to_fa(string: str) -> str:
35
38
  if not isinstance(string, str):
36
39
  raise TypeError("Input must be of type str")
37
40
 
38
- return replace(string, CHARACTER_MAP_AR_TO_FA)
41
+ return AR_TO_FA_PATTERN.sub(lambda match: CHARACTER_MAP_AR_TO_FA[match.group(0)], string)
39
42
 
40
43
 
41
44
  def fa_to_ar(string: str) -> str:
@@ -58,4 +61,4 @@ def fa_to_ar(string: str) -> str:
58
61
  if not isinstance(string, str):
59
62
  raise TypeError("Input must be of type str")
60
63
 
61
- return replace(string, CHARACTER_MAP_FA_TO_AR)
64
+ return FA_TO_AR_PATTERN.sub(lambda match: CHARACTER_MAP_FA_TO_AR[match.group(0)], string)
@@ -132,90 +132,21 @@ _MONTH_COUNT = [
132
132
  _FRACTION_CORRECTION = [100000, 10000, 1000, 100, 10]
133
133
 
134
134
  # List of years that are exceptions to the 33-year leap year rule
135
- NON_LEAP_CORRECTION = [
136
- 1502,
137
- 1601,
138
- 1634,
139
- 1667,
140
- 1700,
141
- 1733,
142
- 1766,
143
- 1799,
144
- 1832,
145
- 1865,
146
- 1898,
147
- 1931,
148
- 1964,
149
- 1997,
150
- 2030,
151
- 2059,
152
- 2063,
153
- 2096,
154
- 2129,
155
- 2158,
156
- 2162,
157
- 2191,
158
- 2195,
159
- 2224,
160
- 2228,
161
- 2257,
162
- 2261,
163
- 2290,
164
- 2294,
165
- 2323,
166
- 2327,
167
- 2356,
168
- 2360,
169
- 2389,
170
- 2393,
171
- 2422,
172
- 2426,
173
- 2455,
174
- 2459,
175
- 2488,
176
- 2492,
177
- 2521,
178
- 2525,
179
- 2554,
180
- 2558,
181
- 2587,
182
- 2591,
183
- 2620,
184
- 2624,
185
- 2653,
186
- 2657,
187
- 2686,
188
- 2690,
189
- 2719,
190
- 2723,
191
- 2748,
192
- 2752,
193
- 2756,
194
- 2781,
195
- 2785,
196
- 2789,
197
- 2818,
198
- 2822,
199
- 2847,
200
- 2851,
201
- 2855,
202
- 2880,
203
- 2884,
204
- 2888,
205
- 2913,
206
- 2917,
207
- 2921,
208
- 2946,
209
- 2950,
210
- 2954,
211
- 2979,
212
- 2983,
213
- 2987,
214
- ]
215
-
216
- NON_LEAP_CORRECTION_SET = frozenset(NON_LEAP_CORRECTION)
217
-
218
- MIN_NON_LEAP_CORRECTION = NON_LEAP_CORRECTION[0]
135
+ # fmt: off
136
+ NON_LEAP_CORRECTION_SET = frozenset(
137
+ [
138
+ 1502, 1601, 1634, 1667, 1700, 1733, 1766, 1799, 1832, 1865, 1898, 1931,
139
+ 1964, 1997, 2030, 2059, 2063, 2096, 2129, 2158, 2162, 2191, 2195, 2224,
140
+ 2228, 2257, 2261, 2290, 2294, 2323, 2327, 2356, 2360, 2389, 2393, 2422,
141
+ 2426, 2455, 2459, 2488, 2492, 2521, 2525, 2554, 2558, 2587, 2591, 2620,
142
+ 2624, 2653, 2657, 2686, 2690, 2719, 2723, 2748, 2752, 2756, 2781, 2785,
143
+ 2789, 2818, 2822, 2847, 2851, 2855, 2880, 2884, 2888, 2913, 2917, 2921,
144
+ 2946, 2950, 2954, 2979, 2983, 2987,
145
+ ]
146
+ )
147
+ # fmt: on
148
+
149
+ MIN_NON_LEAP_CORRECTION = 1502
219
150
 
220
151
 
221
152
  def _is_ascii_digit(c: str) -> bool:
@@ -270,10 +201,7 @@ class JalaliDate:
270
201
  raise ValueError("locale must be 'en' or 'fa'")
271
202
 
272
203
  if isinstance(year, JalaliDate) and month is None:
273
- month = year.month
274
- day = year.day
275
- locale = year.locale
276
- year = year.year
204
+ year, month, day, locale = year.year, year.month, year.day, year.locale
277
205
 
278
206
  elif isinstance(year, date):
279
207
  jdate = self.to_jalali(year)
@@ -284,32 +212,49 @@ class JalaliDate:
284
212
  ):
285
213
  self.__setstate__(year)
286
214
 
287
- year = self._year
288
- month = self._month
289
- day = self._day
290
-
291
- year, month, day, locale = self._check_date_fields(year, month, day, locale)
215
+ year, month, day = self._year, self._month, self._day
292
216
 
293
- self._year = year
294
- self._month = month
295
- self._day = day
296
- self._locale = locale
217
+ self._year, self._month, self._day, self._locale = self._check_date_fields(year, month, day, locale)
297
218
  self._hashcode = -1
298
219
 
299
220
  @property
300
221
  def year(self) -> int:
222
+ """
223
+ Get the year component of the date.
224
+
225
+ Returns:
226
+ int: The year as an integer.
227
+ """
301
228
  return self._year
302
229
 
303
230
  @property
304
231
  def month(self) -> int:
232
+ """
233
+ Get the month component of the date.
234
+
235
+ Returns:
236
+ int: The month as an integer.
237
+ """
305
238
  return self._month
306
239
 
307
240
  @property
308
241
  def day(self) -> int:
242
+ """
243
+ Get the day of the month.
244
+
245
+ Returns:
246
+ int: The day of the month.
247
+ """
309
248
  return self._day
310
249
 
311
250
  @property
312
251
  def locale(self):
252
+ """
253
+ Get the locale setting for the current instance.
254
+
255
+ Returns:
256
+ str: The locale setting.
257
+ """
313
258
  return self._locale
314
259
 
315
260
  @locale.setter
@@ -394,7 +339,7 @@ class JalaliDate:
394
339
  Returns:
395
340
  bool: True if the year is a leap year, False otherwise.
396
341
  """
397
- if not MINYEAR <= year <= MAXYEAR:
342
+ if not (MINYEAR <= year <= MAXYEAR):
398
343
  raise ValueError(f"Year must be between {MINYEAR} and {MAXYEAR}")
399
344
 
400
345
  if year < MIN_NON_LEAP_CORRECTION:
@@ -653,7 +598,19 @@ class JalaliDate:
653
598
 
654
599
  @classmethod
655
600
  def fromisoformat(cls, date_string: str):
656
- """Construct a date from the output of JalaliDate.isoformat()."""
601
+ """
602
+ Construct a JalaliDate from an ISO 8601 formatted date string.
603
+
604
+ Args:
605
+ date_string (str): The date string in ISO 8601 format.
606
+
607
+ Returns:
608
+ JalaliDate: A JalaliDate object corresponding to the given date string.
609
+
610
+ Raises:
611
+ TypeError: If the provided argument is not a string.
612
+ ValueError: If the provided string is not a valid ISO 8601 formatted date.
613
+ """
657
614
  if not isinstance(date_string, str):
658
615
  raise TypeError("fromisoformat: argument must be str")
659
616
 
@@ -751,6 +708,21 @@ class JalaliDate:
751
708
  return cls(date.fromtimestamp(timestamp))
752
709
 
753
710
  def weekday(self) -> int:
711
+ """
712
+ Returns the day of the week as an integer.
713
+
714
+ The days of the week are represented as follows:
715
+ 0 - Shanbeh
716
+ 1 - Yekshanbeh
717
+ 2 - Doshanbeh
718
+ 3 - Seshanbeh
719
+ 4 - Chaharshanbeh
720
+ 5 - Panjshanbeh
721
+ 6 - Jomeh
722
+
723
+ Returns:
724
+ int: An integer representing the day of the week.
725
+ """
754
726
  return (self.toordinal() + 4) % 7
755
727
 
756
728
  def __format__(self, fmt: str):
@@ -762,9 +734,23 @@ class JalaliDate:
762
734
  return str(self)
763
735
 
764
736
  def isoweekday(self) -> int:
737
+ """
738
+ Return the ISO weekday.
739
+
740
+ The ISO weekday is a number representing the day of the week, where Shanbeh is 1 and Jomeh is 7.
741
+
742
+ Returns:
743
+ int: An integer representing the ISO weekday.
744
+ """
765
745
  return self.weekday() + 1
766
746
 
767
747
  def week_of_year(self) -> int:
748
+ """
749
+ Calculate the week number of the year for the current Jalali date.
750
+
751
+ Returns:
752
+ int: The week number of the year, starting from 1.
753
+ """
768
754
  o = JalaliDate(self._year, 1, 1).weekday()
769
755
  days = self.days_before_month(self._month) + self._day + o
770
756
 
@@ -776,10 +762,24 @@ class JalaliDate:
776
762
  return week_no
777
763
 
778
764
  def isocalendar(self):
779
- """Return a 3-tuple containing ISO year, week number, and weekday."""
765
+ """
766
+ Return a 3-tuple containing ISO year, week number, and weekday.
767
+
768
+ Returns:
769
+ tuple: A tuple containing the ISO year, ISO week number, and ISO weekday.
770
+ """
780
771
  return self.year, self.week_of_year(), self.isoweekday()
781
772
 
782
773
  def ctime(self) -> str:
774
+ """
775
+ Return a string representing the date and time in a locale’s appropriate format.
776
+
777
+ This method uses the strftime() function with the format code "%c" to generate
778
+ a string representation of the date and time.
779
+
780
+ Returns:
781
+ str: A string representing the date and time.
782
+ """
783
783
  return self.strftime("%c")
784
784
 
785
785
  def strftime(self, fmt: str, locale=None) -> str:
@@ -923,7 +923,6 @@ class JalaliDate:
923
923
  __radd__ = __add__
924
924
 
925
925
  def __sub__(self, other):
926
- """Subtract two JalaliDates/dates, or a JalaliDate/date and a timedelta."""
927
926
  if isinstance(other, timedelta):
928
927
  return self + timedelta(-other.days)
929
928
 
@@ -1030,22 +1029,52 @@ class JalaliDateTime(JalaliDate):
1030
1029
 
1031
1030
  @property
1032
1031
  def hour(self) -> int:
1032
+ """
1033
+ Get the hour component of the datetime.
1034
+
1035
+ Returns:
1036
+ int: The hour component of the datetime.
1037
+ """
1033
1038
  return self._hour
1034
1039
 
1035
1040
  @property
1036
1041
  def minute(self) -> int:
1042
+ """
1043
+ Get the minute component of the datetime.
1044
+
1045
+ Returns:
1046
+ int: The minute component of the datetime.
1047
+ """
1037
1048
  return self._minute
1038
1049
 
1039
1050
  @property
1040
1051
  def second(self) -> int:
1052
+ """
1053
+ Get the second component of the time.
1054
+
1055
+ Returns:
1056
+ int: The second component of the time.
1057
+ """
1041
1058
  return self._second
1042
1059
 
1043
1060
  @property
1044
1061
  def microsecond(self) -> int:
1062
+ """
1063
+ Returns the microsecond component of the datetime.
1064
+
1065
+ Returns:
1066
+ int: The microsecond component of the datetime.
1067
+ """
1045
1068
  return self._microsecond
1046
1069
 
1047
1070
  @property
1048
1071
  def tzinfo(self):
1072
+ """
1073
+ Returns the time zone information associated with this datetime object.
1074
+
1075
+ :return: The time zone information.
1076
+ :rtype: tzinfo
1077
+ """
1049
1078
  return self._tzinfo
1050
1079
 
1051
1080
  @classmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: persiantools
3
- Version: 5.1.1
3
+ Version: 5.2.0
4
4
  Summary: Jalali date and datetime with other tools
5
5
  Home-page: https://github.com/majiidd/persiantools
6
6
  Author: Majid Hajiloo
@@ -24,11 +24,18 @@ class TestDigits(TestCase):
24
24
  with pytest.raises(TypeError):
25
25
  characters.ar_to_fa(12345)
26
26
 
27
+ with self.assertRaises(TypeError):
28
+ characters.ar_to_fa(None)
29
+
27
30
  orig = "السلام عليكم ٠١٢٣٤٥٦٧٨٩"
28
31
  converted = characters.ar_to_fa(orig)
29
32
  converted = digits.ar_to_fa(converted)
30
33
  self.assertEqual(converted, "السلام علیکم ۰۱۲۳۴۵۶۷۸۹")
31
34
 
35
+ input_string = "يياكشيسِ"
36
+ expected_output = "ییاکشیس"
37
+ self.assertEqual(characters.ar_to_fa(input_string), expected_output)
38
+
32
39
  def test_fa_to_fa(self):
33
40
  self.assertEqual(characters.ar_to_fa("السلام علیکم"), "السلام علیکم")
34
41
  self.assertEqual(characters.ar_to_fa("کیک"), "کیک")
@@ -48,3 +55,6 @@ class TestDigits(TestCase):
48
55
 
49
56
  with pytest.raises(TypeError):
50
57
  characters.fa_to_ar(12345)
58
+
59
+ with self.assertRaises(TypeError):
60
+ characters.fa_to_ar(None)
@@ -85,5 +85,8 @@ class TestDigits(TestCase):
85
85
  with pytest.raises(digits.OutOfRangeException):
86
86
  digits.to_word(1000000000000001)
87
87
 
88
+ with pytest.raises(digits.OutOfRangeException):
89
+ digits.to_word(0.123456789012345)
90
+
88
91
  with pytest.raises(TypeError):
89
92
  digits.to_word("123")
@@ -12,9 +12,7 @@ from persiantools.jdatetime import MAXYEAR, MINYEAR, JalaliDate
12
12
  class TestJalaliDate(TestCase):
13
13
  def test_shamsi_to_gregorian(self):
14
14
  cases = [
15
- (JalaliDate(1210, 12, 29), date(1832, 3, 19)),
16
- (JalaliDate(1210, 12, 30), date(1832, 3, 20)),
17
- (JalaliDate(1211, 1, 1), date(1832, 3, 21)),
15
+ (JalaliDate(1100, 1, 1), date(1721, 3, 21)),
18
16
  (JalaliDate(1367, 2, 14), date(1988, 5, 4)),
19
17
  (JalaliDate(1395, 3, 21), date(2016, 6, 10)),
20
18
  (JalaliDate(1395, 12, 9), date(2017, 2, 27)),
@@ -33,6 +31,7 @@ class TestJalaliDate(TestCase):
33
31
  (JalaliDate(1403, 4, 8), date(2024, 6, 28)),
34
32
  (JalaliDate(1403, 8, 18), date(2024, 11, 8)),
35
33
  (JalaliDate(1403, 10, 27), date(2025, 1, 16)),
34
+ (JalaliDate(1210, 12, 29), date(1832, 3, 19)),
36
35
  (JalaliDate(1367, 12, 29), date(1989, 3, 20)),
37
36
  (JalaliDate(1392, 12, 29), date(2014, 3, 20)),
38
37
  (JalaliDate(1398, 12, 29), date(2020, 3, 19)),
@@ -40,6 +39,8 @@ class TestJalaliDate(TestCase):
40
39
  (JalaliDate(1400, 12, 29), date(2022, 3, 20)),
41
40
  (JalaliDate(1402, 12, 29), date(2024, 3, 19)),
42
41
  (JalaliDate(1403, 12, 29), date(2025, 3, 19)),
42
+ (JalaliDate(1504, 12, 29), date(2126, 3, 20)),
43
+ (JalaliDate(1210, 12, 30), date(1832, 3, 20)),
43
44
  (JalaliDate(1391, 12, 30), date(2013, 3, 20)),
44
45
  (JalaliDate(1395, 12, 30), date(2017, 3, 20)),
45
46
  (JalaliDate(1399, 12, 30), date(2021, 3, 20)),
@@ -55,6 +56,7 @@ class TestJalaliDate(TestCase):
55
56
  (JalaliDate(1400, 10, 11), date(2022, 1, 1)),
56
57
  (JalaliDate(1402, 10, 11), date(2024, 1, 1)),
57
58
  (JalaliDate(1403, 10, 12), date(2025, 1, 1)),
59
+ (JalaliDate(1211, 1, 1), date(1832, 3, 21)),
58
60
  (JalaliDate(1367, 1, 1), date(1988, 3, 21)),
59
61
  (JalaliDate(1388, 1, 1), date(2009, 3, 21)),
60
62
  (JalaliDate(1396, 1, 1), date(2017, 3, 21)),
@@ -64,6 +66,7 @@ class TestJalaliDate(TestCase):
64
66
  (JalaliDate(1402, 1, 1), date(2023, 3, 21)),
65
67
  (JalaliDate(1403, 1, 1), date(2024, 3, 20)),
66
68
  (JalaliDate(1404, 1, 1), date(2025, 3, 21)),
69
+ (JalaliDate(1505, 1, 1), date(2126, 3, 21)),
67
70
  (JalaliDate.today(), date.today()),
68
71
  ]
69
72
  for jdate, gdate in cases:
@@ -71,9 +74,6 @@ class TestJalaliDate(TestCase):
71
74
 
72
75
  def test_gregorian_to_shamsi(self):
73
76
  cases = [
74
- (date(1832, 3, 19), JalaliDate(1210, 12, 29)),
75
- (date(1832, 3, 20), JalaliDate(1210, 12, 30)),
76
- (date(1832, 3, 21), JalaliDate(1211, 1, 1)),
77
77
  (date(1988, 5, 4), JalaliDate(1367, 2, 14)),
78
78
  (date(2122, 1, 31), JalaliDate(1500, 11, 11)),
79
79
  (date(2017, 10, 19), JalaliDate(1396, 7, 27)),
@@ -90,9 +90,12 @@ class TestJalaliDate(TestCase):
90
90
  (date(2000, 12, 31), JalaliDate(1379, 10, 11)),
91
91
  (date(2023, 12, 31), JalaliDate(1402, 10, 10)),
92
92
  (date(2024, 12, 31), JalaliDate(1403, 10, 11)),
93
+ (date(1832, 3, 19), JalaliDate(1210, 12, 29)),
94
+ (date(1832, 3, 20), JalaliDate(1210, 12, 30)),
93
95
  (date(2017, 3, 20), JalaliDate(1395, 12, 30)),
94
96
  (date(2021, 3, 20), JalaliDate(1399, 12, 30)),
95
97
  (date(2025, 3, 20), JalaliDate(1403, 12, 30)),
98
+ (date(1832, 3, 21), JalaliDate(1211, 1, 1)),
96
99
  (date(2000, 1, 1), JalaliDate(1378, 10, 11)),
97
100
  (date(2012, 1, 1), JalaliDate(1390, 10, 11)),
98
101
  (date(2013, 1, 1), JalaliDate(1391, 10, 12)),
@@ -107,6 +110,12 @@ class TestJalaliDate(TestCase):
107
110
  (date(2023, 3, 21), JalaliDate(1402, 1, 1)),
108
111
  (date(2024, 3, 20), JalaliDate(1403, 1, 1)),
109
112
  (date(2025, 3, 21), JalaliDate(1404, 1, 1)),
113
+ (date(1827, 3, 22), JalaliDate(1206, 1, 1)),
114
+ (date(1828, 3, 21), JalaliDate(1207, 1, 1)),
115
+ (date(1839, 3, 21), JalaliDate(1218, 1, 1)),
116
+ (date(1864, 3, 20), JalaliDate(1243, 1, 1)),
117
+ (date(2118, 3, 21), JalaliDate(1497, 1, 1)),
118
+ (date(2119, 3, 21), JalaliDate(1498, 1, 1)),
110
119
  (date.today(), JalaliDate.today()),
111
120
  ]
112
121
  for gdate, jdate in cases:
@@ -279,6 +288,8 @@ class TestJalaliDate(TestCase):
279
288
  # Known non-leap years
280
289
  (1206, False),
281
290
  (1207, False),
291
+ (1208, False),
292
+ (1209, False),
282
293
  (1211, False),
283
294
  (1215, False),
284
295
  (1216, False),
@@ -302,6 +313,10 @@ class TestJalaliDate(TestCase):
302
313
  (1409, False),
303
314
  (1410, False),
304
315
  (1411, False),
316
+ (1493, False),
317
+ (1495, False),
318
+ (1496, False),
319
+ (1497, False),
305
320
  ]
306
321
  for year, is_leap in cases:
307
322
  self.assertEqual(JalaliDate.is_leap(year), is_leap)
@@ -413,6 +428,7 @@ class TestJalaliDate(TestCase):
413
428
  self.assertEqual(JalaliDate(1399, 1, 2).week_of_year(), 2)
414
429
  self.assertEqual(JalaliDate(1403, 1, 5).week_of_year(), 2)
415
430
  self.assertEqual(JalaliDate(1403, 4, 3).week_of_year(), 15)
431
+ self.assertEqual(JalaliDate(1403, 10, 28).week_of_year(), 44)
416
432
 
417
433
  self.assertEqual(JalaliDate(1367, 2, 14).weekday(), 4)
418
434
  self.assertEqual(JalaliDate(1393, 1, 1).weekday(), 6)
@@ -425,10 +441,13 @@ class TestJalaliDate(TestCase):
425
441
  self.assertEqual(JalaliDate(1397, 1, 1).weekday(), 4)
426
442
  self.assertEqual(JalaliDate(1397, 11, 29).weekday(), 2)
427
443
  self.assertEqual(JalaliDate(1400, 1, 1).weekday(), 1)
444
+ self.assertEqual(JalaliDate(1403, 10, 28).weekday(), 6)
445
+
428
446
  self.assertEqual(JalaliDate(1403, 4, 3).isoweekday(), 2)
429
447
  self.assertEqual(JalaliDate(1400, 1, 1).isoweekday(), 2)
430
448
  self.assertEqual(JalaliDate(1396, 7, 27).isoweekday(), 6)
431
449
  self.assertEqual(JalaliDate(1397, 11, 29).isoweekday(), 3)
450
+ self.assertEqual(JalaliDate(1403, 10, 28).isoweekday(), 7)
432
451
 
433
452
  def test_operators(self):
434
453
  self.assertTrue(JalaliDate(1367, 2, 14) == JalaliDate(date(1988, 5, 4)))
@@ -481,6 +500,8 @@ class TestJalaliDate(TestCase):
481
500
  self.assertEqual(JalaliDate(1395, 3, 21) + timedelta(days=-38), JalaliDate(1395, 2, 14))
482
501
  self.assertEqual(JalaliDate(1395, 3, 21) - timedelta(days=38), JalaliDate(1395, 2, 14))
483
502
  self.assertEqual(JalaliDate(1397, 11, 29) + timedelta(days=2), JalaliDate(1397, 12, 1))
503
+ self.assertEqual(JalaliDate(1403, 12, 29) + timedelta(days=2), JalaliDate(1404, 1, 1))
504
+ self.assertEqual(JalaliDate(1403, 12, 29) + timedelta(days=365), JalaliDate(1404, 12, 28))
484
505
 
485
506
  self.assertEqual(JalaliDate(1395, 3, 21) - JalaliDate(1395, 2, 14), timedelta(days=38))
486
507
  self.assertEqual(JalaliDate(1397, 12, 1) - JalaliDate(1397, 11, 29), timedelta(hours=48))
@@ -492,6 +513,7 @@ class TestJalaliDate(TestCase):
492
513
  self.assertEqual(JalaliDate(1400, 1, 1) - JalaliDate(1399, 12, 29), timedelta(days=2))
493
514
  self.assertEqual(JalaliDate(1403, 1, 1) - JalaliDate(1402, 12, 29), timedelta(days=1))
494
515
  self.assertEqual(JalaliDate(1404, 1, 1) - JalaliDate(1403, 12, 29), timedelta(days=2))
516
+ self.assertEqual(JalaliDate(1404, 1, 1) - JalaliDate(1403, 12, 30), timedelta(days=1))
495
517
 
496
518
  def test_pickle(self):
497
519
  file = open("save.p", "wb")
@@ -513,7 +535,7 @@ class TestJalaliDate(TestCase):
513
535
 
514
536
  self.assertEqual(
515
537
  {j1: "today", j2: "majid1", j3: "majid2"},
516
- {JalaliDate.today(): "today", JalaliDate(1367, 2, 14): "majid2"},
538
+ {JalaliDate.today(): "today", JalaliDate(date(1988, 5, 4)): "majid1", JalaliDate(1367, 2, 14): "majid2"},
517
539
  )
518
540
 
519
541
  def test_invalid_dates(self):
@@ -537,7 +559,7 @@ class TestJalaliDate(TestCase):
537
559
  self.assertEqual(str(JalaliDate(1403, 4, 7)), "1403-04-07")
538
560
  self.assertEqual(repr(JalaliDate(1403, 4, 7)), "JalaliDate(1403, 4, 7, Panjshanbeh)")
539
561
 
540
- def test_strptime_raises_not_implemented_error(self):
562
+ def test_strptime(self):
541
563
  with self.assertRaises(NotImplementedError):
542
564
  JalaliDate.strptime("1400-01-01", "%Y-%m-%d")
543
565
 
@@ -546,3 +568,17 @@ class TestJalaliDate(TestCase):
546
568
 
547
569
  with pytest.raises(ValueError, match="locale must be 'en' or 'fa'"):
548
570
  jdate.locale = "de"
571
+
572
+ def test_setstate(self):
573
+ jdate = JalaliDate(1400, 1, 1)
574
+ state = bytes([5, 87, 2, 14])
575
+ jdate.__setstate__(state)
576
+
577
+ self.assertEqual(jdate.year, 1367)
578
+ self.assertEqual(jdate.month, 2)
579
+ self.assertEqual(jdate.day, 14)
580
+
581
+ jdate = JalaliDate(1400, 1, 1)
582
+ state = bytes([5, 112, 1]) # Invalid length
583
+ with pytest.raises(TypeError, match="not enough arguments"):
584
+ jdate.__setstate__(state)
File without changes
File without changes
File without changes
File without changes