[Python-checkins] python/nondist/sandbox/datetime US.py,1.12,1.13 datetime.py,1.128,1.129 doc.txt,1.74,1.75 test_datetime.py,1.87,1.88

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Mon, 30 Dec 2002 11:43:23 -0800


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

Modified Files:
	US.py datetime.py doc.txt test_datetime.py 
Log Message:
A step on the way to making tzinfo classes writable by mortals:  get rid
of the timetz case.  A tzinfo method will always see a datetimetz arg,
or None, now.  In the former case, it's still possible that it will get
a datetimetz argument belonging to a different timezone.  That will get
fixed next (but I have to pause to bring the 2.3 C implementation into
synch with this change first).


Index: US.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/US.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** US.py	28 Dec 2002 18:16:46 -0000	1.12
--- US.py	30 Dec 2002 19:43:21 -0000	1.13
***************
*** 68,72 ****
  
      def dst(self, dt):
!         if dt is None or isinstance(dt, time) or dt.tzinfo is None:
              # An exception instead may be sensible here, in one or more of
              # the cases.
--- 68,72 ----
  
      def dst(self, dt):
!         if dt is None or dt.tzinfo is None:
              # An exception instead may be sensible here, in one or more of
              # the cases.

Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.128
retrieving revision 1.129
diff -C2 -d -r1.128 -r1.129
*** datetime.py	27 Dec 2002 21:31:40 -0000	1.128
--- datetime.py	30 Dec 2002 19:43:21 -0000	1.129
***************
*** 217,224 ****
      return _time.strftime(newformat, timetuple)
  
! def _call_tzinfo_method(self, tzinfo, methname):
      if tzinfo is None:
          return None
!     return getattr(tzinfo, methname)(self)
  
  # Just raise TypeError if the arg isn't None or a string.
--- 217,224 ----
      return _time.strftime(newformat, timetuple)
  
! def _call_tzinfo_method(tzinfo, methname, tzinfoarg):
      if tzinfo is None:
          return None
!     return getattr(tzinfo, methname)(tzinfoarg)
  
  # Just raise TypeError if the arg isn't None or a string.
***************
*** 1129,1133 ****
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
--- 1129,1133 ----
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
***************
*** 1137,1141 ****
      # Return an integer (or None) instead of a timedelta (or None).
      def _utcoffset(self):
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          return offset
--- 1137,1141 ----
      # Return an integer (or None) instead of a timedelta (or None).
      def _utcoffset(self):
!         offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
          offset = _check_utc_offset("utcoffset", offset)
          return offset
***************
*** 1148,1152 ****
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self._tzinfo, "tzname")
          _check_tzname(name)
          return name
--- 1148,1152 ----
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self._tzinfo, "tzname", None)
          _check_tzname(name)
          return name
***************
*** 1161,1165 ****
          info.
          """
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
--- 1161,1165 ----
          info.
          """
!         offset = _call_tzinfo_method(self._tzinfo, "dst", None)
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
***************
*** 1186,1190 ****
      # Return an integer (or None) instead of a timedelta (or None).
      def _dst(self):
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          return offset
--- 1186,1190 ----
      # Return an integer (or None) instead of a timedelta (or None).
      def _dst(self):
!         offset = _call_tzinfo_method(self._tzinfo, "dst", None)
          offset = _check_utc_offset("dst", offset)
          return offset
***************
*** 1650,1654 ****
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
--- 1650,1654 ----
          """Return the timezone offset in minutes east of UTC (negative west of
          UTC)."""
!         offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
          offset = _check_utc_offset("utcoffset", offset)
          if offset is not None:
***************
*** 1658,1662 ****
      # Return an integer (or None) instead of a timedelta (or None).
      def _utcoffset(self):
!         offset = _call_tzinfo_method(self, self._tzinfo, "utcoffset")
          offset = _check_utc_offset("utcoffset", offset)
          return offset
--- 1658,1662 ----
      # Return an integer (or None) instead of a timedelta (or None).
      def _utcoffset(self):
!         offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
          offset = _check_utc_offset("utcoffset", offset)
          return offset
***************
*** 1669,1673 ****
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self, self._tzinfo, "tzname")
          _check_tzname(name)
          return name
--- 1669,1673 ----
          "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
          """
!         name = _call_tzinfo_method(self._tzinfo, "tzname", self)
          _check_tzname(name)
          return name
***************
*** 1682,1686 ****
          info.
          """
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
--- 1682,1686 ----
          info.
          """
!         offset = _call_tzinfo_method(self._tzinfo, "dst", self)
          offset = _check_utc_offset("dst", offset)
          if offset is not None:
***************
*** 1690,1694 ****
      # Return an integer (or None) instead of a timedelta (or None).1573
      def _dst(self):
!         offset = _call_tzinfo_method(self, self._tzinfo, "dst")
          offset = _check_utc_offset("dst", offset)
          return offset
--- 1690,1694 ----
      # Return an integer (or None) instead of a timedelta (or None).1573
      def _dst(self):
!         offset = _call_tzinfo_method(self._tzinfo, "dst", self)
          offset = _check_utc_offset("dst", offset)
          return offset

Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.74
retrieving revision 1.75
diff -C2 -d -r1.74 -r1.75
*** doc.txt	27 Dec 2002 21:31:40 -0000	1.74
--- doc.txt	30 Dec 2002 19:43:21 -0000	1.75
***************
*** 760,764 ****
  class should not be instantiated.  You need to derive a concrete
  subclass, and (at least) supply implementations of the standard tzinfo
! methods needed by the datetime methods you use. The datetime module does
  not supply any concrete subclasses of tzinfo.
  
--- 760,764 ----
  class should not be instantiated.  You need to derive a concrete
  subclass, and (at least) supply implementations of the standard tzinfo
! methods needed by the datetime methods you use.  The datetime module does
  not supply any concrete subclasses of tzinfo.
  
***************
*** 777,786 ****
  A concrete subclass of tzinfo may need to implement the following
  methods.  Exactly which methods are needed depends on the uses made
! of aware datetime objects; if in doubt, simply implement all of them.
! The methods are called by a datetimetz or timetz object, passing itself
! as the argument.  A tzinfo subclass's methods should be prepared to
! accept an argument of type None, timetz, or datetimetz.
  
!   - utcoffset(dt)
      Return offset of local time from UTC, in minutes east of UTC.  If
      local time is west of UTC, this should be negative.  Note that this
--- 777,783 ----
  A concrete subclass of tzinfo may need to implement the following
  methods.  Exactly which methods are needed depends on the uses made
! of aware datetime objects.  If in doubt, simply implement all of them.
  
!   - utcoffset(self, dt)
      Return offset of local time from UTC, in minutes east of UTC.  If
      local time is west of UTC, this should be negative.  Note that this
***************
*** 791,797 ****
      timedelta object, in the range -1439 to 1439 inclusive (1440 = 24*60;
      the magnitude of the offset must be less than one day, and must be
!     a whole number of minutes).
  
!   - tzname(dt)
      Return the timezone name corresponding to the datetime represented
      by dt, as a string.  Nothing about string names is defined by the
--- 788,798 ----
      timedelta object, in the range -1439 to 1439 inclusive (1440 = 24*60;
      the magnitude of the offset must be less than one day, and must be
!     a whole number of minutes).  Most implementations of utcoffset()
!     will probably look like:
  
!         return CONSTANT  # fixed-offset class
!         return CONSTANT + self.dst(dt)  # daylight-aware class
! 
!   - tzname(self, dt)
      Return the timezone name corresponding to the datetime represented
      by dt, as a string.  Nothing about string names is defined by the
***************
*** 804,808 ****
      of dt passed, especially if the tzinfo class is accounting for DST.
  
!   - dst(dt)
      Return the DST offset, in minutes east of UTC, or None if DST
      information isn't known.  Return 0 if DST is not in effect.
--- 805,809 ----
      of dt passed, especially if the tzinfo class is accounting for DST.
  
!   - dst(self, dt)
      Return the DST offset, in minutes east of UTC, or None if DST
      information isn't known.  Return 0 if DST is not in effect.
***************
*** 815,818 ****
--- 816,840 ----
      tzinfo object's dst() method to determine how the tm_isdst flag
      should be set.
+ 
+ These methods are called by a datetimetz or timetz object, in response to
+ their methods of the same names.  A datetimetz object passes itself as the
+ argument, and a timetz object passes None as the argument.  A tzinfo
+ subclass's methods should therefore be prepared to accept a dt argument of
+ None, or of class datetimetz.
+ 
+ When None is passed, it's up to the class designer to decide the best
+ response.  For example, returning None is appropriate if the class wishes
+ to say that timetz objects don't participate in the tzinfo protocol.  In
+ other applications, it may be more useful for utcoffset(None} to return the
+ standard UTC offset.
+ 
+ When a datetimetz object is passed in response to a datetimetz method,
+ dt.tzinfo is the same object as self.  tzinfo methods can rely on this,
+ unless user code calls tzinfo methods directly.  The intent is that the
+ tzinfo methods interpret dt as being in local time, and not need to worry
+ about objects in other timezones.
+ XXX That isn't always true yet:  datetimetz.astimezone(tz) can pass
+ XXX a datetimetz with a "foreign" tzinfo to a self.tzinfo method.  This
+ XXX needs to be repaired.
  
  Example tzinfo classes:

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.87
retrieving revision 1.88
diff -C2 -d -r1.87 -r1.88
*** test_datetime.py	30 Dec 2002 17:36:58 -0000	1.87
--- test_datetime.py	30 Dec 2002 19:43:21 -0000	1.88
***************
*** 1570,1573 ****
--- 1570,1590 ----
  class TZInfoBase(unittest.TestCase):
  
+     def test_argument_passing(self):
+         cls = self.theclass
+         # A datetimetz passes itself on, a timetz passes None.
+         class introspective(tzinfo):
+             def tzname(self, dt):    return dt and "real" or "none"
+             def utcoffset(self, dt): return dt and 42 or -42
+             dst = utcoffset
+ 
+         obj = cls(1, 2, 3, tzinfo=introspective())
+ 
+         expected = cls is timetz and "none" or "real"
+         self.assertEqual(obj.tzname(), expected)
+ 
+         expected = timedelta(minutes=(cls is timetz and -42 or 42))
+         self.assertEqual(obj.utcoffset(), expected)
+         self.assertEqual(obj.dst(), expected)
+ 
      def test_bad_tzinfo_classes(self):
          cls = self.theclass
***************
*** 1690,1709 ****
  
          # However, if they're different members, uctoffset is not ignored.
!         d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
!         d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
!         d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
!         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)
  
  
--- 1707,1730 ----
  
          # However, if they're different members, uctoffset is not ignored.
!         # Note that a timetz can't actually have an operand-depedent offset,
!         # though (and timetz.utcoffset() passes None to tzinfo.utcoffset()),
!         # so skip this test for timetz.
!         if cls is not timetz:
!             d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
!             d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
!             d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
!             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)
  
  
***************
*** 2550,2554 ****
  
      def dst(self, dt):
!         if dt is None or isinstance(dt, time) or dt.tzinfo is None:
              # An exception instead may be sensible here, in one or more of
              # the cases.
--- 2571,2575 ----
  
      def dst(self, dt):
!         if dt is None or dt.tzinfo is None:
              # An exception instead may be sensible here, in one or more of
              # the cases.