[Python-checkins] CVS: python/nondist/sandbox/datetime datetime.py,1.37,1.38 test_datetime.py,1.23,1.24

Tim Peters tim_one@users.sourceforge.net
Sun, 03 Mar 2002 22:10:17 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory usw-pr-cvs1:/tmp/cvs-serv26196

Modified Files:
	datetime.py test_datetime.py 
Log Message:
Fixed two bugs in datetime.__cmp__:

1. Shallow.  When the tzoffsets matched, it forgot to compare
   microseconds.

2. Not so shallow.  When the tzoffsets didn't match, it reverted to
   comparing timestamps, but an IEEE double doesn't have enough
   precision to avoid mapping distinct datetimes onto the same timestamp.
   It takes about 38 bits to distinguish the individual seconds across
   10000 years, leaving only about 53-38 = 15 bits to distinguish the
   microseconds.  As a result, non-equal datetimes could compare equal,
   especially for years near MAXYEAR.


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -C2 -d -r1.37 -r1.38
*** datetime.py	4 Mar 2002 03:01:14 -0000	1.37
--- datetime.py	4 Mar 2002 06:10:15 -0000	1.38
***************
*** 578,592 ****
          "Three-way comparison."
          if isinstance(other, datetime):
!             if other.__tzoffset == self.__tzoffset:
                  y, m, d = self.__year, self.__month, self.__day
!                 hh, mm, ss = self.__hour, self.__minute, self.__second
!                 tz = self.__tzoffset
                  y2, m2, d2 = other.__year, other.__month, other.__day
!                 hh2, mm2, ss2 = other.__hour, other.__minute, other.__second
!                 tz2 = other.__tzoffset
!                 return cmp((y, m, d, hh, mm, ss, tz),
!                            (y2, m2, d2, hh2, mm2, ss2, tz2))
              else:
!                 return cmp(self._mktime(), other._mktime())
          else:
              raise TypeError, ("can't compare datetime to %s instance" %
--- 578,593 ----
          "Three-way comparison."
          if isinstance(other, datetime):
!             if self.__tzoffset == other.__tzoffset:
                  y, m, d = self.__year, self.__month, self.__day
!                 H, M = self.__hour, self.__minute
                  y2, m2, d2 = other.__year, other.__month, other.__day
!                 H2, M2 = other.__hour, other.__minute
              else:
!                 y, m, d, H, M = self._utc_ymdHM()
!                 y2, m2, d2, H2, M2 = other._utc_ymdHM()
!             return cmp((y, m, d, H, M,
!                         self.__second, self.__microsecond),
!                        (y2, m2, d2, H2, M2,
!                         other.__second, other.__microsecond))
          else:
              raise TypeError, ("can't compare datetime to %s instance" %

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.23
retrieving revision 1.24
diff -C2 -d -r1.23 -r1.24
*** test_datetime.py	4 Mar 2002 01:24:50 -0000	1.23
--- test_datetime.py	4 Mar 2002 06:10:15 -0000	1.24
***************
*** 7,11 ****
  import unittest
  
! from datetime import datetime, timedelta
  
  class TestDateTime(unittest.TestCase):
--- 7,11 ----
  import unittest
  
! from datetime import datetime, timedelta, MINYEAR, MAXYEAR
  
  class TestDateTime(unittest.TestCase):
***************
*** 42,47 ****
          self.assert_(dt2 > dt3)
  
      def test_ordinal_conversions(self):
!         from datetime import _ymd2ord, _ord2ymd, MINYEAR, MAXYEAR
  
          # Check some fixed values.
--- 42,66 ----
          self.assert_(dt2 > dt3)
  
+         # Make sure comparison doesn't forget microseconds, and isn't done
+         # via comparing a float timestamp (an IEEE double doesn't have enough
+         # precision to span microsecond resolution across years 1 thru 9999,
+         # so comparing via timestamp necessarily calls some distinct values
+         # equal).
+         dt1 = datetime.new(MAXYEAR, 12, 31, 23, 59, 59, 999998, 0)
+         us = timedelta(microseconds=1)
+         dt2 = dt1 + us
+         self.assertEqual(dt2 - dt1, us)
+         self.assert_(dt1 < dt2)
+ 
+         # Again, but mix timezones.
+         dt1 = datetime.new(MAXYEAR, 12, 31, 22, 59, 59, 999998, -60)
+         dt2 = datetime.new(MAXYEAR, 12, 31, 23, 59, 59, 999998, 0)
+         self.assert_(dt1 == dt2)
+         dt2 += us
+         self.assertEqual(dt2 - dt1, us)
+         self.assert_(dt1 < dt2)
+ 
      def test_ordinal_conversions(self):
!         from datetime import _ymd2ord, _ord2ymd
  
          # Check some fixed values.
***************
*** 78,82 ****
  
      def test_bad_constructor_arguments(self):
-         from datetime import MINYEAR, MAXYEAR
          # bad years
          datetime(MINYEAR, 1, 1)  # no exception
--- 97,100 ----