]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Add helper function to return timezone from second
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 2 Jun 2021 03:57:15 +0000 (04:57 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Wed, 2 Jun 2021 03:57:51 +0000 (04:57 +0100)
Use a cache to return the same object for the same offset.

psycopg3_c/psycopg3_c/types/date.pyx

index b35f29d4e33a0e97e6c1b6ebb874ca43682fcf6b..0fb30d47ae254da7a1a13347eb1b4dc0747b19db 100644 (file)
@@ -5,15 +5,25 @@ Cython adapters for date/time types.
 # Copyright (C) 2021 The Psycopg Team
 
 from cpython cimport datetime as cdt
+from cpython.dict cimport PyDict_GetItem
 from cpython.object cimport PyObject, PyObject_CallFunctionObjArgs
 from cpython.version cimport PY_VERSION_HEX
 
 cdef extern from "Python.h":
     const char *PyUnicode_AsUTF8AndSize(unicode obj, Py_ssize_t *size) except NULL
+    object PyTimeZone_FromOffset(object offset)
+
+# Hack to compile on Python 3.6
+cdef extern from *:
+    """
+#if PY_VERSION_HEX < 0x03070000
+#define PyTimeZone_FromOffset(x) x
+#endif
+    """
 
 from psycopg3_c._psycopg3 cimport endian
 
-import datetime as dt
+from datetime import date, time, timedelta, datetime, timezone
 
 from psycopg3 import errors as e
 
@@ -30,12 +40,12 @@ DEF INTERVALSTYLE_SQL_STANDARD = 1
 DEF PG_DATE_EPOCH_DAYS = 730120  # date(2000, 1, 1).toordinal()
 DEF PY_DATE_MIN_DAYS = 1  # date.min.toordinal()
 
-cdef object date_toordinal = dt.date.toordinal
-cdef object date_fromordinal = dt.date.fromordinal
-cdef object time_utcoffset = dt.time.utcoffset
-cdef object timedelta_total_seconds = dt.timedelta.total_seconds
-cdef object pg_datetimetz_epoch = dt.datetime(2000, 1, 1, tzinfo=dt.timezone.utc)
-cdef object pg_datetime_epoch = dt.datetime(2000, 1, 1)
+cdef object date_toordinal = date.toordinal
+cdef object date_fromordinal = date.fromordinal
+cdef object time_utcoffset = time.utcoffset
+cdef object timedelta_total_seconds = timedelta.total_seconds
+cdef object pg_datetimetz_epoch = datetime(2000, 1, 1, tzinfo=timezone.utc)
+cdef object pg_datetime_epoch = datetime(2000, 1, 1)
 
 @cython.final
 cdef class DateDumper(CDumper):
@@ -560,8 +570,7 @@ cdef class TimetzLoader(CLoader):
         if sgn == b"-":
             off = -off
 
-        tz = dt.timezone(cdt.timedelta_new(0, off, 0))
-
+        tz = timezone_from_seconds(off)
         try:
             return cdt.time_new(vals[HO], vals[MI], vals[SE], vals[MS], tz)
         except ValueError as ex:
@@ -569,6 +578,21 @@ cdef class TimetzLoader(CLoader):
             raise e.DataError(f"can't parse timetz {s!r}: {ex}") from None
 
 
+cdef object timezone_from_seconds(int sec, __cache={}):
+    cdef object pysec = sec
+    cdef PyObject *ptr = PyDict_GetItem(__cache, pysec)
+    if ptr != NULL:
+        return <object>ptr
+
+    delta = cdt.timedelta_new(0, sec, 0)
+    if PY_VERSION_HEX >= 0x03070000:
+        tz = PyTimeZone_FromOffset(delta)
+    else:
+        tz = timezone(delta)
+    __cache[pysec] = tz
+    return tz
+
+
 cdef const char *_get_datestyle(pq.PGconn pgconn):
     cdef const char *ds
     if pgconn is not None: