Implemented Timezone Offset functionality and enabled unit tests
This commit is contained in:
parent
aceaa3594f
commit
f611e90e5c
3 changed files with 110 additions and 37 deletions
|
|
@ -665,12 +665,12 @@ class date:
|
|||
|
||||
"""
|
||||
match = _re.match(
|
||||
r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])", date_string
|
||||
r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$", date_string
|
||||
)
|
||||
if match:
|
||||
y, m, d = int(match.group(1)), int(match.group(2)), int(match.group(3))
|
||||
return cls(y, m, d)
|
||||
raise ValueError("Not a valid ISO Date")
|
||||
raise ValueError("Invalid isoformat string")
|
||||
|
||||
@classmethod
|
||||
def today(cls):
|
||||
|
|
@ -922,6 +922,90 @@ class time:
|
|||
"""
|
||||
return self._tzinfo
|
||||
|
||||
@staticmethod
|
||||
def _parse_iso_string(string_to_parse, segments):
|
||||
results = []
|
||||
|
||||
for regex in segments:
|
||||
match = _re.match(regex, string_to_parse)
|
||||
if match:
|
||||
for grp in range(regex.count("(")):
|
||||
results.append(int(match.group(grp + 1)))
|
||||
string_to_parse = string_to_parse[len(match.group(0)) :]
|
||||
elif string_to_parse: # Only raise an error if we're not done yet
|
||||
raise ValueError("Invalid isoformat string")
|
||||
if string_to_parse:
|
||||
raise ValueError("Invalid isoformat string")
|
||||
return results
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
@classmethod
|
||||
def fromisoformat(cls, time_string):
|
||||
"""Return a time object constructed from an ISO date format.
|
||||
Valid format is ``HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]``
|
||||
|
||||
"""
|
||||
match = _re.match(r"(.*)[\-\+]", time_string)
|
||||
offset_string = None
|
||||
if match:
|
||||
offset_string = time_string[len(match.group(1)) :]
|
||||
time_string = match.group(1)
|
||||
|
||||
time_segments = (
|
||||
r"([0-9][0-9])",
|
||||
r":([0-9][0-9])",
|
||||
r":([0-9][0-9])",
|
||||
r"\.([0-9][0-9][0-9])",
|
||||
r"([0-9][0-9][0-9])",
|
||||
)
|
||||
offset_segments = (
|
||||
r"([\-\+][0-9][0-9]):([0-9][0-9])",
|
||||
r":([0-9][0-9])",
|
||||
r"\.([0-9][0-9][0-9][0-9][0-9][0-9])",
|
||||
)
|
||||
|
||||
results = cls._parse_iso_string(time_string, time_segments)
|
||||
if len(results) < 1:
|
||||
raise ValueError("Invalid isoformat string")
|
||||
if len(results) < len(time_segments):
|
||||
results += [None] * (len(time_segments) - len(results))
|
||||
if offset_string:
|
||||
results += cls._parse_iso_string(offset_string, offset_segments)
|
||||
|
||||
hh = results[0]
|
||||
mm = results[1] if len(results) >= 2 and results[1] is not None else 0
|
||||
ss = results[2] if len(results) >= 3 and results[2] is not None else 0
|
||||
us = 0
|
||||
if len(results) >= 4 and results[3] is not None:
|
||||
us += results[3] * 1000
|
||||
if len(results) >= 5 and results[4] is not None:
|
||||
us += results[4]
|
||||
tz = None
|
||||
if len(results) >= 7:
|
||||
offset_hh = results[5]
|
||||
multiplier = -1 if offset_hh < 0 else 1
|
||||
offset_mm = results[6] * multiplier
|
||||
offset_ss = (results[7] if len(results) >= 8 else 0) * multiplier
|
||||
offset_us = (results[8] if len(results) >= 9 else 0) * multiplier
|
||||
offset = timedelta(
|
||||
hours=offset_hh,
|
||||
minutes=offset_mm,
|
||||
seconds=offset_ss,
|
||||
microseconds=offset_us,
|
||||
)
|
||||
tz = timezone(offset, name="utcoffset")
|
||||
|
||||
result = cls(
|
||||
hh,
|
||||
mm,
|
||||
ss,
|
||||
us,
|
||||
tz,
|
||||
)
|
||||
return result
|
||||
|
||||
# pylint: enable=too-many-locals
|
||||
|
||||
# Instance methods
|
||||
def isoformat(self, timespec="auto"):
|
||||
"""Return a string representing the time in ISO 8601 format, one of:
|
||||
|
|
@ -1222,41 +1306,20 @@ class datetime(date):
|
|||
return cls._fromtimestamp(timestamp, tz is not None, tz)
|
||||
|
||||
@classmethod
|
||||
def fromisoformat(cls, date_string, tz=None):
|
||||
def fromisoformat(cls, date_string):
|
||||
"""Return a datetime object constructed from an ISO date format.
|
||||
Valid format is ``YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]]]``
|
||||
Valid format is ``YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]``
|
||||
|
||||
"""
|
||||
match = _re.match(
|
||||
(
|
||||
r"([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])(T([0-9][0-9]))?(:([0-9][0-9]))?"
|
||||
r"(:([0-9][0-9]))?(\.([0-9][0-9][0-9])([0-9][0-9][0-9])?)?"
|
||||
),
|
||||
date_string,
|
||||
)
|
||||
if match:
|
||||
y, m, d = int(match.group(1)), int(match.group(2)), int(match.group(3))
|
||||
hh = int(match.group(5)) if match.group(5) else 0
|
||||
mm = int(match.group(5)) if match.group(7) else 0
|
||||
ss = int(match.group(9)) if match.group(9) else 0
|
||||
us = 0
|
||||
print(match.group(10))
|
||||
if match.group(11):
|
||||
us += int(match.group(11)) * 1000
|
||||
if match.group(12):
|
||||
us += int(match.group(12))
|
||||
result = cls(
|
||||
y,
|
||||
m,
|
||||
d,
|
||||
hh,
|
||||
mm,
|
||||
ss,
|
||||
us,
|
||||
tz,
|
||||
)
|
||||
return result
|
||||
raise ValueError("Not a valid ISO Date")
|
||||
if "T" in date_string:
|
||||
date_string, time_string = date_string.split("T")
|
||||
dateval = date.fromisoformat(date_string)
|
||||
timeval = time.fromisoformat(time_string)
|
||||
else:
|
||||
dateval = date.fromisoformat(date_string)
|
||||
timeval = time()
|
||||
|
||||
return cls.combine(dateval, timeval)
|
||||
|
||||
@classmethod
|
||||
def now(cls, timezone=None):
|
||||
|
|
|
|||
|
|
@ -88,6 +88,19 @@ class TestDate(unittest.TestCase):
|
|||
self.assertEqual(d.month, month)
|
||||
self.assertEqual(d.day, day)
|
||||
|
||||
def test_fromisoformat(self):
|
||||
# Try an arbitrary fixed value.
|
||||
iso_date_string = "1999-09-19"
|
||||
d = cpy_date.fromisoformat(iso_date_string)
|
||||
self.assertEqual(d.year, 1999)
|
||||
self.assertEqual(d.month, 9)
|
||||
self.assertEqual(d.day, 19)
|
||||
|
||||
def test_fromisoformat_bad_formats(self):
|
||||
# Try an arbitrary fixed value.
|
||||
self.assertRaises(ValueError, cpy_date.fromisoformat, "99-09-19")
|
||||
self.assertRaises(ValueError, cpy_date.fromisoformat, "1999-13-19")
|
||||
|
||||
# TODO: Test this when timedelta is added in
|
||||
@unittest.skip("Skip for CircuitPython - timedelta() not yet implemented.")
|
||||
def test_today(self):
|
||||
|
|
|
|||
|
|
@ -1161,8 +1161,6 @@ class TestDateTime(TestDate):
|
|||
dt_rt = self.theclass.fromisoformat(dtstr)
|
||||
self.assertEqual(dt, dt_rt)
|
||||
|
||||
# TODO
|
||||
@unittest.skip("fromisoformat not implemented")
|
||||
def test_fromisoformat_fails_datetime(self):
|
||||
# Test that fromisoformat() fails on invalid values
|
||||
bad_strs = [
|
||||
|
|
@ -1219,7 +1217,6 @@ class TestDateTime(TestDate):
|
|||
|
||||
self.assertIs(dt.tzinfo, timezone.utc)
|
||||
|
||||
# TODO
|
||||
@unittest.skip("fromisoformat not implemented")
|
||||
def test_fromisoformat_subclass(self):
|
||||
class DateTimeSubclass(self.theclass):
|
||||
|
|
|
|||
Loading…
Reference in a new issue