From: Daniele Varrazzo Date: Sun, 6 Sep 2020 11:50:12 +0000 (+0100) Subject: Added datetime dump X-Git-Tag: 3.0.dev0~437 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3ff13103a06f64e7c8842493499818e6f2822cf3;p=thirdparty%2Fpsycopg.git Added datetime dump --- diff --git a/psycopg3/psycopg3/types/date.py b/psycopg3/psycopg3/types/date.py index 51de1b9ee..89400767e 100644 --- a/psycopg3/psycopg3/types/date.py +++ b/psycopg3/psycopg3/types/date.py @@ -6,7 +6,7 @@ Adapters for date/time types. import re import codecs -from datetime import date +from datetime import date, datetime from ..adapt import Dumper, Loader from ..proto import AdaptContext @@ -30,6 +30,22 @@ class DateDumper(Dumper): return self.DATE_OID +@Dumper.text(datetime) +class DateTimeDumper(Dumper): + + _encode = codecs.lookup("ascii").encode + TIMESTAMPTZ_OID = builtins["timestamptz"].oid + + def dump(self, obj: date) -> bytes: + # NOTE: whatever the PostgreSQL DateStyle input format (DMY, MDY, YMD) + # the YYYY-MM-DD is always understood correctly. + return self._encode(str(obj))[0] + + @property + def oid(self) -> int: + return self.TIMESTAMPTZ_OID + + @Loader.text(builtins["date"].oid) class DateLoader(Loader): diff --git a/tests/types/test_date.py b/tests/types/test_date.py index 4fd76ac01..81d85b700 100644 --- a/tests/types/test_date.py +++ b/tests/types/test_date.py @@ -5,6 +5,10 @@ import psycopg3 from psycopg3.adapt import Format +# +# date tests +# + @pytest.mark.parametrize( "val, expr", [ @@ -36,8 +40,8 @@ def test_dump_date_binary(conn, val, expr): def test_dump_date_datestyle(conn, datestyle_in): cur = conn.cursor() cur.execute(f"set datestyle = ISO, {datestyle_in}") - cur.execute("select '2000-01-02'::date = %s", (dt.date(2000, 1, 2),)) - assert cur.fetchone()[0] + cur.execute("select 'epoch'::date + 1 = %s", (dt.date(1970, 1, 2),)) + assert cur.fetchone()[0] is True @pytest.mark.parametrize( @@ -91,3 +95,103 @@ def test_load_date_too_large(conn, datestyle_out): cur.execute("select %s + 1", (dt.date.max,)) with pytest.raises(psycopg3.InterfaceError): cur.fetchone()[0] + + +# +# datetime tests +# + +@pytest.mark.parametrize( + "val, expr", + [ + (dt.datetime.min, "'0001-01-01 00:00'::timestamp"), + (dt.datetime(1000, 1, 1, 0, 0), "'1000-01-01 00:00'::timestamp"), + (dt.datetime(2000, 1, 1, 0, 0), "'2000-01-01 00:00'::timestamp"), + ( + dt.datetime(2000, 12, 31, 23, 59, 59, 999999), + "'2000-12-31 23:59:59.999999'::timestamp", + ), + (dt.datetime(3000, 1, 1, 0, 0), "'3000-01-01 00:00'::timestamp"), + (dt.datetime.max, "'9999-12-31 23:59:59.999999'::timestamp"), + ], +) +def test_dump_datetime(conn, val, expr): + cur = conn.cursor() + cur.execute("set timezone to '+02:00'") + cur.execute("select %s = %%s" % expr, (val,)) + assert cur.fetchone()[0] is True + + +@pytest.mark.xfail # TODO: binary dump +@pytest.mark.parametrize( + "val, expr", + [(dt.datetime(2000, 1, 1, 0, 0), "'2000-01-01 00:00'::timestamp")], +) +def test_dump_datetime_binary(conn, val, expr): + cur = conn.cursor() + cur.execute("set timezone to '+02:00'") + cur.execute("select %s = %%b" % expr, (val,)) + assert cur.fetchone()[0] is True + + +@pytest.mark.parametrize("datestyle_in", ["DMY", "MDY", "YMD"]) +def test_dump_datetime_datestyle(conn, datestyle_in): + cur = conn.cursor() + cur.execute(f"set datestyle = ISO, {datestyle_in}") + cur.execute( + "select 'epoch'::timestamp + '1d 3h 4m 5s'::interval = %s", + (dt.datetime(1970, 1, 2, 3, 4, 5),), + ) + assert cur.fetchone()[0] is True + + +# +# datetime+tz tests +# + +@pytest.mark.parametrize( + "val, expr", + [ + (dt.datetime.min, "'0001-01-01 00:00'::timestamptz"), + (dt.datetime(1000, 1, 1, 0, 0), "'1000-01-01 00:00+2'::timestamptz"), + (dt.datetime(2000, 1, 1, 0, 0), "'2000-01-01 00:00+2'::timestamptz"), + ( + dt.datetime(2000, 12, 31, 23, 59, 59, 999999), + "'2000-12-31 23:59:59.999999+2'::timestamptz", + ), + (dt.datetime(3000, 1, 1, 0, 0), "'3000-01-01 00:00+2'::timestamptz"), + (dt.datetime.max, "'9999-12-31 23:59:59.999999'::timestamptz"), + ], +) +def test_dump_datetimetz(conn, val, expr): + val = val.replace(tzinfo=dt.timezone(dt.timedelta(hours=2))) + cur = conn.cursor() + cur.execute("set timezone to '-02:00'") + cur.execute("select %s = %%s" % expr, (val,)) + assert cur.fetchone()[0] is True + + +@pytest.mark.xfail # TODO: binary dump +@pytest.mark.parametrize( + "val, expr", + [(dt.datetime(2000, 1, 1, 0, 0), "'2000-01-01 00:00'::timestamptz")], +) +def test_dump_datetimetz_binary(conn, val, expr): + val = val.replace(tzinfo=dt.timezone(dt.timedelta(hours=2))) + cur = conn.cursor() + cur.execute("set timezone to '-02:00'") + cur.execute("select %s = %%b" % expr, (val,)) + assert cur.fetchone()[0] is True + + +@pytest.mark.parametrize("datestyle_in", ["DMY", "MDY", "YMD"]) +def test_dump_datetimetz_datestyle(conn, datestyle_in): + tzinfo = dt.timezone(dt.timedelta(hours=2)) + cur = conn.cursor() + cur.execute(f"set datestyle = ISO, {datestyle_in}") + cur.execute("set timezone to '-02:00'") + cur.execute( + "select 'epoch'::timestamptz + '1d 3h 4m 5.678s'::interval = %s", + (dt.datetime(1970, 1, 2, 5, 4, 5, 678000, tzinfo=tzinfo),), + ) + assert cur.fetchone()[0] is True