]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Fixed timezone parsing on Python 3.6
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 27 Oct 2020 13:43:37 +0000 (14:43 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 28 Oct 2020 03:19:24 +0000 (04:19 +0100)
psycopg3/psycopg3/types/date.py
tests/types/test_date.py

index 9f888e6d876c3d12c403df36c4b34329bfbe7313..9d4f16f247217d2915a4392f1f1d0cbfece7183b 100644 (file)
@@ -5,6 +5,7 @@ Adapters for date/time types.
 # Copyright (C) 2020 The Psycopg Team
 
 import re
+import sys
 import codecs
 from datetime import date, datetime, time, timedelta
 from typing import cast
@@ -186,9 +187,15 @@ class TimeTzLoader(TimeLoader):
     _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
@@ -199,6 +206,16 @@ class TimeTzLoader(TimeLoader):
 
         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):
@@ -254,6 +271,12 @@ 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
@@ -282,8 +305,21 @@ class TimestamptzLoader(TimestampLoader):
 
     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)
 
index 8810de92521b9ffe727ca21424084255030c2821..036e102b8b451f9408acd8d2e06fe634c3da43b4 100644 (file)
@@ -1,4 +1,6 @@
+import sys
 import datetime as dt
+
 import pytest
 
 from psycopg3 import DataError
@@ -194,6 +196,12 @@ def test_load_datetime_overflow(conn, val, datestyle_out):
     ],
 )
 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),))
@@ -523,9 +531,11 @@ def as_tzinfo(s):
     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)