]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Better workaround for bad datetime comparison Python bug
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Thu, 28 Oct 2021 22:51:11 +0000 (00:51 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Thu, 28 Oct 2021 22:51:11 +0000 (00:51 +0200)
The problem doesn't just happen far away in the future. 2255 is enough.
See https://bugs.python.org/issue45347#msg405284

Compare timestamps by checking if their delta is 0, convert ranges bounds
to utc.

Fun fact: pytest --randomly-seed=293472621 triggers both the multirange
array bug and this.

tests/fix_faker.py

index 79f3dcbff57e4cc3b8f82ef6d22643b32aeefd64..36ced9f5e549a3c5b6f3ec3f7436512b390173ac 100644 (file)
@@ -341,18 +341,19 @@ class Faker:
     def make_datetime(self, spec):
         # Add a day because with timezone we might go BC
         dtmin = dt.datetime.min + dt.timedelta(days=1)
-
-        # Comparisons might get unreliable too far in the future
-        # https://bugs.python.org/issue45347
-        # delta = dt.datetime.max - dtmin
-        delta = dt.timedelta(days=3000 * 365)
-
+        delta = dt.datetime.max - dtmin
         micros = randrange((delta.days + 1) * 24 * 60 * 60 * 1_000_000)
         rv = dtmin + dt.timedelta(microseconds=micros)
         if spec[1]:
             rv = rv.replace(tzinfo=self._make_tz(spec))
         return rv
 
+    def match_datetime(self, spec, got, want):
+        # Comparisons with different timezones is unreliable: certain pairs
+        # are reported different but their delta is 0
+        # https://bugs.python.org/issue45347
+        assert not (got - want)
+
     def make_Decimal(self, spec):
         if random() >= 0.99:
             if self.conn.info.server_version >= 140000:
@@ -718,6 +719,17 @@ class Faker:
                     want.lower, want.upper + unit, want.bounds[0] + ")"
                 )
 
+        if spec[1] == (dt.datetime, True):
+            # work around https://bugs.python.org/issue45347
+            def fix_dt(x):
+                return x.astimezone(dt.timezone.utc) if x is not None else None
+
+            def fix_range(r):
+                return type(r)(fix_dt(r.lower), fix_dt(r.upper), r.bounds)
+
+            want = fix_range(want)
+            got = fix_range(got)
+
         assert got == want
 
     def match_Int4Range(self, spec, got, want):