[Python-checkins] python/nondist/sandbox/datetime US.py,1.5,1.6 datetime.py,1.125,1.126 test_datetime.py,1.82,1.83
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Wed, 25 Dec 2002 20:46:36 -0800
Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv1978
Modified Files:
US.py datetime.py 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).
Index: US.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/US.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** US.py 26 Dec 2002 01:17:55 -0000 1.5
--- US.py 26 Dec 2002 04:46:34 -0000 1.6
***************
*** 131,146 ****
Sun Apr 7 02:00:00 2002
! The difference is confusing. 2:00:00 doesn't exist on the naive clock (the
! naive clock leaps from 1:59:59 to 3:00:00), and is taken to be in DST, as a
! redundant spelling of 1:00:00 standard time. So we actually expect b to be
! about an hour *before* a. But subtraction doesn't do that: because the
! tzinfo objects are identical, subtraction ignores them, and the difference
! comes out as positive one second.
! >>> print after - before
! 0:00:01
! OTOH, if we convert them to UTC and subtract, the difference is about
! an hour.
>>> class UTC(tzinfo):
--- 131,142 ----
Sun Apr 7 02:00:00 2002
! 2:00:00 doesn't exist on the naive clock (the naive clock leaps from 1:59:59
! to 3:00:00), and is taken to be in DST, as a redundant spelling of 1:00:00
! standard time. So we actually expect b to be about an hour *before* a.
! >>> print -(after - before)
! 0:59:59
! Converting to UTC and subtracting, we should get the same thing.
>>> class UTC(tzinfo):
***************
*** 175,183 ****
The naive clock repeats the times in 1:HH:MM, so 1:59:59 was actually
ambiguous, and resolved arbitrarily as being in DST. 2:00:00 is in standard
! time, and is actually about an hour later, but the tzinfo objects are the same
! so the offsets are again ignored by subtraction.
>>> print after - before
! 0:00:01
>>> print after.astimezone(utc) - before.astimezone(utc)
1:00:01
--- 171,178 ----
The naive clock repeats the times in 1:HH:MM, so 1:59:59 was actually
ambiguous, and resolved arbitrarily as being in DST. 2:00:00 is in standard
! time, and is actually about an hour later.
>>> print after - before
! 1:00:01
>>> print after.astimezone(utc) - before.astimezone(utc)
1:00:01
Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.125
retrieving revision 1.126
diff -C2 -d -r1.125 -r1.126
*** datetime.py 25 Dec 2002 19:52:25 -0000 1.125
--- datetime.py 26 Dec 2002 04:46:34 -0000 1.126
***************
*** 1057,1066 ****
superself = super(timetz, self)
supercmp = superself.__cmp__
- mytz = self.__tzinfo
- ottz = None
- if isinstance(other, timetz):
- ottz = other.__tzinfo
- if mytz is ottz:
- return supercmp(other)
myoff = self._utcoffset()
otoff = other._utcoffset()
--- 1057,1060 ----
***************
*** 1673,1682 ****
supersub = super(datetimetz, self).__sub__
if not isinstance(other, datetime):
! return supersub(other) # XXX should set tzinfo on result
! mytz = self.__tzinfo
! ottz = None
! if isinstance(other, datetimetz):
! ottz = other.__tzinfo
! if mytz is ottz:
return supersub(other)
myoff = self._utcoffset()
--- 1667,1674 ----
supersub = super(datetimetz, self).__sub__
if not isinstance(other, datetime):
! # This manages to attach self.tzinfo to the result via a
! # devious route: self - timedelta is changed to
! # self + (-timedelta) by datetime.__sub__, and the latter is
! # handled by datetimetz.__add__.
return supersub(other)
myoff = self._utcoffset()
Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.82
retrieving revision 1.83
diff -C2 -d -r1.82 -r1.83
*** test_datetime.py 25 Dec 2002 19:52:25 -0000 1.82
--- test_datetime.py 26 Dec 2002 04:46:34 -0000 1.83
***************
*** 1667,1670 ****
--- 1667,1701 ----
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):
***************
*** 1875,1878 ****
--- 1906,1910 ----
self.assertRaises(ValueError, base.replace, microsecond=1000000)
+
class TestDateTimeTZ(TestDateTime, TZInfoBase):
theclass = datetimetz
***************
*** 2056,2061 ****
now = self.theclass.now()
tz55 = FixedOffset(-330, "west 5:30")
! timeaware = timetz(now.hour, now.minute, now.second,
! now.microsecond, tzinfo=tz55)
nowaware = self.theclass.combine(now.date(), timeaware)
self.failUnless(nowaware.tzinfo is tz55)
--- 2088,2092 ----
now = self.theclass.now()
tz55 = FixedOffset(-330, "west 5:30")
! timeaware = now.timetz().replace(tzinfo=tz55)
nowaware = self.theclass.combine(now.date(), timeaware)
self.failUnless(nowaware.tzinfo is tz55)
***************
*** 2093,2103 ****
# 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))
self.failUnless(nowawareplus.tzinfo is tzr)
# Make sure the difference takes the timezone adjustments into account.
--- 2124,2129 ----
# Make up a random timezone.
tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
! # 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.
***************
*** 2372,2375 ****
--- 2398,2433 ----
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)