summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2021-06-14 23:46:39 +0100
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2021-06-15 00:17:14 +0100
commitf28502663f1c318dd01c975cf70e3dcce544ab3b (patch)
tree1bf52dee88b416407560a43783758d58e76d23e0
parent2eac70786e7034888e43a316f05a39e6063253b6 (diff)
downloadpsycopg2-f28502663f1c318dd01c975cf70e3dcce544ab3b.tar.gz
Use datetime.timezone as default tzinfo_factory.
-rw-r--r--NEWS2
-rw-r--r--doc/src/cursor.rst6
-rw-r--r--doc/src/usage.rst16
-rw-r--r--psycopg/cursor_type.c7
-rw-r--r--psycopg/typecast_datetime.c13
-rwxr-xr-xtests/test_dates.py18
6 files changed, 47 insertions, 15 deletions
diff --git a/NEWS b/NEWS
index 51238bf..44289d5 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,8 @@ Other changes:
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
- Dropped support for mx.DateTime.
+- Use `datetime.timezone` objects by default in datetime objects instead of
+ `~psycopg2.tz.FixedOffsetTimezone`.
- Build system for Linux/MacOS binary packages moved to GitHub action, now
providing :pep:`600`\-style wheels packages.
diff --git a/doc/src/cursor.rst b/doc/src/cursor.rst
index 8398c98..2123b17 100644
--- a/doc/src/cursor.rst
+++ b/doc/src/cursor.rst
@@ -498,8 +498,10 @@ The ``cursor`` class
The time zone factory used to handle data types such as
:sql:`TIMESTAMP WITH TIME ZONE`. It should be a `~datetime.tzinfo`
- object. A few implementations are available in the `psycopg2.tz`
- module.
+ object. Default is `datetime.timezone`.
+
+ .. versionchanged:: 2.9
+ previosly the default factory was `psycopg2.tz.FixedOffsetTimezone`.
.. method:: nextset()
diff --git a/doc/src/usage.rst b/doc/src/usage.rst
index b4ffc85..5bb69e9 100644
--- a/doc/src/usage.rst
+++ b/doc/src/usage.rst
@@ -574,14 +574,13 @@ Time zones handling
'''''''''''''''''''
The PostgreSQL type :sql:`timestamp with time zone` (a.k.a.
-:sql:`timestamptz`) is converted into Python `~datetime.datetime` objects with
-a `~datetime.datetime.tzinfo` attribute set to a
-`~psycopg2.tz.FixedOffsetTimezone` instance.
+:sql:`timestamptz`) is converted into Python `~datetime.datetime` objects.
>>> cur.execute("SET TIME ZONE 'Europe/Rome'") # UTC + 1 hour
>>> cur.execute("SELECT '2010-01-01 10:30:45'::timestamptz")
- >>> cur.fetchone()[0].tzinfo
- psycopg2.tz.FixedOffsetTimezone(offset=60, name=None)
+ >>> cur.fetchone()[0]
+ datetime.datetime(2010, 1, 1, 10, 30, 45,
+ tzinfo=datetime.timezone(datetime.timedelta(seconds=3600)))
.. note::
@@ -594,9 +593,9 @@ a `~datetime.datetime.tzinfo` attribute set to a
>>> cur.execute("SELECT '1900-01-01 10:30:45'::timestamptz")
>>> cur.fetchone()[0].tzinfo
# On Python 3.6: 5h, 21m
- psycopg2.tz.FixedOffsetTimezone(offset=datetime.timedelta(0, 19260), name=None)
+ datetime.timezone(datetime.timedelta(0, 19260))
# On Python 3.7 and following: 5h, 21m, 10s
- psycopg2.tz.FixedOffsetTimezone(offset=datetime.timedelta(seconds=19270), name=None)
+ datetime.timezone(datetime.timedelta(seconds=19270))
.. versionchanged:: 2.2.2
timezones with seconds are supported (with rounding). Previously such
@@ -605,6 +604,9 @@ a `~datetime.datetime.tzinfo` attribute set to a
.. versionchanged:: 2.9
timezones with seconds are supported without rounding.
+.. versionchanged:: 2.9
+ use `datetime.timezone` as default tzinfo object instead of
+ `~psycopg2.tz.FixedOffsetTimezone`.
.. index::
double: Date objects; Infinite
diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index 31ce792..abab40c 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -1951,10 +1951,11 @@ cursor_setup(cursorObject *self, connectionObject *conn, const char *name)
/* default tzinfo factory */
{
+ /* The datetime api doesn't seem to have a constructor to make a
+ * datetime.timezone, so use the Python interface. */
PyObject *m = NULL;
- if ((m = PyImport_ImportModule("psycopg2.tz"))) {
- self->tzinfo_factory = PyObject_GetAttrString(
- m, "FixedOffsetTimezone");
+ if ((m = PyImport_ImportModule("datetime"))) {
+ self->tzinfo_factory = PyObject_GetAttrString(m, "timezone");
Py_DECREF(m);
}
if (!self->tzinfo_factory) {
diff --git a/psycopg/typecast_datetime.c b/psycopg/typecast_datetime.c
index d700069..121a228 100644
--- a/psycopg/typecast_datetime.c
+++ b/psycopg/typecast_datetime.c
@@ -104,9 +104,18 @@ _parse_inftz(const char *str, PyObject *curs)
goto exit;
}
- if (!(tzinfo = PyObject_CallFunction(tzinfo_factory, "i", 0))) {
- goto exit;
+#if PY_VERSION_HEX < 0x03070000
+ {
+ PyObject *tzoff;
+ if (!(tzoff = PyDelta_FromDSU(0, 0, 0))) { goto exit; }
+ tzinfo = PyObject_CallFunctionObjArgs(tzinfo_factory, tzoff, NULL);
+ Py_DECREF(tzoff);
+ if (!tzinfo) { goto exit; }
}
+#else
+ tzinfo = PyDateTime_TimeZone_UTC;
+ Py_INCREF(tzinfo);
+#endif
/* m.replace(tzinfo=tzinfo) */
if (!(args = PyTuple_New(0))) { goto exit; }
diff --git a/tests/test_dates.py b/tests/test_dates.py
index 728bb57..850c991 100755
--- a/tests/test_dates.py
+++ b/tests/test_dates.py
@@ -26,7 +26,7 @@
import sys
import math
import pickle
-from datetime import date, datetime, time, timedelta
+from datetime import date, datetime, time, timedelta, timezone
import psycopg2
from psycopg2.tz import FixedOffsetTimezone, ZERO
@@ -192,6 +192,22 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
value_utc = value.astimezone(UTC).replace(tzinfo=None)
self.assertEqual(base - value_utc, timedelta(seconds=offset))
+ def test_default_tzinfo(self):
+ self.curs.execute("select '2000-01-01 00:00+02:00'::timestamptz")
+ dt = self.curs.fetchone()[0]
+ self.assert_(isinstance(dt.tzinfo, timezone))
+ self.assertEqual(dt,
+ datetime(2000, 1, 1, tzinfo=timezone(timedelta(minutes=120))))
+
+ def test_fotz_tzinfo(self):
+ self.curs.tzinfo_factory = FixedOffsetTimezone
+ self.curs.execute("select '2000-01-01 00:00+02:00'::timestamptz")
+ dt = self.curs.fetchone()[0]
+ self.assert_(not isinstance(dt.tzinfo, timezone))
+ self.assert_(isinstance(dt.tzinfo, FixedOffsetTimezone))
+ self.assertEqual(dt,
+ datetime(2000, 1, 1, tzinfo=timezone(timedelta(minutes=120))))
+
def test_parse_datetime_timezone(self):
self.check_datetime_tz("+01", 3600)
self.check_datetime_tz("-01", -3600)