From b28f33ba0761a6517e57d501749e7bae8c607923 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Wed, 2 Jun 2021 04:57:15 +0100 Subject: [PATCH] Add helper function to return timezone from second Use a cache to return the same object for the same offset. --- psycopg3_c/psycopg3_c/types/date.pyx | 42 ++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/psycopg3_c/psycopg3_c/types/date.pyx b/psycopg3_c/psycopg3_c/types/date.pyx index b35f29d4e..0fb30d47a 100644 --- a/psycopg3_c/psycopg3_c/types/date.pyx +++ b/psycopg3_c/psycopg3_c/types/date.pyx @@ -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 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: -- 2.47.3