persiantools 4.1.0__tar.gz → 4.1.2__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.
- {persiantools-4.1.0/persiantools.egg-info → persiantools-4.1.2}/PKG-INFO +1 -1
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools/__init__.py +1 -1
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools/jdatetime.py +198 -4
- {persiantools-4.1.0 → persiantools-4.1.2/persiantools.egg-info}/PKG-INFO +1 -1
- {persiantools-4.1.0 → persiantools-4.1.2}/tests/test_jalalidate.py +1 -1
- {persiantools-4.1.0 → persiantools-4.1.2}/tests/test_jalalidatetime.py +94 -3
- {persiantools-4.1.0 → persiantools-4.1.2}/LICENSE +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/MANIFEST.in +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/README.md +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools/characters.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools/digits.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools/utils.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools.egg-info/SOURCES.txt +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools.egg-info/dependency_links.txt +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools.egg-info/not-zip-safe +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools.egg-info/requires.txt +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/persiantools.egg-info/top_level.txt +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/pyproject.toml +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/setup.cfg +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/setup.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/tests/test_characters.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/tests/test_digits.py +0 -0
- {persiantools-4.1.0 → persiantools-4.1.2}/tests/test_utils.py +0 -0
|
@@ -129,6 +129,12 @@ _MONTH_COUNT = [
|
|
|
129
129
|
[29, 30, 336], # esfand
|
|
130
130
|
]
|
|
131
131
|
|
|
132
|
+
_FRACTION_CORRECTION = [100000, 10000, 1000, 100, 10]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _is_ascii_digit(c):
|
|
136
|
+
return c in "0123456789"
|
|
137
|
+
|
|
132
138
|
|
|
133
139
|
class JalaliDate:
|
|
134
140
|
"""
|
|
@@ -558,12 +564,20 @@ class JalaliDate:
|
|
|
558
564
|
if not isinstance(date_string, str):
|
|
559
565
|
raise TypeError("fromisoformat: argument must be str")
|
|
560
566
|
|
|
561
|
-
|
|
567
|
+
if len(date_string) not in (7, 8, 10):
|
|
568
|
+
raise ValueError(f"Invalid isoformat string: {date_string!r}")
|
|
569
|
+
|
|
570
|
+
try:
|
|
571
|
+
return cls(*cls._parse_isoformat_date(digits.fa_to_en(date_string)))
|
|
572
|
+
except Exception:
|
|
573
|
+
raise ValueError(f"Invalid isoformat string: {date_string!r}")
|
|
562
574
|
|
|
563
575
|
@classmethod
|
|
564
576
|
def _parse_isoformat_date(cls, dtstr):
|
|
565
577
|
# It is assumed that this function will only be called with a
|
|
566
578
|
# string of length exactly 10, and (though this is not used) ASCII-only
|
|
579
|
+
assert len(dtstr) in (7, 8, 10)
|
|
580
|
+
|
|
567
581
|
year = int(dtstr[0:4])
|
|
568
582
|
if dtstr[4] != "-":
|
|
569
583
|
raise ValueError("Invalid date separator: %s" % dtstr[4])
|
|
@@ -952,7 +966,7 @@ class JalaliDateTime(JalaliDate):
|
|
|
952
966
|
|
|
953
967
|
@classmethod
|
|
954
968
|
def utcfromtimestamp(cls, t):
|
|
955
|
-
return cls(dt.
|
|
969
|
+
return cls(dt.fromtimestamp(t, tz=timezone.utc))
|
|
956
970
|
|
|
957
971
|
def date(self):
|
|
958
972
|
return JalaliDate(self.year, self.month, self.day).to_gregorian()
|
|
@@ -1025,7 +1039,187 @@ class JalaliDateTime(JalaliDate):
|
|
|
1025
1039
|
|
|
1026
1040
|
@classmethod
|
|
1027
1041
|
def utcnow(cls):
|
|
1028
|
-
return cls(dt.
|
|
1042
|
+
return cls(dt.now(tz=timezone.utc))
|
|
1043
|
+
|
|
1044
|
+
@classmethod
|
|
1045
|
+
def fromisoformat(cls, date_string):
|
|
1046
|
+
"""Construct a datetime from a string in one of the ISO 8601 formats."""
|
|
1047
|
+
if not isinstance(date_string, str):
|
|
1048
|
+
raise TypeError("fromisoformat: argument must be str")
|
|
1049
|
+
|
|
1050
|
+
if len(date_string) < 7:
|
|
1051
|
+
raise ValueError(f"Invalid isoformat string: {date_string!r}")
|
|
1052
|
+
|
|
1053
|
+
# Split this at the separator
|
|
1054
|
+
try:
|
|
1055
|
+
separator_location = cls._find_isoformat_datetime_separator(date_string)
|
|
1056
|
+
dstr = date_string[0:separator_location]
|
|
1057
|
+
tstr = date_string[(separator_location + 1) :]
|
|
1058
|
+
|
|
1059
|
+
date_components = cls._parse_isoformat_date(dstr)
|
|
1060
|
+
except ValueError:
|
|
1061
|
+
raise ValueError(f"Invalid isoformat string: {date_string!r}") from None
|
|
1062
|
+
|
|
1063
|
+
if tstr:
|
|
1064
|
+
try:
|
|
1065
|
+
time_components = cls._parse_isoformat_time(tstr)
|
|
1066
|
+
except ValueError:
|
|
1067
|
+
raise ValueError(f"Invalid isoformat string: {date_string!r}") from None
|
|
1068
|
+
else:
|
|
1069
|
+
time_components = [0, 0, 0, 0, None]
|
|
1070
|
+
|
|
1071
|
+
return cls(*(date_components + time_components))
|
|
1072
|
+
|
|
1073
|
+
@classmethod
|
|
1074
|
+
def _find_isoformat_datetime_separator(cls, dtstr):
|
|
1075
|
+
# See the comment in _datetimemodule.c:_find_isoformat_datetime_separator
|
|
1076
|
+
len_dtstr = len(dtstr)
|
|
1077
|
+
if len_dtstr == 7:
|
|
1078
|
+
return 7
|
|
1079
|
+
|
|
1080
|
+
assert len_dtstr > 7
|
|
1081
|
+
date_separator = "-"
|
|
1082
|
+
week_indicator = "W"
|
|
1083
|
+
|
|
1084
|
+
if dtstr[4] == date_separator:
|
|
1085
|
+
if dtstr[5] == week_indicator:
|
|
1086
|
+
if len_dtstr < 8:
|
|
1087
|
+
raise ValueError("Invalid ISO string")
|
|
1088
|
+
if len_dtstr > 8 and dtstr[8] == date_separator:
|
|
1089
|
+
if len_dtstr == 9:
|
|
1090
|
+
raise ValueError("Invalid ISO string")
|
|
1091
|
+
if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):
|
|
1092
|
+
# This is as far as we need to resolve the ambiguity for
|
|
1093
|
+
# the moment - if we have YYYY-Www-##, the separator is
|
|
1094
|
+
# either a hyphen at 8 or a number at 10.
|
|
1095
|
+
#
|
|
1096
|
+
# We'll assume it's a hyphen at 8 because it's way more
|
|
1097
|
+
# likely that someone will use a hyphen as a separator than
|
|
1098
|
+
# a number, but at this point it's really best effort
|
|
1099
|
+
# because this is an extension of the spec anyway.
|
|
1100
|
+
# TODO(pganssle): Document this
|
|
1101
|
+
return 8
|
|
1102
|
+
return 10
|
|
1103
|
+
else:
|
|
1104
|
+
# YYYY-Www (8)
|
|
1105
|
+
return 8
|
|
1106
|
+
else:
|
|
1107
|
+
# YYYY-MM-DD (10)
|
|
1108
|
+
return 10
|
|
1109
|
+
else:
|
|
1110
|
+
if dtstr[4] == week_indicator:
|
|
1111
|
+
# YYYYWww (7) or YYYYWwwd (8)
|
|
1112
|
+
idx = 7
|
|
1113
|
+
while idx < len_dtstr:
|
|
1114
|
+
if not _is_ascii_digit(dtstr[idx]):
|
|
1115
|
+
break
|
|
1116
|
+
idx += 1
|
|
1117
|
+
|
|
1118
|
+
if idx < 9:
|
|
1119
|
+
return idx
|
|
1120
|
+
|
|
1121
|
+
if idx % 2 == 0:
|
|
1122
|
+
# If the index of the last number is even, it's YYYYWwwd
|
|
1123
|
+
return 7
|
|
1124
|
+
else:
|
|
1125
|
+
return 8
|
|
1126
|
+
else:
|
|
1127
|
+
# YYYYMMDD (8)
|
|
1128
|
+
return 8
|
|
1129
|
+
|
|
1130
|
+
@classmethod
|
|
1131
|
+
def _parse_isoformat_time(cls, tstr):
|
|
1132
|
+
# Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
|
|
1133
|
+
len_str = len(tstr)
|
|
1134
|
+
if len_str < 2:
|
|
1135
|
+
raise ValueError("Isoformat time too short")
|
|
1136
|
+
|
|
1137
|
+
# This is equivalent to re.search('[+-Z]', tstr), but faster
|
|
1138
|
+
tz_pos = tstr.find("-") + 1 or tstr.find("+") + 1 or tstr.find("Z") + 1
|
|
1139
|
+
timestr = tstr[: tz_pos - 1] if tz_pos > 0 else tstr
|
|
1140
|
+
|
|
1141
|
+
time_comps = cls._parse_hh_mm_ss_ff(timestr)
|
|
1142
|
+
|
|
1143
|
+
tzi = None
|
|
1144
|
+
if tz_pos == len_str and tstr[-1] == "Z":
|
|
1145
|
+
tzi = timezone.utc
|
|
1146
|
+
elif tz_pos > 0:
|
|
1147
|
+
tzstr = tstr[tz_pos:]
|
|
1148
|
+
|
|
1149
|
+
# Valid time zone strings are:
|
|
1150
|
+
# HH len: 2
|
|
1151
|
+
# HHMM len: 4
|
|
1152
|
+
# HH:MM len: 5
|
|
1153
|
+
# HHMMSS len: 6
|
|
1154
|
+
# HHMMSS.f+ len: 7+
|
|
1155
|
+
# HH:MM:SS len: 8
|
|
1156
|
+
# HH:MM:SS.f+ len: 10+
|
|
1157
|
+
|
|
1158
|
+
if len(tzstr) in (0, 1, 3):
|
|
1159
|
+
raise ValueError("Malformed time zone string")
|
|
1160
|
+
|
|
1161
|
+
tz_comps = cls._parse_hh_mm_ss_ff(tzstr)
|
|
1162
|
+
|
|
1163
|
+
if all(x == 0 for x in tz_comps):
|
|
1164
|
+
tzi = timezone.utc
|
|
1165
|
+
else:
|
|
1166
|
+
tzsign = -1 if tstr[tz_pos - 1] == "-" else 1
|
|
1167
|
+
|
|
1168
|
+
td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], seconds=tz_comps[2], microseconds=tz_comps[3])
|
|
1169
|
+
|
|
1170
|
+
tzi = timezone(tzsign * td)
|
|
1171
|
+
|
|
1172
|
+
time_comps.append(tzi)
|
|
1173
|
+
|
|
1174
|
+
return time_comps
|
|
1175
|
+
|
|
1176
|
+
@classmethod
|
|
1177
|
+
def _parse_hh_mm_ss_ff(cls, tstr):
|
|
1178
|
+
# Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]
|
|
1179
|
+
len_str = len(tstr)
|
|
1180
|
+
|
|
1181
|
+
time_comps = [0, 0, 0, 0]
|
|
1182
|
+
pos = 0
|
|
1183
|
+
for comp in range(0, 3):
|
|
1184
|
+
if (len_str - pos) < 2:
|
|
1185
|
+
raise ValueError("Incomplete time component")
|
|
1186
|
+
|
|
1187
|
+
time_comps[comp] = int(tstr[pos : pos + 2])
|
|
1188
|
+
|
|
1189
|
+
pos += 2
|
|
1190
|
+
next_char = tstr[pos : pos + 1]
|
|
1191
|
+
|
|
1192
|
+
if comp == 0:
|
|
1193
|
+
has_sep = next_char == ":"
|
|
1194
|
+
|
|
1195
|
+
if not next_char or comp >= 2:
|
|
1196
|
+
break
|
|
1197
|
+
|
|
1198
|
+
if has_sep and next_char != ":":
|
|
1199
|
+
raise ValueError("Invalid time separator: %c" % next_char)
|
|
1200
|
+
|
|
1201
|
+
pos += has_sep
|
|
1202
|
+
|
|
1203
|
+
if pos < len_str:
|
|
1204
|
+
if tstr[pos] not in ".,":
|
|
1205
|
+
raise ValueError("Invalid microsecond component")
|
|
1206
|
+
else:
|
|
1207
|
+
pos += 1
|
|
1208
|
+
|
|
1209
|
+
len_remainder = len_str - pos
|
|
1210
|
+
|
|
1211
|
+
if len_remainder >= 6:
|
|
1212
|
+
to_parse = 6
|
|
1213
|
+
else:
|
|
1214
|
+
to_parse = len_remainder
|
|
1215
|
+
|
|
1216
|
+
time_comps[3] = int(tstr[pos : (pos + to_parse)])
|
|
1217
|
+
if to_parse < 6:
|
|
1218
|
+
time_comps[3] *= _FRACTION_CORRECTION[to_parse - 1]
|
|
1219
|
+
if len_remainder > to_parse and not all(map(_is_ascii_digit, tstr[(pos + to_parse) :])):
|
|
1220
|
+
raise ValueError("Non-digit values in unparsed fraction")
|
|
1221
|
+
|
|
1222
|
+
return time_comps
|
|
1029
1223
|
|
|
1030
1224
|
@staticmethod
|
|
1031
1225
|
def _check_tzinfo_arg(tz):
|
|
@@ -1428,7 +1622,7 @@ class JalaliDateTime(JalaliDate):
|
|
|
1428
1622
|
"%S": "%02d" % self._second,
|
|
1429
1623
|
"%f": "%06d" % self._microsecond,
|
|
1430
1624
|
"%z": datetime.strftime("%z"),
|
|
1431
|
-
"%Z": ("" if not self._tzinfo else self._tzinfo.tzname(
|
|
1625
|
+
"%Z": ("" if not self._tzinfo else self._tzinfo.tzname(datetime)),
|
|
1432
1626
|
"%X": "%02d:%02d:%02d" % (self._hour, self._minute, self._second),
|
|
1433
1627
|
}
|
|
1434
1628
|
|
|
@@ -278,7 +278,7 @@ class TestJalaliDate(TestCase):
|
|
|
278
278
|
with pytest.raises(TypeError):
|
|
279
279
|
JalaliDate.fromisoformat(13670214)
|
|
280
280
|
|
|
281
|
-
with pytest.raises(ValueError
|
|
281
|
+
with pytest.raises(ValueError):
|
|
282
282
|
JalaliDate.fromisoformat("1367/02/14")
|
|
283
283
|
|
|
284
284
|
with pytest.raises(ValueError):
|
|
@@ -9,7 +9,7 @@ from unittest import TestCase
|
|
|
9
9
|
import pytest
|
|
10
10
|
import pytz
|
|
11
11
|
|
|
12
|
-
from persiantools.jdatetime import JalaliDate, JalaliDateTime
|
|
12
|
+
from persiantools.jdatetime import JalaliDate, JalaliDateTime, _is_ascii_digit
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TestJalaliDateTime(TestCase):
|
|
@@ -64,7 +64,7 @@ class TestJalaliDateTime(TestCase):
|
|
|
64
64
|
)
|
|
65
65
|
self.assertEqual(
|
|
66
66
|
JalaliDateTime.utcfromtimestamp(578723400),
|
|
67
|
-
JalaliDateTime(1367, 2, 14, 4, 30, 0, 0),
|
|
67
|
+
JalaliDateTime(1367, 2, 14, 4, 30, 0, 0, tzinfo=timezone.utc),
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
with pytest.raises(TypeError):
|
|
@@ -376,7 +376,7 @@ class TestJalaliDateTime(TestCase):
|
|
|
376
376
|
JalaliDateTime.strptime(date_string, fmt, locale="invalid")
|
|
377
377
|
|
|
378
378
|
def test_utcnow(self):
|
|
379
|
-
now_utc = datetime.
|
|
379
|
+
now_utc = datetime.now(timezone.utc)
|
|
380
380
|
jalali_now = JalaliDateTime.utcnow()
|
|
381
381
|
gregorian_now = jalali_now.to_gregorian()
|
|
382
382
|
|
|
@@ -546,3 +546,94 @@ class TestJalaliDateTime(TestCase):
|
|
|
546
546
|
expected = timedelta(days=1)
|
|
547
547
|
|
|
548
548
|
self.assertEqual(result, expected)
|
|
549
|
+
|
|
550
|
+
def test_to_jalali_with_timezone(self):
|
|
551
|
+
dt = datetime.now(timezone.utc)
|
|
552
|
+
jdate = JalaliDateTime.to_jalali(dt)
|
|
553
|
+
self.assertEqual(jdate.tzinfo, timezone.utc)
|
|
554
|
+
|
|
555
|
+
def test_strftime_basic(self):
|
|
556
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45)
|
|
557
|
+
self.assertEqual(jdate.strftime("%Y-%m-%d %H:%M:%S"), "1400-01-01 15:30:45")
|
|
558
|
+
|
|
559
|
+
def test_strftime_locale_fa(self):
|
|
560
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45, locale="fa")
|
|
561
|
+
self.assertEqual(jdate.strftime("%Y-%m-%d %H:%M:%S", locale="fa"), "۱۴۰۰-۰۱-۰۱ ۱۵:۳۰:۴۵")
|
|
562
|
+
|
|
563
|
+
def test_strftime_with_timezone_utc(self):
|
|
564
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45, tzinfo=timezone.utc)
|
|
565
|
+
self.assertEqual(jdate.strftime("%Y-%m-%d %H:%M:%S %Z"), "1400-01-01 15:30:45 UTC")
|
|
566
|
+
|
|
567
|
+
def test_strftime_with_timezone_offset(self):
|
|
568
|
+
tz = timezone(timedelta(hours=3, minutes=30))
|
|
569
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45, tzinfo=tz)
|
|
570
|
+
self.assertEqual(jdate.strftime("%Y-%m-%d %H:%M:%S %z"), "1400-01-01 15:30:45 +0330")
|
|
571
|
+
|
|
572
|
+
def test_strftime_with_abbreviated_month_day(self):
|
|
573
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45)
|
|
574
|
+
self.assertEqual(jdate.strftime("%b %a"), "Far Yek")
|
|
575
|
+
|
|
576
|
+
def test_strftime_with_full_month_day(self):
|
|
577
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45)
|
|
578
|
+
self.assertEqual(jdate.strftime("%B %A"), "Farvardin Yekshanbeh")
|
|
579
|
+
|
|
580
|
+
def test_strftime_with_custom_format(self):
|
|
581
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45)
|
|
582
|
+
self.assertEqual(jdate.strftime("%d %B %Y - %H:%M"), "01 Farvardin 1400 - 15:30")
|
|
583
|
+
|
|
584
|
+
def test_strftime_with_persian_locale(self):
|
|
585
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45, locale="fa")
|
|
586
|
+
self.assertEqual(jdate.strftime("%A, %d %B %Y - %H:%M", locale="fa"), "یکشنبه, ۰۱ فروردین ۱۴۰۰ - ۱۵:۳۰")
|
|
587
|
+
|
|
588
|
+
def test_strftime_with_periodic_time(self):
|
|
589
|
+
jdate = JalaliDateTime(1400, 1, 1, 15, 30, 45)
|
|
590
|
+
self.assertEqual(jdate.strftime("%I:%M %p"), "03:30 PM")
|
|
591
|
+
|
|
592
|
+
def test_strftime_edge_case_midnight(self):
|
|
593
|
+
jdate = JalaliDateTime(1400, 1, 1, 0, 0, 0)
|
|
594
|
+
self.assertEqual(jdate.strftime("%Y-%m-%d %H:%M:%S"), "1400-01-01 00:00:00")
|
|
595
|
+
|
|
596
|
+
def test_fromisoformat_valid_date_and_time(self):
|
|
597
|
+
jdt = JalaliDateTime.fromisoformat("1403-08-09T02:21:45.123456+04:30")
|
|
598
|
+
self.assertEqual(jdt.year, 1403)
|
|
599
|
+
self.assertEqual(jdt.month, 8)
|
|
600
|
+
self.assertEqual(jdt.day, 9)
|
|
601
|
+
self.assertEqual(jdt.hour, 2)
|
|
602
|
+
self.assertEqual(jdt.minute, 21)
|
|
603
|
+
self.assertEqual(jdt.second, 45)
|
|
604
|
+
self.assertEqual(jdt.microsecond, 123456)
|
|
605
|
+
self.assertEqual(jdt.tzinfo, timezone(timedelta(hours=4, minutes=30)))
|
|
606
|
+
|
|
607
|
+
def test_fromisoformat_with_timezone(self):
|
|
608
|
+
jdt = JalaliDateTime.fromisoformat("1403-08-09T02:21:45+04:30")
|
|
609
|
+
self.assertEqual(jdt.tzinfo, timezone(timedelta(hours=4, minutes=30)))
|
|
610
|
+
|
|
611
|
+
def test_fromisoformat_invalid_string(self):
|
|
612
|
+
with self.assertRaises(ValueError):
|
|
613
|
+
JalaliDateTime.fromisoformat("invalid-date-time")
|
|
614
|
+
|
|
615
|
+
def test_find_isoformat_datetime_separator(self):
|
|
616
|
+
separator = JalaliDateTime._find_isoformat_datetime_separator("1403-08-09T02:21:45")
|
|
617
|
+
self.assertEqual(separator, 10)
|
|
618
|
+
|
|
619
|
+
def test_parse_isoformat_time_with_microseconds(self):
|
|
620
|
+
time_components = JalaliDateTime._parse_isoformat_time("02:21:45.123456")
|
|
621
|
+
self.assertEqual(time_components, [2, 21, 45, 123456, None])
|
|
622
|
+
|
|
623
|
+
def test_parse_isoformat_time_with_timezone(self):
|
|
624
|
+
time_components = JalaliDateTime._parse_isoformat_time("02:21:45+04:30")
|
|
625
|
+
self.assertEqual(time_components, [2, 21, 45, 0, timezone(timedelta(hours=4, minutes=30))])
|
|
626
|
+
|
|
627
|
+
def test_parse_hh_mm_ss_ff_with_microseconds(self):
|
|
628
|
+
time_components = JalaliDateTime._parse_hh_mm_ss_ff("02:21:45.123456")
|
|
629
|
+
self.assertEqual(time_components, [2, 21, 45, 123456])
|
|
630
|
+
|
|
631
|
+
def test_is_ascii_digit(self):
|
|
632
|
+
self.assertTrue(_is_ascii_digit("5"))
|
|
633
|
+
self.assertFalse(_is_ascii_digit("a"))
|
|
634
|
+
|
|
635
|
+
def test_isoformat_round_trip(self):
|
|
636
|
+
original = JalaliDateTime(1403, 8, 9, 2, 21, 45, 123456, tzinfo=timezone.utc)
|
|
637
|
+
iso_format = original.isoformat()
|
|
638
|
+
parsed = JalaliDateTime.fromisoformat(iso_format)
|
|
639
|
+
self.assertEqual(original, parsed)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|