]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Added datetime dump
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 6 Sep 2020 11:50:12 +0000 (12:50 +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 51de1b9eebbffd7d5655d382041dfe9b0c26e950..89400767ef1ace44d4432156f0373959ed59256b 100644 (file)
@@ -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):
 
index 4fd76ac01751b046a5d323ada13b8e6380e8b05f..81d85b70046e61e8ca02315390b3b0503da603c4 100644 (file)
@@ -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