persiantools 5.3.0__tar.gz → 5.4.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 (25) hide show
  1. {persiantools-5.3.0/persiantools.egg-info → persiantools-5.4.0}/PKG-INFO +19 -14
  2. {persiantools-5.3.0 → persiantools-5.4.0}/README.md +14 -12
  3. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools/__init__.py +1 -1
  4. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools/jdatetime.py +41 -9
  5. {persiantools-5.3.0 → persiantools-5.4.0/persiantools.egg-info}/PKG-INFO +19 -14
  6. persiantools-5.4.0/persiantools.egg-info/requires.txt +3 -0
  7. persiantools-5.4.0/pyproject.toml +39 -0
  8. {persiantools-5.3.0 → persiantools-5.4.0}/setup.py +15 -6
  9. {persiantools-5.3.0 → persiantools-5.4.0}/tests/test_jalalidate.py +1 -0
  10. {persiantools-5.3.0 → persiantools-5.4.0}/tests/test_jalalidatetime.py +50 -27
  11. persiantools-5.3.0/persiantools.egg-info/requires.txt +0 -1
  12. persiantools-5.3.0/pyproject.toml +0 -27
  13. {persiantools-5.3.0 → persiantools-5.4.0}/LICENSE +0 -0
  14. {persiantools-5.3.0 → persiantools-5.4.0}/MANIFEST.in +0 -0
  15. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools/characters.py +0 -0
  16. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools/digits.py +0 -0
  17. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools/utils.py +0 -0
  18. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools.egg-info/SOURCES.txt +0 -0
  19. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools.egg-info/dependency_links.txt +0 -0
  20. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools.egg-info/not-zip-safe +0 -0
  21. {persiantools-5.3.0 → persiantools-5.4.0}/persiantools.egg-info/top_level.txt +0 -0
  22. {persiantools-5.3.0 → persiantools-5.4.0}/setup.cfg +0 -0
  23. {persiantools-5.3.0 → persiantools-5.4.0}/tests/test_characters.py +0 -0
  24. {persiantools-5.3.0 → persiantools-5.4.0}/tests/test_digits.py +0 -0
  25. {persiantools-5.3.0 → persiantools-5.4.0}/tests/test_utils.py +0 -0
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: persiantools
3
- Version: 5.3.0
3
+ Version: 5.4.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
  Author-email: majid.hajiloo@gmail.com
8
8
  License: MIT
9
+ Project-URL: Source, https://github.com/majiidd/persiantools
10
+ Project-URL: Issues, https://github.com/majiidd/persiantools/issues
9
11
  Keywords: jalali shamsi persian digits characters converter jalalidate jalalidatetime date datetime jdate jdatetime farsi
10
12
  Classifier: Intended Audience :: Developers
11
13
  Classifier: Natural Language :: Persian
@@ -27,7 +29,7 @@ Classifier: Topic :: Utilities
27
29
  Requires-Python: >=3.9
28
30
  Description-Content-Type: text/markdown
29
31
  License-File: LICENSE
30
- Requires-Dist: pytz
32
+ Requires-Dist: tzdata; platform_system == "Windows"
31
33
  Dynamic: author
32
34
  Dynamic: author-email
33
35
  Dynamic: classifier
@@ -37,6 +39,7 @@ Dynamic: home-page
37
39
  Dynamic: keywords
38
40
  Dynamic: license
39
41
  Dynamic: license-file
42
+ Dynamic: project-url
40
43
  Dynamic: requires-dist
41
44
  Dynamic: requires-python
42
45
  Dynamic: summary
@@ -147,15 +150,15 @@ The `JalaliDateTime` object represents a date and time in the Jalali calendar.
147
150
 
148
151
  ```python
149
152
  >>> from persiantools.jdatetime import JalaliDateTime
150
- >>> import datetime, pytz
153
+ >>> import datetime
151
154
 
152
155
  # Current Jalali datetime
153
156
  >>> JalaliDateTime.now()
154
157
  JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909)
155
158
 
156
- # Current Jalali datetime (timezone-aware)
157
- >>> JalaliDateTime.now(pytz.timezone("Asia/Tehran"))
158
- JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
159
+ >>> from zoneinfo import ZoneInfo
160
+ >>> JalaliDateTime.now(ZoneInfo("Asia/Tehran"))
161
+ JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
159
162
 
160
163
  # Current UTC Jalali datetime
161
164
  >>> JalaliDateTime.utcnow()
@@ -175,20 +178,21 @@ JalaliDateTime(1367, 2, 14, 14, 30, 15)
175
178
  JalaliDateTime(1400, 1, 1, 15, 30, 0, 10)
176
179
 
177
180
  # Timezone conversion
178
- >>> tehran_tz = pytz.timezone("Asia/Tehran")
179
- >>> utc_tz = pytz.utc
181
+ >>> from zoneinfo import ZoneInfo
182
+ >>> tehran_tz = ZoneInfo("Asia/Tehran")
183
+ >>> utc_tz = datetime.timezone.utc
180
184
  >>> dt_utc = JalaliDateTime.now(utc_tz)
181
185
  >>> dt_tehran = dt_utc.astimezone(tehran_tz)
182
186
  >>> dt_utc
183
- JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=<UTC>)
187
+ JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=datetime.timezone.utc)
184
188
  >>> dt_tehran
185
- JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
189
+ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
186
190
  ```
187
191
 
188
192
  #### Attributes and Methods
189
193
 
190
194
  ```python
191
- >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=pytz.utc)
195
+ >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=datetime.timezone.utc)
192
196
 
193
197
  >>> dt_obj.year
194
198
  1367
@@ -205,7 +209,7 @@ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0
205
209
  >>> dt_obj.microsecond
206
210
  123
207
211
  >>> dt_obj.tzinfo
208
- <UTC>
212
+ datetime.timezone.utc
209
213
 
210
214
  # Date part as datetime.date (Gregorian)
211
215
  >>> dt_obj.date()
@@ -226,9 +230,9 @@ Based on python `strftime()` behavior
226
230
 
227
231
  ```python
228
232
  >>> from persiantools.jdatetime import JalaliDateTime
229
- >>> import pytz
233
+ >>> from zoneinfo import ZoneInfo
230
234
 
231
- >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=pytz.timezone("Asia/Tehran"))
235
+ >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=ZoneInfo("Asia/Tehran"))
232
236
 
233
237
  >>> dt.strftime("%Y/%m/%d %H:%M:%S")
234
238
  '1367/02/14 14:30:00'
@@ -345,6 +349,7 @@ JalaliDate(1367, 2, 14, Chaharshanbeh)
345
349
  If you find this project helpful and would like to support its continued development, please consider donating.
346
350
 
347
351
  * **Bitcoin (BTC):** `bc1qg5rp7ymznc98wmhltzvpwl2dvfuvjr33m4hy77`
352
+ * **Ethereum (ETH):** `0xC7D6bf306E456632764D0aD111C8dBBb43a3B9ad`
348
353
  * **Tron (TRX):** `TDd63bVWZDBHmwVNFgJ6T2WdWmk9z7PBLg`
349
354
  * **Stellar (XLM):** `GDSFPPLY34QSAOTOP4DQDXAI2YDRNRIADZHTN3HCGMQXRLIGPYOEH7L5`
350
355
  * **Solana (SOL):** `CXHKgCBqBYy1hbZKGqaSmMzQoTC4Wx2v8QfL9Z7JBo3A`
@@ -104,15 +104,15 @@ The `JalaliDateTime` object represents a date and time in the Jalali calendar.
104
104
 
105
105
  ```python
106
106
  >>> from persiantools.jdatetime import JalaliDateTime
107
- >>> import datetime, pytz
107
+ >>> import datetime
108
108
 
109
109
  # Current Jalali datetime
110
110
  >>> JalaliDateTime.now()
111
111
  JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909)
112
112
 
113
- # Current Jalali datetime (timezone-aware)
114
- >>> JalaliDateTime.now(pytz.timezone("Asia/Tehran"))
115
- JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
113
+ >>> from zoneinfo import ZoneInfo
114
+ >>> JalaliDateTime.now(ZoneInfo("Asia/Tehran"))
115
+ JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
116
116
 
117
117
  # Current UTC Jalali datetime
118
118
  >>> JalaliDateTime.utcnow()
@@ -132,20 +132,21 @@ JalaliDateTime(1367, 2, 14, 14, 30, 15)
132
132
  JalaliDateTime(1400, 1, 1, 15, 30, 0, 10)
133
133
 
134
134
  # Timezone conversion
135
- >>> tehran_tz = pytz.timezone("Asia/Tehran")
136
- >>> utc_tz = pytz.utc
135
+ >>> from zoneinfo import ZoneInfo
136
+ >>> tehran_tz = ZoneInfo("Asia/Tehran")
137
+ >>> utc_tz = datetime.timezone.utc
137
138
  >>> dt_utc = JalaliDateTime.now(utc_tz)
138
139
  >>> dt_tehran = dt_utc.astimezone(tehran_tz)
139
140
  >>> dt_utc
140
- JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=<UTC>)
141
+ JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=datetime.timezone.utc)
141
142
  >>> dt_tehran
142
- JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
143
+ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
143
144
  ```
144
145
 
145
146
  #### Attributes and Methods
146
147
 
147
148
  ```python
148
- >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=pytz.utc)
149
+ >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=datetime.timezone.utc)
149
150
 
150
151
  >>> dt_obj.year
151
152
  1367
@@ -162,7 +163,7 @@ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0
162
163
  >>> dt_obj.microsecond
163
164
  123
164
165
  >>> dt_obj.tzinfo
165
- <UTC>
166
+ datetime.timezone.utc
166
167
 
167
168
  # Date part as datetime.date (Gregorian)
168
169
  >>> dt_obj.date()
@@ -183,9 +184,9 @@ Based on python `strftime()` behavior
183
184
 
184
185
  ```python
185
186
  >>> from persiantools.jdatetime import JalaliDateTime
186
- >>> import pytz
187
+ >>> from zoneinfo import ZoneInfo
187
188
 
188
- >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=pytz.timezone("Asia/Tehran"))
189
+ >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=ZoneInfo("Asia/Tehran"))
189
190
 
190
191
  >>> dt.strftime("%Y/%m/%d %H:%M:%S")
191
192
  '1367/02/14 14:30:00'
@@ -302,6 +303,7 @@ JalaliDate(1367, 2, 14, Chaharshanbeh)
302
303
  If you find this project helpful and would like to support its continued development, please consider donating.
303
304
 
304
305
  * **Bitcoin (BTC):** `bc1qg5rp7ymznc98wmhltzvpwl2dvfuvjr33m4hy77`
306
+ * **Ethereum (ETH):** `0xC7D6bf306E456632764D0aD111C8dBBb43a3B9ad`
305
307
  * **Tron (TRX):** `TDd63bVWZDBHmwVNFgJ6T2WdWmk9z7PBLg`
306
308
  * **Stellar (XLM):** `GDSFPPLY34QSAOTOP4DQDXAI2YDRNRIADZHTN3HCGMQXRLIGPYOEH7L5`
307
309
  * **Solana (SOL):** `CXHKgCBqBYy1hbZKGqaSmMzQoTC4Wx2v8QfL9Z7JBo3A`
@@ -7,7 +7,7 @@
7
7
 
8
8
  __title__ = "persiantools"
9
9
  __url__ = "https://github.com/majiidd/persiantools"
10
- __version__ = "5.3.0"
10
+ __version__ = "5.4.0"
11
11
  __build__ = __version__
12
12
  __author__ = "Majid Hajiloo"
13
13
  __author_email__ = "majid.hajiloo@gmail.com"
@@ -5,8 +5,7 @@ from datetime import datetime as dt
5
5
  from datetime import time as _time
6
6
  from datetime import timedelta, timezone, tzinfo
7
7
  from re import escape as re_escape
8
-
9
- import pytz
8
+ from zoneinfo import ZoneInfo
10
9
 
11
10
  from persiantools import digits, utils
12
11
 
@@ -1567,28 +1566,58 @@ class JalaliDateTime(JalaliDate):
1567
1566
  if self._tzinfo is None:
1568
1567
  return None
1569
1568
 
1570
- offset = self._tzinfo.utcoffset(self.to_gregorian())
1569
+ g = self.to_gregorian()
1570
+ if g.tzinfo is None:
1571
+ g = g.replace(tzinfo=self._tzinfo)
1572
+ try:
1573
+ offset = self._tzinfo.utcoffset(g)
1574
+ except Exception:
1575
+ offset = self._tzinfo.utcoffset(None)
1571
1576
 
1577
+ self.check_utc_offset("utcoffset", offset)
1572
1578
  return offset
1573
1579
 
1574
1580
  def tzname(self):
1575
1581
  if self._tzinfo is None:
1576
1582
  return None
1577
1583
 
1578
- name = self._tzinfo.tzname(self.to_gregorian())
1584
+ g = self.to_gregorian()
1585
+ if g.tzinfo is None:
1586
+ g = g.replace(tzinfo=self._tzinfo)
1587
+ try:
1588
+ name = self._tzinfo.tzname(g)
1589
+ except Exception:
1590
+ name = self._tzinfo.tzname(None)
1579
1591
 
1580
1592
  if name is not None and not isinstance(name, str):
1581
- raise TypeError("tzinfo.tzname() must return None or string, " "not '%s'" % type(name))
1593
+ raise TypeError("tzinfo.tzname() must return None or string, not '%s'" % type(name))
1582
1594
 
1583
1595
  return name
1584
1596
 
1585
1597
  def dst(self):
1598
+ """Return DST offset as timedelta or None.
1599
+
1600
+ For stdlib fixed-offset timezones (datetime.timezone, including UTC) this must be timedelta(0).
1601
+ """
1586
1602
  if self._tzinfo is None:
1587
1603
  return None
1588
1604
 
1589
- offset = self._tzinfo.dst(self.to_gregorian())
1590
- self.check_utc_offset("dst", offset)
1605
+ from datetime import timedelta as _td
1606
+ from datetime import timezone as _tz
1607
+
1608
+ # datetime.timezone instances (including timezone.utc) never have DST
1609
+ if self._tzinfo is _tz.utc or isinstance(self._tzinfo, type(_tz.utc)):
1610
+ return _td(0)
1591
1611
 
1612
+ g = self.to_gregorian()
1613
+ if g.tzinfo is None:
1614
+ g = g.replace(tzinfo=self._tzinfo)
1615
+ try:
1616
+ offset = self._tzinfo.dst(g)
1617
+ except Exception:
1618
+ offset = self._tzinfo.dst(None)
1619
+
1620
+ self.check_utc_offset("dst", offset)
1592
1621
  return offset
1593
1622
 
1594
1623
  @staticmethod
@@ -1738,7 +1767,7 @@ class JalaliDateTime(JalaliDate):
1738
1767
  r"(?:[:]?(?P<zS>[0-5]\d))?"
1739
1768
  r"(?:\.(?P<zf>\d{1,6}))?)"
1740
1769
  ),
1741
- "%Z": cls._seqToRE(pytz.all_timezones, "Z"),
1770
+ "%Z": r"(?P<Z>[A-Za-z_/\-]+)",
1742
1771
  }
1743
1772
 
1744
1773
  fmt = utils.replace(
@@ -1787,7 +1816,10 @@ class JalaliDateTime(JalaliDate):
1787
1816
  )
1788
1817
  tz = timezone(delta)
1789
1818
  elif "Z" in directives.keys():
1790
- tz = pytz.timezone(directives.get("Z"))
1819
+ try:
1820
+ tz = ZoneInfo(directives.get("Z"))
1821
+ except Exception:
1822
+ raise ValueError(f"Unknown time zone name: {directives.get('Z')}")
1791
1823
 
1792
1824
  cls_attrs = {
1793
1825
  "year": directives.get("Y", 1),
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: persiantools
3
- Version: 5.3.0
3
+ Version: 5.4.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
  Author-email: majid.hajiloo@gmail.com
8
8
  License: MIT
9
+ Project-URL: Source, https://github.com/majiidd/persiantools
10
+ Project-URL: Issues, https://github.com/majiidd/persiantools/issues
9
11
  Keywords: jalali shamsi persian digits characters converter jalalidate jalalidatetime date datetime jdate jdatetime farsi
10
12
  Classifier: Intended Audience :: Developers
11
13
  Classifier: Natural Language :: Persian
@@ -27,7 +29,7 @@ Classifier: Topic :: Utilities
27
29
  Requires-Python: >=3.9
28
30
  Description-Content-Type: text/markdown
29
31
  License-File: LICENSE
30
- Requires-Dist: pytz
32
+ Requires-Dist: tzdata; platform_system == "Windows"
31
33
  Dynamic: author
32
34
  Dynamic: author-email
33
35
  Dynamic: classifier
@@ -37,6 +39,7 @@ Dynamic: home-page
37
39
  Dynamic: keywords
38
40
  Dynamic: license
39
41
  Dynamic: license-file
42
+ Dynamic: project-url
40
43
  Dynamic: requires-dist
41
44
  Dynamic: requires-python
42
45
  Dynamic: summary
@@ -147,15 +150,15 @@ The `JalaliDateTime` object represents a date and time in the Jalali calendar.
147
150
 
148
151
  ```python
149
152
  >>> from persiantools.jdatetime import JalaliDateTime
150
- >>> import datetime, pytz
153
+ >>> import datetime
151
154
 
152
155
  # Current Jalali datetime
153
156
  >>> JalaliDateTime.now()
154
157
  JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909)
155
158
 
156
- # Current Jalali datetime (timezone-aware)
157
- >>> JalaliDateTime.now(pytz.timezone("Asia/Tehran"))
158
- JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
159
+ >>> from zoneinfo import ZoneInfo
160
+ >>> JalaliDateTime.now(ZoneInfo("Asia/Tehran"))
161
+ JalaliDateTime(1404, 3, 16, 2, 17, 14, 907909, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
159
162
 
160
163
  # Current UTC Jalali datetime
161
164
  >>> JalaliDateTime.utcnow()
@@ -175,20 +178,21 @@ JalaliDateTime(1367, 2, 14, 14, 30, 15)
175
178
  JalaliDateTime(1400, 1, 1, 15, 30, 0, 10)
176
179
 
177
180
  # Timezone conversion
178
- >>> tehran_tz = pytz.timezone("Asia/Tehran")
179
- >>> utc_tz = pytz.utc
181
+ >>> from zoneinfo import ZoneInfo
182
+ >>> tehran_tz = ZoneInfo("Asia/Tehran")
183
+ >>> utc_tz = datetime.timezone.utc
180
184
  >>> dt_utc = JalaliDateTime.now(utc_tz)
181
185
  >>> dt_tehran = dt_utc.astimezone(tehran_tz)
182
186
  >>> dt_utc
183
- JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=<UTC>)
187
+ JalaliDateTime(1404, 3, 15, 22, 54, 8, 835877, tzinfo=datetime.timezone.utc)
184
188
  >>> dt_tehran
185
- JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)
189
+ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))
186
190
  ```
187
191
 
188
192
  #### Attributes and Methods
189
193
 
190
194
  ```python
191
- >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=pytz.utc)
195
+ >>> dt_obj = JalaliDateTime(1367, 2, 14, 14, 30, 15, 123, tzinfo=datetime.timezone.utc)
192
196
 
193
197
  >>> dt_obj.year
194
198
  1367
@@ -205,7 +209,7 @@ JalaliDateTime(1404, 3, 16, 2, 24, 8, 835877, tzinfo=<DstTzInfo 'Asia/Tehran' +0
205
209
  >>> dt_obj.microsecond
206
210
  123
207
211
  >>> dt_obj.tzinfo
208
- <UTC>
212
+ datetime.timezone.utc
209
213
 
210
214
  # Date part as datetime.date (Gregorian)
211
215
  >>> dt_obj.date()
@@ -226,9 +230,9 @@ Based on python `strftime()` behavior
226
230
 
227
231
  ```python
228
232
  >>> from persiantools.jdatetime import JalaliDateTime
229
- >>> import pytz
233
+ >>> from zoneinfo import ZoneInfo
230
234
 
231
- >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=pytz.timezone("Asia/Tehran"))
235
+ >>> dt = JalaliDateTime(1367, 2, 14, 14, 30, 0, tzinfo=ZoneInfo("Asia/Tehran"))
232
236
 
233
237
  >>> dt.strftime("%Y/%m/%d %H:%M:%S")
234
238
  '1367/02/14 14:30:00'
@@ -345,6 +349,7 @@ JalaliDate(1367, 2, 14, Chaharshanbeh)
345
349
  If you find this project helpful and would like to support its continued development, please consider donating.
346
350
 
347
351
  * **Bitcoin (BTC):** `bc1qg5rp7ymznc98wmhltzvpwl2dvfuvjr33m4hy77`
352
+ * **Ethereum (ETH):** `0xC7D6bf306E456632764D0aD111C8dBBb43a3B9ad`
348
353
  * **Tron (TRX):** `TDd63bVWZDBHmwVNFgJ6T2WdWmk9z7PBLg`
349
354
  * **Stellar (XLM):** `GDSFPPLY34QSAOTOP4DQDXAI2YDRNRIADZHTN3HCGMQXRLIGPYOEH7L5`
350
355
  * **Solana (SOL):** `CXHKgCBqBYy1hbZKGqaSmMzQoTC4Wx2v8QfL9Z7JBo3A`
@@ -0,0 +1,3 @@
1
+
2
+ [:platform_system == "Windows"]
3
+ tzdata
@@ -0,0 +1,39 @@
1
+ [tool.black]
2
+ line-length = 120
3
+ target-version = ["py39", "py310", "py311", "py312", "py313"]
4
+ include = '\.pyi?$'
5
+ exclude = '''
6
+ (
7
+ /(
8
+ \.eggs
9
+ | \.git
10
+ | \.hg
11
+ | \.mypy_cache
12
+ | \.tox
13
+ | \.venv
14
+ | _build
15
+ | buck-out
16
+ | build
17
+ | dist
18
+ )/
19
+ )
20
+ '''
21
+
22
+ [tool.isort]
23
+ profile = "black"
24
+ line_length = 120
25
+
26
+ [tool.pytest.ini_options]
27
+ addopts = "-ra"
28
+ testpaths = ["tests"]
29
+ python_files = ["test_*.py"]
30
+ python_functions = ["test_*"]
31
+
32
+ [tool.mypy]
33
+ python_version = "3.9"
34
+ warn_unused_ignores = true
35
+ warn_redundant_casts = true
36
+ disallow_incomplete_defs = false
37
+ no_implicit_optional = true
38
+ check_untyped_defs = false
39
+ ignore_missing_imports = false
@@ -1,18 +1,24 @@
1
- #!/usr/bin/env python
1
+ import re
2
2
 
3
3
  from setuptools import setup
4
4
 
5
- import persiantools
6
-
7
5
 
8
6
  def readme():
9
7
  with open("README.md", encoding="utf-8") as f:
10
8
  return f.read()
11
9
 
12
10
 
11
+ def read_version():
12
+ with open("persiantools/__init__.py", encoding="utf-8") as f:
13
+ m = re.search(r"^__version__\s*=\s*['\"]([^'\"]+)['\"]", f.read(), re.M)
14
+ if not m:
15
+ raise RuntimeError("Cannot find __version__ in persiantools/__init__.py")
16
+ return m.group(1)
17
+
18
+
13
19
  setup(
14
20
  name="persiantools",
15
- version=persiantools.__version__,
21
+ version=read_version(),
16
22
  description="Jalali date and datetime with other tools",
17
23
  long_description=readme(),
18
24
  long_description_content_type="text/markdown",
@@ -38,14 +44,17 @@ setup(
38
44
  keywords="jalali shamsi persian digits characters converter jalalidate "
39
45
  "jalalidatetime date datetime jdate jdatetime farsi",
40
46
  url="https://github.com/majiidd/persiantools",
47
+ project_urls={
48
+ "Source": "https://github.com/majiidd/persiantools",
49
+ "Issues": "https://github.com/majiidd/persiantools/issues",
50
+ },
41
51
  author="Majid Hajiloo",
42
52
  author_email="majid.hajiloo@gmail.com",
43
53
  license="MIT",
44
54
  license_files=("LICENSE",),
45
55
  packages=["persiantools"],
46
56
  python_requires=">=3.9",
47
- tests_require=["pytest", "pytest-cov"],
48
- install_requires=["pytz"],
57
+ install_requires=["tzdata; platform_system == 'Windows'"],
49
58
  include_package_data=True,
50
59
  zip_safe=False,
51
60
  )
@@ -33,6 +33,7 @@ class TestJalaliDate(TestCase):
33
33
  (JalaliDate(1403, 8, 18), date(2024, 11, 8)),
34
34
  (JalaliDate(1404, 3, 16), date(2025, 6, 6)),
35
35
  (JalaliDate(1403, 10, 27), date(2025, 1, 16)),
36
+ (JalaliDate(1404, 7, 4), date(2025, 9, 26)),
36
37
  (JalaliDate(1210, 12, 29), date(1832, 3, 19)),
37
38
  (JalaliDate(1367, 12, 29), date(1989, 3, 20)),
38
39
  (JalaliDate(1392, 12, 29), date(2014, 3, 20)),
@@ -5,9 +5,9 @@ from datetime import date, datetime
5
5
  from datetime import time as _time
6
6
  from datetime import timedelta, timezone
7
7
  from unittest import TestCase
8
+ from zoneinfo import ZoneInfo
8
9
 
9
10
  import pytest
10
- import pytz
11
11
 
12
12
  from persiantools.jdatetime import JalaliDate, JalaliDateTime, _is_ascii_digit
13
13
 
@@ -46,7 +46,7 @@ class TestJalaliDateTime(TestCase):
46
46
  g = JalaliDateTime.now()
47
47
  self.assertEqual(g.time(), _time(g.hour, g.minute, g.second, g.microsecond))
48
48
 
49
- g = g.replace(tzinfo=pytz.timezone("America/Los_Angeles"))
49
+ g = g.replace(tzinfo=ZoneInfo("America/Los_Angeles"))
50
50
  self.assertEqual(
51
51
  g.timetz(),
52
52
  _time(
@@ -54,13 +54,13 @@ class TestJalaliDateTime(TestCase):
54
54
  g.minute,
55
55
  g.second,
56
56
  g.microsecond,
57
- pytz.timezone("America/Los_Angeles"),
57
+ ZoneInfo("America/Los_Angeles"),
58
58
  ),
59
59
  )
60
60
 
61
61
  self.assertEqual(
62
- JalaliDateTime.fromtimestamp(578723400, pytz.utc),
63
- JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, pytz.utc),
62
+ JalaliDateTime.fromtimestamp(578723400, timezone.utc),
63
+ JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, timezone.utc),
64
64
  )
65
65
  self.assertEqual(
66
66
  JalaliDateTime.utcfromtimestamp(578723400),
@@ -111,27 +111,27 @@ class TestJalaliDateTime(TestCase):
111
111
 
112
112
  def test_others(self):
113
113
  self.assertTrue(JalaliDateTime.fromtimestamp(time.time() - 10) <= JalaliDateTime.now())
114
- self.assertEqual(JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, pytz.utc).timestamp(), 578723400)
114
+ self.assertEqual(JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, timezone.utc).timestamp(), 578723400)
115
115
  self.assertEqual(
116
- JalaliDateTime.fromtimestamp(578723400, pytz.utc),
117
- JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, pytz.utc),
116
+ JalaliDateTime.fromtimestamp(578723400, timezone.utc),
117
+ JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, timezone.utc),
118
118
  )
119
119
  self.assertEqual(JalaliDateTime(1367, 2, 14, 4, 30, 4, 4444).jdate(), JalaliDate(1367, 2, 14))
120
120
  self.assertEqual(JalaliDateTime(1367, 2, 14, 4, 30, 4, 4444).date(), date(1988, 5, 4))
121
121
  self.assertEqual(
122
- JalaliDateTime(1367, 2, 14, 4, 30, 0, 0).replace(tzinfo=pytz.utc).__repr__(),
123
- "JalaliDateTime(1367, 2, 14, 4, 30, tzinfo=<UTC>)",
122
+ JalaliDateTime(1367, 2, 14, 4, 30, 0, 0).replace(tzinfo=timezone.utc).__repr__(),
123
+ "JalaliDateTime(1367, 2, 14, 4, 30, tzinfo=datetime.timezone.utc)",
124
124
  )
125
125
  self.assertEqual(
126
126
  JalaliDateTime(1367, 2, 14, 4, 30, 4, 4444).replace(year=1395, day=3, minute=59),
127
127
  JalaliDateTime(1395, 2, 3, 4, 59, 4, 4444),
128
128
  )
129
129
 
130
- self.assertEqual(JalaliDateTime.now(pytz.utc).tzname(), "UTC")
130
+ self.assertEqual(JalaliDateTime.now(timezone.utc).tzname(), "UTC")
131
131
  self.assertIsNone(JalaliDateTime.today().tzname())
132
132
  self.assertIsNone(JalaliDateTime.today().dst())
133
133
 
134
- dt = JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, pytz.utc)
134
+ dt = JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, timezone.utc)
135
135
  self.assertEqual(dt.dst(), timedelta(0))
136
136
 
137
137
  self.assertEqual(
@@ -152,14 +152,37 @@ class TestJalaliDateTime(TestCase):
152
152
  )
153
153
 
154
154
  self.assertEqual(
155
- JalaliDateTime(1367, 2, 14, 4, 30, 0, 1, pytz.utc).isoformat(),
155
+ JalaliDateTime(1367, 2, 14, 4, 30, 0, 1, timezone.utc).isoformat(),
156
156
  "1367-02-14T04:30:00.000001+00:00",
157
157
  )
158
158
  self.assertEqual(
159
- JalaliDateTime(1367, 2, 14, 4, 30, 0, 1, pytz.utc).__str__(),
159
+ JalaliDateTime(1367, 2, 14, 4, 30, 0, 1, timezone.utc).__str__(),
160
160
  "1367-02-14 04:30:00.000001+00:00",
161
161
  )
162
162
 
163
+ def test_dst_fixed_offset_zero(self):
164
+ jdt = JalaliDateTime(1400, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=3)))
165
+ assert jdt.dst() == timedelta(0)
166
+
167
+ def test_tzname_fixed_offset(self):
168
+ jdt = JalaliDateTime(1400, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=-4, minutes=-45)))
169
+ # stdlib datetime.timezone tzname format is "UTC±HH:MM"
170
+ assert jdt.tzname() == "UTC-04:45"
171
+
172
+ def test_strptime_Z_with_zoneinfo(self):
173
+ # 1402-01-01 (2023-03-21) after Iran removed DST; Asia/Tehran is fixed +03:30
174
+ s = "1402-01-01 12:00:00 Asia/Tehran"
175
+ fmt = "%Y-%m-%d %H:%M:%S %Z"
176
+ jdt = JalaliDateTime.strptime(s, fmt)
177
+ assert isinstance(jdt.tzinfo, ZoneInfo)
178
+ assert jdt.utcoffset() == timedelta(hours=3, minutes=30)
179
+
180
+ def test_strptime_Z_invalid_name(self):
181
+ s = "1400-01-01 12:00:00 Mars/Phobos"
182
+ fmt = "%Y-%m-%d %H:%M:%S %Z"
183
+ with pytest.raises(ValueError):
184
+ JalaliDateTime.strptime(s, fmt)
185
+
163
186
  def test_operators(self):
164
187
  self.assertEqual(
165
188
  JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) + timedelta(days=30, seconds=15, milliseconds=1),
@@ -201,13 +224,13 @@ class TestJalaliDateTime(TestCase):
201
224
  self.assertTrue(JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) >= datetime(1988, 5, 4, 0, 0, 0, 0))
202
225
  self.assertTrue(JalaliDateTime(1395, 4, 15, 20, 13, 59, 0) == JalaliDateTime(1395, 4, 15, 20, 13, 59, 0))
203
226
  self.assertTrue(
204
- JalaliDateTime(1395, 4, 15, 20, 13, 59, 0, pytz.utc) != JalaliDateTime(1395, 4, 15, 20, 13, 59, 0)
227
+ JalaliDateTime(1395, 4, 15, 20, 13, 59, 0, timezone.utc) != JalaliDateTime(1395, 4, 15, 20, 13, 59, 0)
205
228
  )
206
229
 
207
230
  self.assertEqual(
208
- JalaliDateTime(1395, 4, 16, 20, 14, 30, 0, pytz.utc)
209
- - JalaliDateTime(1395, 4, 16, 20, 14, 30, 0, pytz.timezone("Asia/Tehran")),
210
- pytz.timezone("Asia/Tehran")._utcoffset,
231
+ JalaliDateTime(1395, 4, 16, 20, 14, 30, 0, timezone.utc)
232
+ - JalaliDateTime(1395, 4, 16, 20, 14, 30, 0, ZoneInfo("Asia/Tehran")),
233
+ timedelta(hours=4, minutes=30),
211
234
  )
212
235
 
213
236
  self.assertFalse(JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) == {"year": 1367})
@@ -233,7 +256,7 @@ class TestJalaliDateTime(TestCase):
233
256
  assert JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) > date(1988, 4, 5)
234
257
 
235
258
  with pytest.raises(NotImplementedError):
236
- assert JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) >= pytz.utc
259
+ assert JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) >= timezone.utc
237
260
 
238
261
  with pytest.raises(TypeError):
239
262
  assert JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) >= JalaliDate(1392, 4, 5)
@@ -245,7 +268,7 @@ class TestJalaliDateTime(TestCase):
245
268
  assert JalaliDateTime(1367, 2, 14, 4, 30, 0, 0) - []
246
269
 
247
270
  def test_hash(self):
248
- j1 = JalaliDateTime.today().replace(tzinfo=pytz.utc)
271
+ j1 = JalaliDateTime.today().replace(tzinfo=timezone.utc)
249
272
  j2 = JalaliDateTime(1369, 7, 1, 0, 0, 0, 0)
250
273
  j3 = JalaliDateTime(datetime(1990, 9, 23, 0, 0, 0, 0))
251
274
 
@@ -268,7 +291,7 @@ class TestJalaliDateTime(TestCase):
268
291
 
269
292
  def test_pickle(self):
270
293
  file = open("save.p", "wb")
271
- now = JalaliDateTime.now().replace(tzinfo=pytz.timezone("Asia/Tehran"))
294
+ now = JalaliDateTime.now().replace(tzinfo=ZoneInfo("Asia/Tehran"))
272
295
  pickle.dump(now, file)
273
296
  file.close()
274
297
 
@@ -282,27 +305,27 @@ class TestJalaliDateTime(TestCase):
282
305
 
283
306
  def test_format(self):
284
307
  self.assertEqual(
285
- JalaliDateTime(1369, 7, 1, 14, 0, 10, 0, pytz.utc).strftime("%X %p %z %Z"),
308
+ JalaliDateTime(1369, 7, 1, 14, 0, 10, 0, timezone.utc).strftime("%X %p %z %Z"),
286
309
  "14:00:10 PM +0000 UTC",
287
310
  )
288
311
 
289
312
  self.assertEqual(
290
- JalaliDateTime(1367, 2, 14, 14, 0, 10, 0, pytz.utc).strftime("%c"),
313
+ JalaliDateTime(1367, 2, 14, 14, 0, 10, 0, timezone.utc).strftime("%c"),
291
314
  "Chaharshanbeh 14 Ordibehesht 1367 14:00:10",
292
315
  )
293
316
 
294
317
  self.assertEqual(
295
- JalaliDateTime(1397, 11, 30, 14, 0, 10, 0, pytz.utc).strftime("%c"),
318
+ JalaliDateTime(1397, 11, 30, 14, 0, 10, 0, timezone.utc).strftime("%c"),
296
319
  "Seshanbeh 30 Bahman 1397 14:00:10",
297
320
  )
298
321
 
299
322
  self.assertEqual(
300
- JalaliDateTime(1367, 2, 14, 11, 0, 10, 553, pytz.utc).strftime("%I:%M:%S.%f %p"),
323
+ JalaliDateTime(1367, 2, 14, 11, 0, 10, 553, timezone.utc).strftime("%I:%M:%S.%f %p"),
301
324
  "11:00:10.000553 AM",
302
325
  )
303
326
 
304
327
  self.assertEqual(
305
- JalaliDateTime(1367, 2, 14, 14, 0, 10, 553, pytz.utc).strftime("%I:%M:%S.%f %p"),
328
+ JalaliDateTime(1367, 2, 14, 14, 0, 10, 553, timezone.utc).strftime("%I:%M:%S.%f %p"),
306
329
  "02:00:10.000553 PM",
307
330
  )
308
331
 
@@ -346,7 +369,7 @@ class TestJalaliDateTime(TestCase):
346
369
  JalaliDateTime.strptime("1400-01-01 02:00:10.000553 PM", "%Y-%m-%d %I:%M:%S.%f %p"),
347
370
  )
348
371
 
349
- jdt = JalaliDateTime(1374, 4, 8, 16, 28, 3, 227, pytz.utc)
372
+ jdt = JalaliDateTime(1374, 4, 8, 16, 28, 3, 227, timezone.utc)
350
373
  self.assertEqual(jdt, JalaliDateTime.strptime(jdt.strftime("%c %f %z %Z"), "%c %f %z %Z"))
351
374
 
352
375
  jdt_utc_combined = JalaliDateTime(1374, 4, 8, 16, 28, 3, 227, timezone.utc)
@@ -1,27 +0,0 @@
1
- [tool.black]
2
- line-length = 120
3
- include = '\.pyi?$'
4
- exclude = '''
5
- (
6
- /(
7
- \.eggs
8
- | \.git
9
- | \.hg
10
- | \.mypy_cache
11
- | \.tox
12
- | \.venv
13
- | _build
14
- | buck-out
15
- | build
16
- | dist
17
- )/
18
- )
19
- '''
20
-
21
- [tool.isort]
22
- multi_line_output = 3
23
- include_trailing_comma = true
24
- force_grid_wrap = 0
25
- use_parentheses = true
26
- ensure_newline_before_comments = true
27
- line_length = 120
File without changes
File without changes
File without changes