[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')