[Zope3-checkins] CVS: Zope3/src/datetime/tests - test_datetime.py:1.3

Tim Peters tim.one@comcast.net
Wed, 25 Dec 2002 23:49:47 -0500


Update of /cvs-repository/Zope3/src/datetime/tests
In directory cvs.zope.org:/tmp/cvs-serv11249/src/datetime/tests

Modified Files:
	test_datetime.py 
Log Message:
timetz comparison, and datetimetz subtraction:  reimplemented these to
work as documented.  They weren't calling utcoffset() if both operands
had the same tzinfo object.  That's fine if it so happens that the
shared tzinfo object returns a fixed offset (independent of operand),
but can give wrong results if that's not so, and the latter obtains in
a tzinfo subclass instance trying to model both standard and daylight
times.

This repairs some of the surprises in the sandbox US.py demo, but
not all of them.  In a way, it makes one part even more surprising:
because addition of datetimetz and timedelta doesn't do anything with
tzinfo except attach it to the result, it's possible to add, e.g.,
one second to a datetimetz, and then see the result of that minus the
original datetimetz span an hour.  This can happen at DST boundaries
(see US.py's doctest for concrete examples).


=== Zope3/src/datetime/tests/test_datetime.py 1.2 => 1.3 ===
--- Zope3/src/datetime/tests/test_datetime.py:1.2	Wed Dec 25 09:12:12 2002
+++ Zope3/src/datetime/tests/test_datetime.py	Wed Dec 25 23:49:46 2002
@@ -1662,6 +1662,37 @@
         self.assertRaises(ValueError, t.utcoffset)
         self.assertRaises(ValueError, t.dst)
 
+    def test_aware_compare(self):
+        cls = self.theclass
+
+        # Primarily trying to ensure that utcoffset() gets called even if
+        # the comparands have the same tzinfo member.  timetz comparison
+        # didn't used to do so, although datetimetz comparison did.
+        class OperandDependentOffset(tzinfo):
+            def utcoffset(self, t):
+                if t.minute < 10:
+                    return t.minute # d0 and d1 equal after adjustment
+                else:
+                    return 59       # d2 off in the weeds
+
+        base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
+        d0 = base.replace(minute=3)
+        d1 = base.replace(minute=9)
+        d2 = base.replace(minute=11)
+        for x in d0, d1, d2:
+            for y in d0, d1, d2:
+                got = cmp(x, y)
+                if (x is d0 or x is d1) and (y is d0 or y is d1):
+                    expected = 0
+                elif x is y is d2:
+                    expected = 0
+                elif x is d2:
+                    expected = -1
+                else:
+                    assert y is d2
+                    expected = 1
+                self.assertEqual(got, expected)
+
 
 class TestTimeTZ(TestTime, TZInfoBase):
     theclass = timetz
@@ -1870,6 +1901,7 @@
         self.assertRaises(ValueError, base.replace, second=100)
         self.assertRaises(ValueError, base.replace, microsecond=1000000)
 
+
 class TestDateTimeTZ(TestDateTime, TZInfoBase):
     theclass = datetimetz
 
@@ -2051,8 +2083,7 @@
 
         now = self.theclass.now()
         tz55 = FixedOffset(-330, "west 5:30")
-        timeaware = timetz(now.hour, now.minute, now.second,
-                           now.microsecond, tzinfo=tz55)
+        timeaware = now.timetz().replace(tzinfo=tz55)
         nowaware = self.theclass.combine(now.date(), timeaware)
         self.failUnless(nowaware.tzinfo is tz55)
         self.assertEqual(nowaware.timetz(), timeaware)
@@ -2088,13 +2119,8 @@
 
         # Make up a random timezone.
         tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
-        # Attach it to nowawareplus -- this is clumsy.
-        nowawareplus = self.theclass.combine(nowawareplus.date(),
-                                             timetz(nowawareplus.hour,
-                                                    nowawareplus.minute,
-                                                    nowawareplus.second,
-                                                    nowawareplus.microsecond,
-                                                    tzinfo=tzr))
+        # Attach it to nowawareplus.
+        nowawareplus = nowawareplus.replace(tzinfo=tzr)
         self.failUnless(nowawareplus.tzinfo is tzr)
         # Make sure the difference takes the timezone adjustments into account.
         got = nowaware - nowawareplus
@@ -2367,6 +2393,38 @@
         self.assertEqual(got.timetz(), expected.timetz())
         self.failUnless(got.tzinfo is expected.tzinfo)
         self.assertEqual(got, expected)
+
+    def test_aware_subtract(self):
+        cls = self.theclass
+
+        # Primarily trying to ensure that utcoffset() gets called even if
+        # the operands have the same tzinfo member.  Subtraction didn't
+        # used to do this, and it makes a difference for DST-aware tzinfo
+        # instances.
+        class OperandDependentOffset(tzinfo):
+            def utcoffset(self, t):
+                if t.minute < 10:
+                    return t.minute # d0 and d1 equal after adjustment
+                else:
+                    return 59       # d2 off in the weeds
+
+        base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
+        d0 = base.replace(minute=3)
+        d1 = base.replace(minute=9)
+        d2 = base.replace(minute=11)
+        for x in d0, d1, d2:
+            for y in d0, d1, d2:
+                got = x - y
+                if (x is d0 or x is d1) and (y is d0 or y is d1):
+                    expected = timedelta(0)
+                elif x is y is d2:
+                    expected = timedelta(0)
+                elif x is d2:
+                    expected = timedelta(minutes=(11-59)-0)
+                else:
+                    assert y is d2
+                    expected = timedelta(minutes=0-(11-59))
+                self.assertEqual(got, expected)
 
 
 def test_suite():