[Python-checkins] CVS: python/nondist/sandbox/datetime datetime.py,1.8,1.9 test_datetime.py,1.4,1.5

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


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

Modified Files:
	datetime.py test_datetime.py 
Log Message:
Repair __hash__ so that equal values have equal hashcodes.  This is
done by normalizing (year, month, day, hour, minute) to UTC (and a
new method computes that), then leaving tzoffset out of the hash
computation.  May not be cheap enough; normalizing to UTC *can* propagate
all the way to the year (and a new test verifies that it can).


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** datetime.py	2 Mar 2002 00:09:55 -0000	1.8
--- datetime.py	2 Mar 2002 01:03:08 -0000	1.9
***************
*** 220,232 ****
                                type(other).__name__)
  
      def __hash__(self):
          """Hash."""
!         # XXX Bug:  objects that compare equal must have equal hashcodes,
!         # XXX and two datetimes can be equal even if their fields aren't
!         # XXX (due to tzoffset).  Do we have to hash the timestamp instead?
!         y, m, d = self.__year, self.__month, self.__day
!         hh, mm, ss = self.__hour, self.__minute, self.__second
!         us, tz = self.__microsecond, self.__tzoffset
!         return hash((y, m, d, hh, mm, ss, us, tz))
  
      def ctime(self):
--- 220,265 ----
                                type(other).__name__)
  
+     def _utc_ymdHM(self):
+         "Return (year, month, day, hour, minute) in UTC equivalent."
+         y, m, d = self.__year, self.__month, self.__day
+         H, M = self.__hour, self.__minute
+         if self.__tzoffset:
+             M -= self.__tzoffset
+             if not 0 <= M <= 59:
+                 carry, M = divmod(M, 60)
+                 H += carry
+                 if not 0 <= H <= 23:
+                     carry, H = divmod(H, 24)
+                     # tzoffset is less than a day, so carry is no more than 1.
+                     if carry > 0:
+                         assert carry == 1
+                         d += 1
+                         if d > _days_in_month(m, y):
+                             d = 1
+                             m += 1
+                             if m > 12:
+                                 m = 1
+                                 y += 1
+                     else:
+                         assert carry == -1
+                         d -= 1
+                         if d == 0:
+                             m -= 1
+                             if m > 0:
+                                 d = _days_in_month(m, y)
+                             else:
+                                 d = 31
+                                 m = 12
+                                 y -= 1
+         return y, m, d, H, M
+ 
      def __hash__(self):
          """Hash."""
!         # Caution:  objects that compare equal must have equal hashcodes,
!         # and two datetimes can be equal even if their fields aren't
!         # (due to tzoffset).  So we have to compute a UTC-normalized hash.
!         # XXX _utc_ymdHM can be expensive; dream up a quicker correct way.
!         y, m, d, H, M = self._utc_ymdHM()
!         return hash((y, m, d, H, M, self.__second, self.__microsecond))
  
      def ctime(self):

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** test_datetime.py	1 Mar 2002 23:55:55 -0000	1.4
--- test_datetime.py	2 Mar 2002 01:03:08 -0000	1.5
***************
*** 74,77 ****
--- 74,88 ----
          self.assertRaises(ValueError, datetime, 2000, 1, 31, 23, 59, 60)
  
+     def test_hash_equality(self):
+         d = datetime(2000, 12, 31, 23, 30, 17, tzoffset=-35)
+         # same thing in UTC.
+         e = datetime(2001,  1,  1,  0,  5, 17, tzoffset=0)
+         self.assertEqual(hash(d), hash(e))
+ 
+         d = datetime(2001,  1,  1,  0,  5, 17, tzoffset=35)
+         # same thing in UTC.
+         e = datetime(2000, 12, 31, 23, 30, 17, tzoffset=0)
+         self.assertEqual(hash(d), hash(e))
+ 
  def test_suite():
      s1 = unittest.makeSuite(TestDateTime, 'test')