[Python-checkins] python/nondist/sandbox/datetime datetime.py,1.108,1.109 doc.txt,1.55,1.56 test_datetime.py,1.69,1.70

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Sun, 15 Dec 2002 16:25:36 -0800


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

Modified Files:
	datetime.py doc.txt test_datetime.py 
Log Message:
Documented "correct" strftime behavior for %z and %Z.  Implemented
correct behavior in the Python implementation.  Added some tests.  Noted
that the C implementation remains broken in this respect (even for a
naive object, on Windows at home %z and %Z both get replaced by "Eastern
Standard Time"; before this checkin, that was also true under the Python
implementation).


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.108
retrieving revision 1.109
diff -C2 -d -r1.108 -r1.109
*** datetime.py	15 Dec 2002 18:30:49 -0000	1.108
--- datetime.py	16 Dec 2002 00:25:34 -0000	1.109
***************
*** 164,170 ****
  
  # Correctly substitute for %z and %Z escapes in strftime formats.
! def _handle_z_escapes(format, Zreplace, zreplace):
!     result = []
!     push = result.append
      i, n = 0, len(format)
      while i < n:
--- 164,175 ----
  
  # Correctly substitute for %z and %Z escapes in strftime formats.
! def _wrap_strftime(object, format, timetuple):
!     # Don't call utcoffset() or tzname() unless actually needed.
!     zreplace = None # the string to use for %z
!     Zreplace = None # the string to use for %Z
! 
!     # Scan format for %z and %Z escapes, replacing as needed.
!     newformat = []
!     push = newformat.append
      i, n = 0, len(format)
      while i < n:
***************
*** 176,186 ****
                  i += 1
                  if ch == 'z':
!                     s = zreplace()
!                     assert '%' not in s
!                     result.append(s)
                  elif ch == 'Z':
!                     # strftime is going to have at this, so escape %
!                     s = Zreplace().replace('%', '%%')
!                     result.append(s)
                  else:
                      push('%')
--- 181,206 ----
                  i += 1
                  if ch == 'z':
!                     if zreplace is None:
!                         zreplace = ""
!                         if hasattr(object, "utcoffset"):
!                             offset = object.utcoffset()
!                             if offset is not None:
!                                 sign = '+'
!                                 if offset < 0:
!                                     offset = -offset
!                                     sign = '-'
!                                 h, m = divmod(offset, 60)
!                                 zreplace = '%c%02d%02d' % (sign, h, m)
!                     assert '%' not in zreplace
!                     newformat.append(zreplace)
                  elif ch == 'Z':
!                     if Zreplace is None:
!                         Zreplace = ""
!                         if hasattr(object, "tzname"):
!                             s = object.tzname()
!                             if s is not None:
!                                 # strftime is going to have at this: escape %
!                                 Zreplace = s.replace('%', '%%')
!                     newformat.append(Zreplace)
                  else:
                      push('%')
***************
*** 190,194 ****
          else:
              push(ch)
!     return "".join(result)
  
  def _check_utc_offset(name, offset):
--- 210,215 ----
          else:
              push(ch)
!     newformat = "".join(newformat)
!     return _time.strftime(newformat, timetuple)
  
  def _check_utc_offset(name, offset):
***************
*** 623,627 ****
      def strftime(self, fmt):
          "Format using strftime()."
!         return _time.strftime(fmt, self.timetuple())
  
      def isoformat(self):
--- 644,648 ----
      def strftime(self, fmt):
          "Format using strftime()."
!         return _wrap_strftime(self, fmt, self.timetuple())
  
      def isoformat(self):
***************
*** 853,859 ****
          to underlying strftime should not be used.
          """
!         return _time.strftime(fmt, (0, 0, 0, self.__hour, self.__minute,
!                                     self.__second, 0, 0, -1))
! 
  
      def __nonzero__(self):
--- 874,881 ----
          to underlying strftime should not be used.
          """
!         timetuple = (0, 0, 0,
!                      self.__hour, self.__minute, self.__second,
!                      0, 0, -1)
!         return _wrap_strftime(self, fmt, timetuple)
  
      def __nonzero__(self):
***************
*** 1025,1048 ****
  
      __str__ = isoformat
- 
-     def strftime(self, fmt):
-         """Format using strftime().  The date part of the timestamp passed
-         to underlying strftime should not be used.
- 
-         You can use %Z to refer to the timezone name and %z to refer to its
-         UTC offset (+zzzz).
-         """
- 
-         # Don't call utcoffset or tzname unless the format string really
-         # needs them -- there's no requirement in the docs that a tzinfo
-         # object implement every method.
-         def getz():
-            return self._tzstr('') or ""
- 
-         def getZ():
-             return self.tzname() or ""
- 
-         fmt = _handle_z_escapes(fmt, getZ, getz)
-         return super(timetz, self).strftime(fmt)
  
      # Timezone functions
--- 1047,1050 ----

Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.55
retrieving revision 1.56
diff -C2 -d -r1.55 -r1.56
*** doc.txt	15 Dec 2002 18:30:49 -0000	1.55
--- doc.txt	16 Dec 2002 00:25:34 -0000	1.56
***************
*** 4,8 ****
  
  - The custom %z and %Z format codes for timetz and datetimetz
!   .strftime needs a C implementation (yuck).
  
  - LaTeXize the docs.
--- 4,9 ----
  
  - The custom %z and %Z format codes for timetz and datetimetz
!   .strftime needs a C implementation (yuck).  More generally,
!   %z and %Z are plain busted in the C implementation.
  
  - LaTeXize the docs.
***************
*** 161,164 ****
--- 162,197 ----
  
  
+ strftime() Behavior
+ ===================
+ date, datetime, datetimetz, time, and timetz objects all support
+ a strftime(format) method, to create a string representing the time
+ under the control of an explicit format string.  Broadly speaking,
+     d.strftime(fmt)
+ acts like the time module's
+     time.strftime(fmt, d.timetuple())
+ although not all objects support a timetuple() method.
+ 
+ For time and timetz objects, format codes for year, month, and day
+ should not be used, as time objects have no such values.  0 is used
+ instead.
+ 
+ For date objects, format codes for hours, minutes, and seconds should
+ not be used, as date objects have no such values.  0 is used insted.
+ 
+ For a naive object, the %z and %Z format codes are replaced by
+ empty strings.
+ 
+ For an aware object:
+ 
+ - %z:  self.utcoffset() is transformed into a 5-character
+   string of the form +HHMM or -HHMM, where HH is a 2-digit string
+   giving the number of UTC offset hours, and MM is a 2-digit string
+   giving the number of UTC offset minutes.  For example, if
+   utcoffset() returns -180, %z is replaced with string "-0300".
+ 
+ - %Z:  If self.tzname() returns None, %Z is replaced by an empty string.
+   Else %Z is replaced by the returned value, which must be a string.
+ 
+ 
  class timedelta
  ===============
***************
*** 442,447 ****
      Return a string representing the date, controlled by an explicit
      format string.  Format codes referring to hours, minutes or seconds
!     will see 0 values.  d.strftime(f) is the same as
!     time.strftime(f, d.timetuple()).
  
  
--- 475,479 ----
      Return a string representing the date, controlled by an explicit
      format string.  Format codes referring to hours, minutes or seconds
!     will see 0 values.  See the section on strftime() behavior.
  
  
***************
*** 632,637 ****
    - strftime(format)
      Return a string representing the date and time, controlled by an
!     explicit format string.  d.strftime(f) is the same as
!     time.strftime(f, d.timetuple()).
  
  
--- 664,668 ----
    - strftime(format)
      Return a string representing the date and time, controlled by an
!     explicit format string.  See the section on strftime() behavior.
  
  
***************
*** 704,710 ****
    - strftime(format)
      Return a string representing the time, controlled by an explicit
!     format string.  Format codes for any fields other than hour, minute
!     and second should not be used, since a time object has meaningful
!     values only for those fields.
  
  
--- 735,739 ----
    - strftime(format)
      Return a string representing the time, controlled by an explicit
!     format string.  See the section on strftime() behavior.
  
  
***************
*** 900,916 ****
    - strftime(format)
      Return a string representing the time, controlled by an explicit
!     format string.  Format codes for any fields other than hour, minute,
!     second and time zone should not be used, since a timetz object has
!     meaningful values only for those fields.
!     Format code %Z:  If self.tzname() returns None, %Z is replaced by
!     an empty string.  Else %Z is replaced by the returned value, which
!     must be a string.
!     Format code %z:  This is an extension to the usual set of format
!     codes.  If self.uctoffset() returns None, %z is replaced by an empty
!     string.  Else the return value is transformed into a 5-character
!     string of the form +HHMM or -HHMM, and replaces %z, where HH is a
!     2-digit string giving the number of UTC offset hours, and MM is a
!     2-digit string giving the number of UTC offset minutes.
!     XXX Sheesh.  This needs an example.
  
    - utcoffset()
--- 929,933 ----
    - strftime(format)
      Return a string representing the time, controlled by an explicit
!     format string.  See the section on strftime() behavior.
  
    - utcoffset()
***************
*** 1054,1057 ****
--- 1071,1075 ----
      ctime()
      __str__()
+     strftime(format)
  
      These are the same as the datetime methods of the same names.
***************
*** 1111,1119 ****
  
      str(d) is equivalent to d.isoformat(' ').
- 
-   - strftime(format)
-     Return a string representing the date and time, controlled by an
-     explicit format string.  See timetz.strftime() for the treatment
-     of the %Z and %z format codes.
  
  
--- 1129,1132 ----

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.69
retrieving revision 1.70
diff -C2 -d -r1.69 -r1.70
*** test_datetime.py	15 Dec 2002 23:36:36 -0000	1.69
--- test_datetime.py	16 Dec 2002 00:25:34 -0000	1.70
***************
*** 13,16 ****
--- 13,18 ----
  import unittest
  
+ # XXX strftime is broken in the C implementation, when using %z or %Z.
+ 
  # Import the right implementation, under name "datetime".
  TESTING_C = 'c' in sys.argv
***************
*** 770,773 ****
--- 772,778 ----
          self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
  
+         # A naive object replaces %z and %Z w/ empty strings.
+         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
+ 
      def test_resolution_info(self):
          self.assert_(isinstance(self.theclass.min, self.theclass))
***************
*** 1415,1418 ****
--- 1420,1425 ----
          t = self.theclass(1, 2, 3, 4)
          self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
+         # A naive object replaces %z and %Z with empty strings.
+         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
  
      def test_str(self):
***************
*** 1565,1571 ****
          self.assertEqual(repr(t5), d + "timetz(0, 0, 0, 40, tzinfo=utc)")
  
-         # XXX %z and %Z don't work correctly in C yet.
-         if TESTING_C:
-             return
          self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
                                       "07:47:00 %Z=EST %z=-0500")
--- 1572,1575 ----