# Copyright (C) 2020 The Psycopg Team
import re
+import sys
import codecs
from datetime import date, datetime, time, timedelta
from typing import cast
_format = "%H:%M:%S.%f%z"
_format_no_micro = _format.replace(".%f", "")
+ def __init__(self, oid: int, context: AdaptContext):
+ if sys.version_info < (3, 7):
+ self.load = self._load_py36
+
+ super().__init__(oid, context)
+
def load(self, data: bytes) -> time:
# Hack to convert +HH in +HHMM
- if data[-3:-2] in (b"-", b"+"):
+ if data[-3] in (43, 45):
data += b"00"
fmt = self._format if b"." in data else self._format_no_micro
return dt.time().replace(tzinfo=dt.tzinfo)
+ def _load_py36(self, data: bytes) -> time:
+ # 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]
+
+ return TimeTzLoader.load(self, data)
+
@Loader.text(builtins["timestamp"].oid)
class TimestampLoader(DateLoader):
@Loader.text(builtins["timestamptz"].oid)
class TimestamptzLoader(TimestampLoader):
+ def __init__(self, oid: int, context: AdaptContext):
+ if sys.version_info < (3, 7):
+ self.load = self._load_py36
+
+ super().__init__(oid, context)
+
def _format_from_context(self) -> str:
ds = self._get_datestyle()
if ds.startswith(b"I"): # ISO
def load(self, data: bytes) -> datetime:
# Hack to convert +HH in +HHMM
- if data[-3:-2] in (b"-", b"+"):
+ if data[-3] in (43, 45):
+ data += b"00"
+
+ return super().load(data)
+
+ def _load_py36(self, data: bytes) -> datetime:
+ # Drop seconds from timezone for Python 3.6
+ # Also, Python 3.6 doesn't support HHMM, only HH:MM
+ tzsep = (43, 45) # + and - bytes
+ if data[-3] in tzsep: # +HH, -HH
data += b"00"
+ elif data[-6] in tzsep:
+ data = data[:-3] + data[-2:]
+ elif data[-9] in tzsep:
+ data = data[:-6] + data[-5:-3]
return super().load(data)
+import sys
import datetime as dt
+
import pytest
from psycopg3 import DataError
],
)
def test_dump_datetimetz(conn, val, expr):
+ # adjust for Python 3.6 missing seconds in tzinfo
+ if val.count(":") > 1:
+ expr = expr.rsplit(":", 1)[0]
+ val, rest = val.rsplit(":", 1)
+ val += rest[3:] # skip tz seconds, but include micros
+
cur = conn.cursor()
cur.execute("set timezone to '-02:00'")
cur.execute(f"select '{expr}'::timestamptz = %s", (as_dt(val),))
else:
mul = 1
- tzoff = mul * dt.timedelta(
- **dict(zip(("hours", "minutes", "seconds"), map(int, s.split(":"))))
- )
+ if sys.version_info < (3, 7):
+ fields = ("hours", "minutes")
+ else:
+ fields = ("hours", "minutes", "seconds")
+ tzoff = mul * dt.timedelta(**dict(zip(fields, map(int, s.split(":")))))
return dt.timezone(tzoff)