From: Daniele Varrazzo Date: Sat, 15 May 2021 12:56:15 +0000 (+0200) Subject: Base TimeTz loader on regexp X-Git-Tag: 3.0.dev0~42^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=61301620e80f4adb1ffa6d1261a830c5bc619326;p=thirdparty%2Fpsycopg.git Base TimeTz loader on regexp --- diff --git a/psycopg3/psycopg3/types/date.py b/psycopg3/psycopg3/types/date.py index 44b238a77..8490cd8bc 100644 --- a/psycopg3/psycopg3/types/date.py +++ b/psycopg3/psycopg3/types/date.py @@ -363,45 +363,50 @@ class TimeBinaryLoader(Loader): raise DataError(f"time not supported by Python: hour={h}") -class TimeTzLoader(TimeLoader): +class TimeTzLoader(Loader): format = Format.TEXT - _format = "%H:%M:%S.%f%z" - _format_no_micro = _format.replace(".%f", "") + _py37 = sys.version_info >= (3, 7) - def _load(self, data: Buffer) -> time: - if isinstance(data, memoryview): - data = bytes(data) - - # Hack to convert +HH in +HHMM - if data[-3] in (43, 45): - data += b"00" - - fmt = self._format if b"." in data else self._format_no_micro - try: - dt = datetime.strptime(data.decode("utf8"), fmt) - except ValueError as e: - return self._raise_error(data, e) + _re_format = re.compile( + rb"""(?ix) + ^ + (\d+) : (\d+) : (\d+) (?: \. (\d+) )? # Time and micros + (-|\+) (\d+) (?: : (\d+) )? (?: : (\d+) )? # Timezone + $ + """ + ) - return dt.time().replace(tzinfo=dt.tzinfo) + def load(self, data: Buffer) -> time: + m = self._re_format.match(data) + if not m: + s = bytes(data).decode("utf8", "replace") + raise DataError(f"can't parse timetz {s!r}") - def _load_py36(self, data: Buffer) -> time: - if isinstance(data, memoryview): - data = bytes(data) - # Drop seconds from timezone for Python 3.6 - # Also, Python 3.6 doesn't support HHMM, only HH:MM - if data[-6] in (43, 45): # +-HH:MM -> +-HHMM - data = data[:-3] + data[-2:] - elif data[-9] in (43, 45): # +-HH:MM:SS -> +-HHMM - data = data[:-6] + data[-5:-3] + ho, mi, se, ms, sgn, oh, om, os = m.groups() - return self._load(data) + # Pad the fraction of second to get millis + if ms: + if len(ms) == 6: + ims = int(ms) + else: + ims = int(ms + _ms_trail[len(ms)]) + else: + ims = 0 + # Calculate timezone + off = 60 * 60 * int(oh) + if om: + off += 60 * int(om) + if os and self._py37: + off += int(os) + tz = timezone(timedelta(0, off if sgn == b"+" else -off)) -if sys.version_info >= (3, 7): - setattr(TimeTzLoader, "load", TimeTzLoader._load) -else: - setattr(TimeTzLoader, "load", TimeTzLoader._load_py36) + try: + return time(int(ho), int(mi), int(se), ims, tz) + except ValueError as e: + s = bytes(data).decode("utf8", "replace") + raise DataError(f"can't manage timetz {s!r}: {e}") class TimeTzBinaryLoader(Loader):