persiantools 3.0.1__tar.gz → 4.0.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-3.0.1 → persiantools-4.0.0}/LICENSE +2 -2
  2. {persiantools-3.0.1/persiantools.egg-info → persiantools-4.0.0}/PKG-INFO +15 -16
  3. {persiantools-3.0.1 → persiantools-4.0.0}/README.md +10 -12
  4. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools/__init__.py +2 -2
  5. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools/digits.py +12 -6
  6. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools/jdatetime.py +62 -44
  7. {persiantools-3.0.1 → persiantools-4.0.0/persiantools.egg-info}/PKG-INFO +15 -16
  8. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools.egg-info/SOURCES.txt +7 -1
  9. persiantools-4.0.0/persiantools.egg-info/requires.txt +1 -0
  10. {persiantools-3.0.1 → persiantools-4.0.0}/setup.py +5 -4
  11. persiantools-4.0.0/tests/test_characters.py +35 -0
  12. persiantools-4.0.0/tests/test_digits.py +64 -0
  13. persiantools-4.0.0/tests/test_jalalidate.py +318 -0
  14. persiantools-4.0.0/tests/test_jalalidatetime.py +326 -0
  15. persiantools-4.0.0/tests/test_utils.py +22 -0
  16. {persiantools-3.0.1 → persiantools-4.0.0}/MANIFEST.in +0 -0
  17. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools/characters.py +0 -0
  18. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools/utils.py +0 -0
  19. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools.egg-info/dependency_links.txt +0 -0
  20. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools.egg-info/not-zip-safe +0 -0
  21. {persiantools-3.0.1 → persiantools-4.0.0}/persiantools.egg-info/top_level.txt +0 -0
  22. {persiantools-3.0.1 → persiantools-4.0.0}/pyproject.toml +0 -0
  23. {persiantools-3.0.1 → persiantools-4.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2016-2022 Majid Hajiloo
3
+ Copyright (c) 2016-2024 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.1
2
2
  Name: persiantools
3
- Version: 3.0.1
3
+ Version: 4.0.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
@@ -13,11 +13,11 @@ Classifier: Natural Language :: Persian
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.6
17
- Classifier: Programming Language :: Python :: 3.7
18
16
  Classifier: Programming Language :: Python :: 3.8
19
17
  Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: Implementation :: CPython
22
22
  Classifier: Programming Language :: Python :: Implementation :: PyPy
23
23
  Classifier: Topic :: Software Development
@@ -25,9 +25,10 @@ Classifier: Topic :: Software Development :: Libraries
25
25
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: Software Development :: Localization
27
27
  Classifier: Topic :: Utilities
28
- Requires-Python: >=3.6
28
+ Requires-Python: >=3.8
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
+ Requires-Dist: pytz
31
32
 
32
33
  # PersianTools
33
34
 
@@ -38,21 +39,19 @@ License-File: LICENSE
38
39
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/persiantools.svg)](https://pypi.org/project/persiantools/)
39
40
  [![PyPI - License](https://img.shields.io/pypi/l/persiantools.svg)](https://pypi.org/project/persiantools/)
40
41
 
41
- - Jalali (Shamsi) date and datetime (based on python datetime's module)
42
-
43
- - Convert Jalali to Gregorian date/datetime and vice versa
44
- - Support comparison and arithmetic operators such as `+`, `-`, `==`, `>=`
45
- - Support timezone
46
-
47
- - Convert Arabic and Persian characters/digits to each other
48
- - Convert numbers to words
42
+ Provides Jalali (also known as Shamsi or Persian) dates and datetimes functionalities, among other tools.
43
+ - It converts between Jalali and Gregorian dates and datetimes (based on python datetime's module).
44
+ - It supports operators like `+`, `-`, `==`, and `>=`.
45
+ - It includes timezone support.
46
+ - It converts between Arabic and Persian characters/digits.
47
+ - It turns numbers into Persian words.
49
48
 
50
49
  ## Install Package
51
-
50
+ You can install the package using pip with the following command:
52
51
  ```bash
53
52
  python -m pip install persiantools
54
53
  ```
55
- Persiantools supports Python 3.6+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
54
+ Persiantools supports Python 3.8+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
56
55
 
57
56
  ## How to use
58
57
 
@@ -149,7 +148,7 @@ Based on python `strftime()` behavior
149
148
  ```
150
149
 
151
150
  ### Operators
152
-
151
+ The package supports various operators for date and time manipulations. Here are some examples:
153
152
  ```python
154
153
  >>> from persiantools.jdatetime import JalaliDate, JalaliDateTime
155
154
  >>> import datetime
@@ -157,7 +156,7 @@ Based on python `strftime()` behavior
157
156
  >>> JalaliDate(1367, 2, 14) == JalaliDate(datetime.date(1988, 5, 4))
158
157
  True
159
158
 
160
- >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1369, 7, 1, 1, 0)
159
+ >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1368, 2, 14, 1, 0)
161
160
  False
162
161
 
163
162
  >>> JalaliDate(1367, 2, 14) == datetime.date(1988, 5, 4)
@@ -7,21 +7,19 @@
7
7
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/persiantools.svg)](https://pypi.org/project/persiantools/)
8
8
  [![PyPI - License](https://img.shields.io/pypi/l/persiantools.svg)](https://pypi.org/project/persiantools/)
9
9
 
10
- - Jalali (Shamsi) date and datetime (based on python datetime's module)
11
-
12
- - Convert Jalali to Gregorian date/datetime and vice versa
13
- - Support comparison and arithmetic operators such as `+`, `-`, `==`, `>=`
14
- - Support timezone
15
-
16
- - Convert Arabic and Persian characters/digits to each other
17
- - Convert numbers to words
10
+ Provides Jalali (also known as Shamsi or Persian) dates and datetimes functionalities, among other tools.
11
+ - It converts between Jalali and Gregorian dates and datetimes (based on python datetime's module).
12
+ - It supports operators like `+`, `-`, `==`, and `>=`.
13
+ - It includes timezone support.
14
+ - It converts between Arabic and Persian characters/digits.
15
+ - It turns numbers into Persian words.
18
16
 
19
17
  ## Install Package
20
-
18
+ You can install the package using pip with the following command:
21
19
  ```bash
22
20
  python -m pip install persiantools
23
21
  ```
24
- Persiantools supports Python 3.6+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
22
+ Persiantools supports Python 3.8+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
25
23
 
26
24
  ## How to use
27
25
 
@@ -118,7 +116,7 @@ Based on python `strftime()` behavior
118
116
  ```
119
117
 
120
118
  ### Operators
121
-
119
+ The package supports various operators for date and time manipulations. Here are some examples:
122
120
  ```python
123
121
  >>> from persiantools.jdatetime import JalaliDate, JalaliDateTime
124
122
  >>> import datetime
@@ -126,7 +124,7 @@ Based on python `strftime()` behavior
126
124
  >>> JalaliDate(1367, 2, 14) == JalaliDate(datetime.date(1988, 5, 4))
127
125
  True
128
126
 
129
- >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1369, 7, 1, 1, 0)
127
+ >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1368, 2, 14, 1, 0)
130
128
  False
131
129
 
132
130
  >>> JalaliDate(1367, 2, 14) == datetime.date(1988, 5, 4)
@@ -7,9 +7,9 @@
7
7
 
8
8
  __title__ = "persiantools"
9
9
  __url__ = "https://github.com/majiidd/persiantools"
10
- __version__ = "3.0.1"
10
+ __version__ = "4.0.0"
11
11
  __build__ = __version__
12
12
  __author__ = "Majid Hajiloo"
13
13
  __author_email__ = "majid.hajiloo@gmail.com"
14
14
  __license__ = "MIT"
15
- __copyright__ = "Copyright (c) 2016-2022 Majid Hajiloo"
15
+ __copyright__ = "Copyright (c) 2016-2024 Majid Hajiloo"
@@ -1,4 +1,4 @@
1
- from persiantools import utils
1
+ import re
2
2
 
3
3
  EN_TO_FA_MAP = {
4
4
  "0": "۰",
@@ -48,6 +48,12 @@ FA_TO_AR_MAP = {
48
48
  "۸": "٨",
49
49
  "۹": "٩",
50
50
  }
51
+
52
+ EN_TO_FA_REGEX = re.compile("|".join(EN_TO_FA_MAP.keys()))
53
+ AR_TO_FA_REGEX = re.compile("|".join(AR_TO_FA_MAP.keys()))
54
+ FA_TO_EN_REGEX = re.compile("|".join(FA_TO_EN_MAP.keys()))
55
+ FA_TO_AR_REGEX = re.compile("|".join(FA_TO_AR_MAP.keys()))
56
+
51
57
  ONES = ("یک", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", "نه")
52
58
  TENS = ("بیست", "سی", "چهل", "پنجاه", "شصت", "هفتاد", "هشتاد", "نود")
53
59
  HUNDREDS = ("یکصد", "دویست", "سیصد", "چهارصد", "پانصد", "ششصد", "هفتصد", "هشتصد", "نهصد")
@@ -80,7 +86,7 @@ DECISION = {
80
86
  1000: lambda n, depth: HUNDREDS[n // 100 - 1] + _to_word(n % 100, True),
81
87
  1000000: lambda n, depth: _to_word(n // 1000, depth) + BIG_RANGE[0] + _to_word(n % 1000, True),
82
88
  1000000000: lambda n, depth: _to_word(n // 1000000, depth) + BIG_RANGE[1] + _to_word(n % 1000000, True),
83
- 1000000000000: lambda n, depth: _to_word(n // n, depth) + BIG_RANGE[2] + _to_word(n % 1000000000, True),
89
+ 1000000000000: lambda n, depth: _to_word(n // 1000000000, depth) + BIG_RANGE[2] + _to_word(n % 1000000000, True),
84
90
  1000000000000000: lambda n, depth: _to_word(n // 1000000000000, depth)
85
91
  + BIG_RANGE[3]
86
92
  + _to_word(n % 1000000000000, True),
@@ -101,7 +107,7 @@ def en_to_fa(string: str) -> str:
101
107
  :param string: A string, will be converted
102
108
  :rtype: str
103
109
  """
104
- return utils.replace(string, EN_TO_FA_MAP)
110
+ return EN_TO_FA_REGEX.sub(lambda x: EN_TO_FA_MAP[x.group()], string)
105
111
 
106
112
 
107
113
  def ar_to_fa(string: str) -> str:
@@ -114,7 +120,7 @@ def ar_to_fa(string: str) -> str:
114
120
  :param string: A string, will be converted
115
121
  :rtype: str
116
122
  """
117
- return utils.replace(string, AR_TO_FA_MAP)
123
+ return AR_TO_FA_REGEX.sub(lambda x: AR_TO_FA_MAP[x.group()], string)
118
124
 
119
125
 
120
126
  def fa_to_en(string: str) -> str:
@@ -127,7 +133,7 @@ def fa_to_en(string: str) -> str:
127
133
  :param string: A string, will be converted
128
134
  :rtype: str
129
135
  """
130
- return utils.replace(string, FA_TO_EN_MAP)
136
+ return FA_TO_EN_REGEX.sub(lambda x: FA_TO_EN_MAP[x.group()], string)
131
137
 
132
138
 
133
139
  def fa_to_ar(string: str) -> str:
@@ -140,7 +146,7 @@ def fa_to_ar(string: str) -> str:
140
146
  :param string: A string, will be converted
141
147
  :rtype: str
142
148
  """
143
- return utils.replace(string, FA_TO_AR_MAP)
149
+ return FA_TO_AR_REGEX.sub(lambda x: FA_TO_AR_MAP[x.group()], string)
144
150
 
145
151
 
146
152
  def _to_word(number: int, depth: bool) -> str:
@@ -181,7 +181,7 @@ class JalaliDate:
181
181
  return year, month, day, locale
182
182
 
183
183
  @classmethod
184
- def chack_date(cls, year, month, day):
184
+ def check_date(cls, year, month, day):
185
185
  try:
186
186
  cls._check_date_fields(year, month, day, "en")
187
187
  except (ValueError, TypeError):
@@ -191,11 +191,19 @@ class JalaliDate:
191
191
 
192
192
  @staticmethod
193
193
  def is_leap(year):
194
- assert MINYEAR <= year <= MAXYEAR
194
+ """
195
+ This function calculates whether a given year in the Persian calendar is a leap year.
195
196
 
196
- c = 0.24219858156028368 # 683 / 2820
197
- # return ((year + 2346) * 683) % 2820 < 683
198
- return ((year + 2346) * c) % 1 < c
197
+ It calculates `((year + 2346) * 683) % 2820`. This expression does a few things:
198
+ - It offsets the input year by 2346. This is done to align the Persian calendar with the astronomical solar year.
199
+ - It multiplies the result by 683, which is the number of leap years in a 2820-year cycle.
200
+ - It then takes the result modulo 2820 to get the position of the year within the current 2820-year cycle.
201
+
202
+ If the result of this calculation is less than 683, the year is a leap year in the Persian calendar.
203
+ This is because there are 683 leap years in each 2820-year cycle of the Persian calendar.
204
+ """
205
+ assert MINYEAR <= year <= MAXYEAR
206
+ return ((year + 2346) * 683) % 2820 < 683
199
207
 
200
208
  @classmethod
201
209
  def days_in_month(cls, month, year):
@@ -220,31 +228,40 @@ class JalaliDate:
220
228
  day = year.day
221
229
  year = year.year
222
230
 
223
- g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
231
+ # Days in each month of the Gregorian calendar
232
+ gregorian_days_in_month = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
224
233
 
225
- jy = 0 if year <= 1600 else 979
234
+ # Determine the Jalali year
235
+ jalali_year = 0 if year <= 1600 else 979
226
236
  year -= 621 if year <= 1600 else 1600
227
- year2 = year + 1 if month > 2 else year
228
- days = (365 * year) + int((year2 + 3) / 4) - int((year2 + 99) / 100)
229
- days += int((year2 + 399) / 400) - 80 + day + g_d_m[month - 1]
230
- jy += 33 * int(days / 12053)
237
+
238
+ # Determine if the year is a leap year
239
+ leap_year = year + 1 if month > 2 else year
240
+
241
+ # Calculate the number of days
242
+ days = (365 * year) + (leap_year + 3) // 4 - (leap_year + 99) // 100
243
+ days += (leap_year + 399) // 400 - 80 + day + gregorian_days_in_month[month - 1]
244
+
245
+ # Update the Jalali year
246
+ jalali_year += 33 * (days // 12053)
231
247
  days %= 12053
232
- jy += 4 * int(days / 1461)
248
+ jalali_year += 4 * (days // 1461)
233
249
  days %= 1461
234
- jy += int((days - 1) / 365)
250
+ jalali_year += (days - 1) // 365
235
251
 
236
252
  if days > 365:
237
253
  days = (days - 1) % 365
238
254
 
255
+ # Determine the Jalali month and day
239
256
  if days < 186:
240
- jm = 1 + int(days / 31)
241
- jd = 1 + (days % 31)
257
+ jalali_month = 1 + days // 31
258
+ jalali_day = 1 + (days % 31)
242
259
  else:
243
- arit = days - 186
244
- jm = 7 + int(arit / 30)
245
- jd = 1 + (arit % 30)
260
+ days -= 186
261
+ jalali_month = 7 + days // 30
262
+ jalali_day = 1 + (days % 30)
246
263
 
247
- return cls(jy, jm, jd)
264
+ return cls(jalali_year, jalali_month, jalali_day)
248
265
 
249
266
  def to_gregorian(self):
250
267
  """based on jdf.scr.ir"""
@@ -252,37 +269,40 @@ class JalaliDate:
252
269
  day = self.day
253
270
  year = self.year
254
271
 
255
- gy = 621 if year <= 979 else 1600
272
+ # Determine the Gregorian year
273
+ gregorian_year = 621 if year <= 979 else 1600
256
274
  year -= 0 if year <= 979 else 979
257
275
 
258
- d = (month - 1) * 31 if month < 7 else ((month - 7) * 30) + 186
259
- days = (365 * year) + (int(year / 33) * 8) + int(((year % 33) + 3) / 4)
260
- days += 78 + day + d
276
+ # Calculate the number of days
277
+ days_in_month = (month - 1) * 31 if month < 7 else ((month - 7) * 30) + 186
278
+ days = (365 * year) + (year // 33) * 8 + ((year % 33) + 3) // 4
279
+ days += 78 + day + days_in_month
261
280
 
262
- gy += 400 * int(days / 146097)
281
+ gregorian_year += 400 * (days // 146097)
263
282
  days %= 146097
264
283
 
265
284
  if days > 36524:
266
285
  days -= 1
267
- gy += 100 * int(days / 36524)
286
+ gregorian_year += 100 * (days // 36524)
268
287
  days %= 36524
269
288
 
270
289
  if days >= 365:
271
290
  days += 1
272
291
 
273
- gy += 4 * int(days / 1461)
292
+ gregorian_year += 4 * (days // 1461)
274
293
  days %= 1461
275
- gy += int((days - 1) / 365)
294
+ gregorian_year += (days - 1) // 365
276
295
 
277
296
  if days > 365:
278
297
  days = (days - 1) % 365
279
298
 
280
- gd = days + 1
299
+ gregorian_day = days + 1
281
300
 
282
- g_d_m = [
301
+ # Days in each month of the Gregorian calendar
302
+ gregorian_days_in_month = [
283
303
  0,
284
304
  31,
285
- 29 if (gy % 4 == 0 and gy % 100 != 0) or gy % 400 == 0 else 28,
305
+ 29 if (gregorian_year % 4 == 0 and gregorian_year % 100 != 0) or gregorian_year % 400 == 0 else 28,
286
306
  31,
287
307
  30,
288
308
  31,
@@ -295,13 +315,13 @@ class JalaliDate:
295
315
  31,
296
316
  ]
297
317
 
298
- _gm = 0
299
- for _gm, g in enumerate(g_d_m):
300
- if gd <= g:
318
+ # Determine the Gregorian month
319
+ for _gregorian_month, g in enumerate(gregorian_days_in_month):
320
+ if gregorian_day <= g:
301
321
  break
302
- gd -= g
322
+ gregorian_day -= g
303
323
 
304
- return date(gy, _gm, gd)
324
+ return date(gregorian_year, _gregorian_month, gregorian_day)
305
325
 
306
326
  @classmethod
307
327
  def today(cls):
@@ -958,7 +978,7 @@ class JalaliDateTime(JalaliDate):
958
978
  "%B": cls.__seqToRE(cls, month_names, "B"),
959
979
  "%H": r"(?P<H>2[0-3]|[0-1]\d|\d)",
960
980
  "%I": r"(?P<I>1[0-2]|0[1-9]|[1-9])",
961
- "%p": "(?i)" + cls.__seqToRE(cls, periods, "p"),
981
+ "%p": cls.__seqToRE(cls, periods, "p"),
962
982
  "%M": r"(?P<M>[0-5]\d|\d)",
963
983
  "%S": r"(?P<S>6[0-1]|[0-5]\d|\d)",
964
984
  "%f": r"(?P<f>\d{1,6})",
@@ -977,8 +997,8 @@ class JalaliDateTime(JalaliDate):
977
997
 
978
998
  data_string_regex = utils.replace(fmt, directives_regex_pattern)
979
999
 
980
- if re.match(data_string_regex, data_string):
981
- directives = re.search(data_string_regex, data_string).groupdict()
1000
+ if re.match(data_string_regex, data_string, re.IGNORECASE):
1001
+ directives = re.search(data_string_regex, data_string, re.IGNORECASE).groupdict()
982
1002
 
983
1003
  if "Y" in directives.keys() and len(directives.get("Y")) < 4:
984
1004
  raise ValueError("Year element must contain exactly 4 digits")
@@ -1100,7 +1120,7 @@ class JalaliDateTime(JalaliDate):
1100
1120
  def __base_compare(self, other):
1101
1121
  assert isinstance(other, JalaliDateTime)
1102
1122
 
1103
- y, m, d, h, m, s, ms = [
1123
+ y, mo, d, h, m, s, ms = [
1104
1124
  self._year,
1105
1125
  self._month,
1106
1126
  self._day,
@@ -1109,7 +1129,7 @@ class JalaliDateTime(JalaliDate):
1109
1129
  self._second,
1110
1130
  self._microsecond,
1111
1131
  ]
1112
- y2, m2, d2, h2, m2, s2, ms2 = [
1132
+ y2, mo2, d2, h2, m2, s2, ms2 = [
1113
1133
  other.year,
1114
1134
  other.month,
1115
1135
  other.day,
@@ -1121,10 +1141,8 @@ class JalaliDateTime(JalaliDate):
1121
1141
 
1122
1142
  return (
1123
1143
  0
1124
- if (y, m, d, h, m, s, ms) == (y2, m2, d2, h2, m2, s2, ms2)
1125
- else 1
1126
- if (y, m, d, h, m, s, ms) > (y2, m2, d2, h2, m2, s2, ms2)
1127
- else -1
1144
+ if (y, mo, d, h, m, s, ms) == (y2, mo2, d2, h2, m2, s2, ms2)
1145
+ else 1 if (y, mo, d, h, m, s, ms) > (y2, mo2, d2, h2, m2, s2, ms2) else -1
1128
1146
  )
1129
1147
 
1130
1148
  def _cmp(self, other, allow_mixed=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: persiantools
3
- Version: 3.0.1
3
+ Version: 4.0.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
@@ -13,11 +13,11 @@ Classifier: Natural Language :: Persian
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.6
17
- Classifier: Programming Language :: Python :: 3.7
18
16
  Classifier: Programming Language :: Python :: 3.8
19
17
  Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: Implementation :: CPython
22
22
  Classifier: Programming Language :: Python :: Implementation :: PyPy
23
23
  Classifier: Topic :: Software Development
@@ -25,9 +25,10 @@ Classifier: Topic :: Software Development :: Libraries
25
25
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: Software Development :: Localization
27
27
  Classifier: Topic :: Utilities
28
- Requires-Python: >=3.6
28
+ Requires-Python: >=3.8
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
+ Requires-Dist: pytz
31
32
 
32
33
  # PersianTools
33
34
 
@@ -38,21 +39,19 @@ License-File: LICENSE
38
39
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/persiantools.svg)](https://pypi.org/project/persiantools/)
39
40
  [![PyPI - License](https://img.shields.io/pypi/l/persiantools.svg)](https://pypi.org/project/persiantools/)
40
41
 
41
- - Jalali (Shamsi) date and datetime (based on python datetime's module)
42
-
43
- - Convert Jalali to Gregorian date/datetime and vice versa
44
- - Support comparison and arithmetic operators such as `+`, `-`, `==`, `>=`
45
- - Support timezone
46
-
47
- - Convert Arabic and Persian characters/digits to each other
48
- - Convert numbers to words
42
+ Provides Jalali (also known as Shamsi or Persian) dates and datetimes functionalities, among other tools.
43
+ - It converts between Jalali and Gregorian dates and datetimes (based on python datetime's module).
44
+ - It supports operators like `+`, `-`, `==`, and `>=`.
45
+ - It includes timezone support.
46
+ - It converts between Arabic and Persian characters/digits.
47
+ - It turns numbers into Persian words.
49
48
 
50
49
  ## Install Package
51
-
50
+ You can install the package using pip with the following command:
52
51
  ```bash
53
52
  python -m pip install persiantools
54
53
  ```
55
- Persiantools supports Python 3.6+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
54
+ Persiantools supports Python 3.8+. (_for python 2.7 and 3.5 use [1.5.x](https://github.com/majiidd/persiantools/tree/1.5.x) version_)
56
55
 
57
56
  ## How to use
58
57
 
@@ -149,7 +148,7 @@ Based on python `strftime()` behavior
149
148
  ```
150
149
 
151
150
  ### Operators
152
-
151
+ The package supports various operators for date and time manipulations. Here are some examples:
153
152
  ```python
154
153
  >>> from persiantools.jdatetime import JalaliDate, JalaliDateTime
155
154
  >>> import datetime
@@ -157,7 +156,7 @@ Based on python `strftime()` behavior
157
156
  >>> JalaliDate(1367, 2, 14) == JalaliDate(datetime.date(1988, 5, 4))
158
157
  True
159
158
 
160
- >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1369, 7, 1, 1, 0)
159
+ >>> JalaliDateTime(1367, 2, 14, 4, 30) >= JalaliDateTime(1368, 2, 14, 1, 0)
161
160
  False
162
161
 
163
162
  >>> JalaliDate(1367, 2, 14) == datetime.date(1988, 5, 4)
@@ -12,4 +12,10 @@ persiantools.egg-info/PKG-INFO
12
12
  persiantools.egg-info/SOURCES.txt
13
13
  persiantools.egg-info/dependency_links.txt
14
14
  persiantools.egg-info/not-zip-safe
15
- persiantools.egg-info/top_level.txt
15
+ persiantools.egg-info/requires.txt
16
+ persiantools.egg-info/top_level.txt
17
+ tests/test_characters.py
18
+ tests/test_digits.py
19
+ tests/test_jalalidate.py
20
+ tests/test_jalalidatetime.py
21
+ tests/test_utils.py
@@ -23,11 +23,11 @@ setup(
23
23
  "Operating System :: OS Independent",
24
24
  "Programming Language :: Python",
25
25
  "Programming Language :: Python :: 3",
26
- "Programming Language :: Python :: 3.6",
27
- "Programming Language :: Python :: 3.7",
28
26
  "Programming Language :: Python :: 3.8",
29
27
  "Programming Language :: Python :: 3.9",
30
28
  "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
31
  "Programming Language :: Python :: Implementation :: CPython",
32
32
  "Programming Language :: Python :: Implementation :: PyPy",
33
33
  "Topic :: Software Development",
@@ -42,10 +42,11 @@ setup(
42
42
  author="Majid Hajiloo",
43
43
  author_email="majid.hajiloo@gmail.com",
44
44
  license="MIT",
45
+ license_files=("LICENSE",),
45
46
  packages=["persiantools"],
46
- python_requires=">=3.6",
47
+ python_requires=">=3.8",
47
48
  tests_require=["pytest", "pytest-cov"],
48
- install_requires=[],
49
+ install_requires=["pytz"],
49
50
  include_package_data=True,
50
51
  zip_safe=False,
51
52
  )
@@ -0,0 +1,35 @@
1
+ from unittest import TestCase
2
+
3
+ import pytest
4
+
5
+ from persiantools import characters, digits
6
+
7
+
8
+ class TestDigits(TestCase):
9
+ def test_ar_to_fa(self):
10
+ self.assertEqual(characters.ar_to_fa("السلام عليكم"), "السلام علیکم")
11
+ self.assertEqual(characters.ar_to_fa("HI ي"), "HI ی")
12
+ self.assertEqual(characters.ar_to_fa("دِ بِ زِ ذِ شِ سِ ى ي ك"), "د ب ز ذ ش س ی ی ک")
13
+ self.assertEqual(characters.ar_to_fa("دِ بِ زِ ذِ شِ سِ ى ي ك"), "د ب ز ذ ش س ی ی ک")
14
+ self.assertEqual(
15
+ characters.ar_to_fa("ظ ط ذ د ز ر و ، . ش س ي ب ل ا ت ن م ك ض ص ث ق ف غ ع ه خ ح ؟"),
16
+ "ظ ط ذ د ز ر و ، . ش س ی ب ل ا ت ن م ک ض ص ث ق ف غ ع ه خ ح ؟",
17
+ )
18
+
19
+ with pytest.raises(TypeError):
20
+ characters.ar_to_fa(12345)
21
+
22
+ orig = "السلام عليكم ٠١٢٣٤٥٦٧٨٩"
23
+ converted = characters.ar_to_fa(orig)
24
+ converted = digits.ar_to_fa(converted)
25
+ self.assertEqual(converted, "السلام علیکم ۰۱۲۳۴۵۶۷۸۹")
26
+
27
+ def test_fa_to_fa(self):
28
+ self.assertEqual(characters.ar_to_fa("السلام علیکم"), "السلام علیکم")
29
+ self.assertEqual(characters.ar_to_fa("السلام علیکم"), "السلام علیکم")
30
+
31
+ def test_fa_to_ar(self):
32
+ self.assertEqual(characters.fa_to_ar("کیک"), "كيك")
33
+
34
+ with pytest.raises(TypeError):
35
+ characters.ar_to_fa(12345)
@@ -0,0 +1,64 @@
1
+ from unittest import TestCase
2
+
3
+ import pytest
4
+
5
+ from persiantools import digits
6
+
7
+
8
+ class TestDigits(TestCase):
9
+ def test_en_to_fa(self):
10
+ self.assertEqual(digits.en_to_fa("0987654321"), "۰۹۸۷۶۵۴۳۲۱")
11
+ self.assertEqual(digits.en_to_fa("0987654321"), "۰۹۸۷۶۵۴۳۲۱")
12
+ self.assertEqual(digits.en_to_fa("۰۹۸۷۶۵۴۳۲۱"), "۰۹۸۷۶۵۴۳۲۱")
13
+ self.assertEqual(digits.en_to_fa("+0987654321 abcd"), "+۰۹۸۷۶۵۴۳۲۱ abcd")
14
+
15
+ with pytest.raises(TypeError):
16
+ digits.en_to_fa(12345)
17
+
18
+ def test_ar_to_fa(self):
19
+ self.assertEqual(digits.ar_to_fa("٠٩٨٧٦٥٤٣٢١"), "۰۹۸۷۶۵۴۳۲۱")
20
+ self.assertEqual(digits.ar_to_fa("٠٩٨٧٦٥٤٣٢١"), "۰۹۸۷۶۵۴۳۲۱")
21
+
22
+ orig = "0987٦٥٤٣۲۱"
23
+ converted = digits.en_to_fa(orig)
24
+ converted = digits.ar_to_fa(converted)
25
+
26
+ self.assertEqual(converted, "۰۹۸۷۶۵۴۳۲۱")
27
+
28
+ def test_fa_to_en(self):
29
+ self.assertEqual(digits.fa_to_en("۰۹۸۷۶۵۴۳۲۱"), "0987654321")
30
+
31
+ def test_fa_to_ar(self):
32
+ self.assertEqual(digits.fa_to_ar("۰۹۸۷۶۵۴۳۲۱"), "٠٩٨٧٦٥٤٣٢١")
33
+ self.assertEqual(digits.fa_to_ar(" ۰۹۸۷۶۵۴۳۲۱"), " ٠٩٨٧٦٥٤٣٢١")
34
+
35
+ def test_to_letter(self):
36
+ self.assertEqual(digits.to_word(1), "یک")
37
+ self.assertEqual(digits.to_word(12), "دوازده")
38
+ self.assertEqual(digits.to_word(49), "چهل و نه")
39
+ self.assertEqual(digits.to_word(77), "هفتاد و هفت")
40
+ self.assertEqual(digits.to_word(250), "دویست و پنجاه")
41
+ self.assertEqual(digits.to_word(809), "هشتصد و نه")
42
+ self.assertEqual(digits.to_word(1001), "یک هزار و یک")
43
+ self.assertEqual(digits.to_word(3512), "سه هزار و پانصد و دوازده")
44
+ self.assertEqual(digits.to_word(10000), "ده هزار")
45
+ self.assertEqual(digits.to_word(20010), "بیست هزار و ده")
46
+ self.assertEqual(digits.to_word(10001), "ده هزار و یک")
47
+ self.assertEqual(digits.to_word(500253), "پانصد هزار و دویست و پنجاه و سه")
48
+ self.assertEqual(digits.to_word(6000123), "شش میلیون و یکصد و بیست و سه")
49
+ self.assertEqual(digits.to_word(1000000985), "یک میلیارد و نهصد و هشتاد و پنج")
50
+ self.assertEqual(digits.to_word(100_000_000_000_004), "یکصد تریلیون و چهار")
51
+ self.assertEqual(digits.to_word(9_512_026_000_000), "نه تریلیون و پانصد و دوازده میلیارد و بیست و شش میلیون")
52
+
53
+ self.assertEqual(digits.to_word(-305), "منفی سیصد و پنج")
54
+ self.assertEqual(digits.to_word(10.02), "ده و دو صدم")
55
+ self.assertEqual(digits.to_word(15.007), "پانزده و هفت هزارم")
56
+ self.assertEqual(digits.to_word(12519.85), "دوازده هزار و پانصد و نوزده و هشتاد و پنج صدم")
57
+ self.assertEqual(digits.to_word(123.50), "یکصد و بیست و سه و پنج دهم")
58
+
59
+ self.assertEqual(
60
+ digits.to_word(-0.1554845), "منفی یک میلیون و پانصد و پنجاه و چهار هزار و هشتصد و چهل و پنج ده میلیونیم"
61
+ )
62
+
63
+ with pytest.raises(digits.OutOfRangeException):
64
+ digits.to_word(1000000000000001)