[Python-checkins] python/dist/src/Doc/lib libdatetime.tex,1.36,1.37 tzinfo-examples.py,1.6,1.7

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Tue, 21 Jan 2003 20:45:53 -0800


Update of /cvsroot/python/python/dist/src/Doc/lib
In directory sc8-pr-cvs1:/tmp/cvs-serv2341/python/Doc/lib

Modified Files:
	libdatetime.tex tzinfo-examples.py 
Log Message:
"Premature" doc changes, for new astimezone() rules, and the new
tzinfo.fromutc() method.  The C code doesn't implement any of this
yet (well, not the C code on the machine I'm using now), nor does
the test suite reflect it.  The Python datetime.py implementation and
test suite in the sandbox do match these doc changes.  The C
implementation probably won't catch up before Thursday (Wednesday is
a scheduled "black hole" day this week <0.4 wink>).


Index: libdatetime.tex
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/lib/libdatetime.tex,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** libdatetime.tex	21 Jan 2003 16:44:27 -0000	1.36
--- libdatetime.tex	22 Jan 2003 04:45:50 -0000	1.37
***************
*** 735,764 ****
  \begin{methoddesc}{astimezone}{tz}
    Return a \class{datetime} object with new \member{tzinfo} member
!   \var{tz}.
!   \var{tz} must be \code{None}, or an instance of a \class{tzinfo} subclass.
!   If \var{tz} is \code{None}, \var{self} is naive,
!   or \code{self.tzinfo}\ is \var{tz},
!   \code{self.astimezone(tz)} is equivalent to
!   \code{self.replace(tzinfo=tz)}:  a new time zone object is attached
!   without any conversion of date or time members.  Else \code{self.tzinfo}
!   and \var{tz} must implement the \method{utcoffset()} and \method{dst()}
!   \class{tzinfo} methods, and the date and time members are adjusted so
!   that the result is local time in time zone \var{tz}, representing the
!   same UTC time as \var{self}:  after \code{astz = dt.astimezone(tz)},
!   \code{astz - astz.utcoffset()} will usually have the same date and time
!   members as \code{dt - dt.utcoffset()}.  The discussion of class
!   \class{tzinfo} explains the cases at Daylight Saving Time
!   transition boundaries where this cannot be achieved (an issue only if
!   \var{tz} models both standard and daylight time).
  \end{methoddesc}
  
  \begin{methoddesc}{utcoffset}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{tzinfo.utcoffset(self)}.
  \end{methoddesc}
  
  \begin{methoddesc}{tzname}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{tzinfo.tzname(self)}.
  \end{methoddesc}
  
--- 735,803 ----
  \begin{methoddesc}{astimezone}{tz}
    Return a \class{datetime} object with new \member{tzinfo} member
!   \var{tz}, adjusting the date and time members so the result is the
!   same UTC time as \var{self}, but in \var{tz}'s local time.
! 
!   \var{tz} must be an instance of a \class{tzinfo} subclass, and its
!   \method{utcoffset()} and \method{dst()} methods must not return
!   \code{None}.  \var{self} must be aware (\code{\var{self}.tzinfo} must
!   not be \code{None}, and \code{\var{self}.utcoffset()} must not return
!   \code{None}).
! 
!   If code{\var{self}.tzinfo} is \var{tz},
!   \code{\var{self}.astimezone(\var{tz})} is equal to \var{self}:  no
!   adjustment of date or time members is performed.
!   Else the result is local time in time zone \var{tz}, representing the
!   same UTC time as \var{self}:  after \code{\var{astz} =
!   \var{dt}.astimezone(\var{tz})},
!   \code{\var{astz} - \var{astz}.utcoffset()} will usually have the same
!   date and time members as \code{\var{dt} - \var{dt}.utcoffset()}.
!   The discussion of class \class{tzinfo} explains the cases at Daylight
!   Saving Time transition boundaries where this cannot be achieved (an issue
!   only if \var{tz} models both standard and daylight time).
! 
!   If you merely want to attach a time zone object \var{tz} to a
!   datetime \var{dt} without adjustment of date and time members,
!   use \code{\var{dt}.replace(tzinfo=\var{tz})}.  If
!   you merely want to remove the time zone object from an aware datetime
!   \var{dt} without conversion of date and time members, use
!   \code{\var{dt}.replace(tzinfo=None)}.
! 
!   Note that the default \method{tzinfo.fromutc()} method can be overridden
!   in a \class{tzinfo} subclass to effect the result returned by
!   \method{astimezone()}.  Ignoring error cases, \method{astimezone()}
!   acts like:
! 
!   \begin{verbatim}
!   def astimezone(self, tz):
!       if self.tzinfo is tz:
!           return self
!       # Convert self to UTC, and attach the new time zone object.
!       utc = (self - self.utcoffset()).replace(tzinfo=tz)
!       # Convert from UTC to tz's local time.
!       return tz.fromutc(utc)
!   \end{verbatim}
  \end{methoddesc}
  
  \begin{methoddesc}{utcoffset}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.utcoffset(\var{self})}, and
!   raises an exception if the latter doesn't return \code{None}, or
!   a \class{timedelta} object representing a whole number of minutes
!   with magnitude less than one day.
! \end{methoddesc}
! 
! \begin{methoddesc}{dst}{}
!   If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.dst(\var{self})}, and
!   raises an exception if the latter doesn't return \code{None}, or
!   a \class{timedelta} object representing a whole number of minutes
!   with magnitude less than one day.
  \end{methoddesc}
  
  \begin{methoddesc}{tzname}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.tzname(\var{self})},
!   raises an exception if the latter doesn't return \code{None} or
!   a string object,
  \end{methoddesc}
  
***************
*** 990,1004 ****
  \begin{methoddesc}{utcoffset}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{tzinfo.utcoffset(self)}.
  \end{methoddesc}
  
  \begin{methoddesc}{dst}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{tzinfo.dst(self)}.
  \end{methoddesc}
  
  \begin{methoddesc}{tzname}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{tzinfo.tzname(self)}.
  \end{methoddesc}
  
--- 1029,1051 ----
  \begin{methoddesc}{utcoffset}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.utcoffset(None)}, and
!   raises an exception if the latter doesn't return \code{None} or
!   a \class{timedelta} object representing a whole number of minutes
!   with magnitude less than one day.
  \end{methoddesc}
  
  \begin{methoddesc}{dst}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.dst(None)}, and
!   raises an exception if the latter doesn't return \code{None}, or
!   a \class{timedelta} object representing a whole number of minutes
!   with magnitude less than one day.
  \end{methoddesc}
  
  \begin{methoddesc}{tzname}{}
    If \member{tzinfo} is \code{None}, returns \code{None}, else
!   returns \code{\var{self}.tzinfo.tzname(None)}, or
!   raises an exception if the latter doesn't return \code{None} or
!   a string object.
  \end{methoddesc}
  
***************
*** 1067,1071 ****
    member's \method{dst()} method to determine how the
    \member{tm_isdst} flag should be set, and
!   \method{datetime.astimezone()} calls \method{dst()} to account for
    DST changes when crossing time zones.
  
--- 1114,1118 ----
    member's \method{dst()} method to determine how the
    \member{tm_isdst} flag should be set, and
!   \method{tzinfo.fromutc()} calls \method{dst()} to account for
    DST changes when crossing time zones.
  
***************
*** 1073,1085 ****
    standard and daylight times must be consistent in this sense:
  
!       \code{tz.utcoffset(dt) - tz.dst(dt)}
  
    must return the same result for every \class{datetime} \var{dt}
!   with \code{dt.tzinfo==tz}  For sane \class{tzinfo} subclasses, this
!   expression yields the time zone's "standard offset", which should not
!   depend on the date or the time, but only on geographic location.  The
!   implementation of \method{datetime.astimezone()} relies on this, but
!   cannot detect violations; it's the programmer's responsibility to
!   ensure it.
  
    The default implementation of \method{dst()} raises
--- 1120,1153 ----
    standard and daylight times must be consistent in this sense:
  
!       \code{\var{tz}.utcoffset(\var{dt}) - \var{tz}.dst(\var{dt})}
  
    must return the same result for every \class{datetime} \var{dt}
!   with \code{\var{dt}.tzinfo==\var{tz}}  For sane \class{tzinfo}
!   subclasses, this expression yields the time zone's "standard offset",
!   which should not depend on the date or the time, but only on geographic
!   location.  The implementation of \method{datetime.astimezone()} relies
!   on this, but cannot detect violations; it's the programmer's
!   responsibility to ensure it.  If a \class{tzinfo} subclass cannot
!   guarantee this, it may be able to override the default implementation
!   of \method{tzinfo.fromutc()} to work correctly with \method{astimezone()}
!   regardless.
! 
!   Most implementations of \method{dst()} will probably look like one
!   of these two:
! 
! \begin{verbatim}
!     return timedelta(0)   # a fixed-offset class:  doesn't account for DST
! 
!   or
! 
!     # Code to set dston and dstoff to the time zone's DST transition
!     # times based on the input dt.year, and expressed in standard local
!     # time.  Then
! 
!     if dston <= dt.replace(tzinfo=None) < dstoff:
!         return timedelta(hours=1)
!     else:
!         return timedelta(0)
! \end{verbatim}
  
    The default implementation of \method{dst()} raises
***************
*** 1088,1101 ****
  
  \begin{methoddesc}{tzname}{self, dt}
!   Return the timezone name corresponding to the \class{datetime}
!   object represented
!   by \var{dt}, as a string.  Nothing about string names is defined by the
!   \module{datetime} module, and there's no requirement that it mean anything
!   in particular.  For example, "GMT", "UTC", "-500", "-5:00", "EDT",
!   "US/Eastern", "America/New York" are all valid replies.  Return
    \code{None} if a string name isn't known.  Note that this is a method
!   rather than a fixed string primarily because some \class{tzinfo} objects
!   will wish to return different names depending on the specific value
!   of \var{dt} passed, especially if the \class{tzinfo} class is
    accounting for daylight time.
  
--- 1156,1169 ----
  
  \begin{methoddesc}{tzname}{self, dt}
!   Return the time zone name corresponding to the \class{datetime}
!   object \var{dt}, as a string.
!   Nothing about string names is defined by the
!   \module{datetime} module, and there's no requirement that it mean
!   anything in particular.  For example, "GMT", "UTC", "-500", "-5:00",
!   "EDT", "US/Eastern", "America/New York" are all valid replies.  Return
    \code{None} if a string name isn't known.  Note that this is a method
!   rather than a fixed string primarily because some \class{tzinfo}
!   subclasses will wish to return different names depending on the specific
!   value of \var{dt} passed, especially if the \class{tzinfo} class is
    accounting for daylight time.
  
***************
*** 1114,1118 ****
  best response.  For example, returning \code{None} is appropriate if the
  class wishes to say that time objects don't participate in the
! \class{tzinfo} protocol.  It may be more useful for \code{utcoffset(None)}
  to return the standard UTC offset, as there is no other convention for
  discovering the standard offset.
--- 1182,1186 ----
  best response.  For example, returning \code{None} is appropriate if the
  class wishes to say that time objects don't participate in the
! \class{tzinfo} protocols.  It may be more useful for \code{utcoffset(None)}
  to return the standard UTC offset, as there is no other convention for
  discovering the standard offset.
***************
*** 1125,1128 ****
--- 1193,1240 ----
  and not need worry about objects in other timezones.
  
+ There is one more \class{tzinfo} method that a subclass may wish to
+ override:
+ 
+ \begin{methoddesc}{fromutc}{self, dt}
+   This is called from the default \class{datetime.astimezone()}
+   implementation.  When called from that, \code{\var{dt}.tzinfo} is
+   \var{self}, and \var{dt}'s date and time members are to be viewed as
+   expressing a UTC time.  The purpose of \method{fromutc()} is to
+   adjust the date and time members, returning an equivalent datetime in
+   \var{self}'s local time.
+ 
+   Most \class{tzinfo} subclasses should be able to inherit the default
+   \method{fromutc()} implementation without problems.  It's strong enough
+   to handle fixed-offset time zones, and time zones accounting for both
+   standard and daylight time, and the latter even if the DST transition
+   times differ in different years.  An example of a time zone the default
+   \method{fromutc()} implementation may not handle correctly in all cases
+   is one where the standard offset (from UTC) depends on the specific date
+   and time passed, which can happen for political reasons.
+   The default implementations of \method{astimezone()} and
+   \method{fromutc()} may not produce the result you want if the result is
+   one of the hours straddling the moment the standard offset changes.
+ 
+   Skipping code for error cases, the default \method{fromutc()}
+   implementation acts like:
+ 
+   \begin{verbatim}
+   def fromutc(self, dt):
+       # raise ValueError error if dt.tzinfo is not self
+       dtoff = dt.utcoffset()
+       dtdst = dt.dst()
+       # raise ValueError if dtoff is None or dtdst is None
+       delta = dtoff - dtdst  # this is self's standard offset
+       if delta:
+           dt += delta   # convert to standard local time
+           dtdst = dt.dst()
+           # raise ValueError if dtdst is None
+       if dtdst:
+           return dt + dtdst
+       else:
+           return dt
+   \end{verbatim}
+ \end{methoddesc}
+ 
  Example \class{tzinfo} classes:
  
***************
*** 1151,1155 ****
  \code{hour==2} on the
  day DST begins.  In order for \method{astimezone()} to make this
! guarantee, the \class{tzinfo} \method{dst()} method must consider times
  in the "missing hour" (2:MM for Eastern) to be in daylight time.
  
--- 1263,1267 ----
  \code{hour==2} on the
  day DST begins.  In order for \method{astimezone()} to make this
! guarantee, the \method{rzinfo.dst()} method must consider times
  in the "missing hour" (2:MM for Eastern) to be in daylight time.
  
***************
*** 1163,1168 ****
  same local hour then.  In the Eastern example, UTC times of the form
  5:MM and 6:MM both map to 1:MM when converted to Eastern.  In order for
! \method{astimezone()} to make this guarantee, the \class{tzinfo}
! \method{dst()} method must consider times in the "repeated hour" to be in
  standard time.  This is easily arranged, as in the example, by expressing
  DST switch times in the time zone's standard local time.
--- 1275,1280 ----
  same local hour then.  In the Eastern example, UTC times of the form
  5:MM and 6:MM both map to 1:MM when converted to Eastern.  In order for
! \method{astimezone()} to make this guarantee, the \method{tzinfo.dst()}
! method must consider times in the "repeated hour" to be in
  standard time.  This is easily arranged, as in the example, by expressing
  DST switch times in the time zone's standard local time.
***************
*** 1236,1242 ****
      PyDateTime_Date
      PyDateTime_DateTime
-     PyDateTime_DateTimeTZ
      PyDateTime_Time
-     PyDateTime_TimeTZ
      PyDateTime_Delta
      PyDateTime_TZInfo
--- 1348,1352 ----
***************
*** 1250,1262 ****
      PyDateTime_CheckExact(op)
  
-     PyDateTimeTZ_Check(op)
-     PyDateTimeTZ_CheckExact(op)
- 
      PyTime_Check(op)
      PyTime_CheckExact(op)
  
-     PyTimeTZ_Check(op)
-     PyTimeTZ_CheckExact(op)
- 
      PyDelta_Check(op)
      PyDelta_CheckExact(op)
--- 1360,1366 ----
***************
*** 1270,1279 ****
  return ints:
  
!     For \class{date}, \class{datetime}, and \class{datetimetz} instances:
          PyDateTime_GET_YEAR(o)
          PyDateTime_GET_MONTH(o)
          PyDateTime_GET_DAY(o)
  
!     For \class{datetime} and \class{datetimetz} instances:
          PyDateTime_DATE_GET_HOUR(o)
          PyDateTime_DATE_GET_MINUTE(o)
--- 1374,1383 ----
  return ints:
  
!     For \class{date} and \class{datetime} instances:
          PyDateTime_GET_YEAR(o)
          PyDateTime_GET_MONTH(o)
          PyDateTime_GET_DAY(o)
  
!     For \class{datetime} instances:
          PyDateTime_DATE_GET_HOUR(o)
          PyDateTime_DATE_GET_MINUTE(o)
***************
*** 1281,1285 ****
          PyDateTime_DATE_GET_MICROSECOND(o)
  
!     For \class{time} and \class{timetz} instances:
          PyDateTime_TIME_GET_HOUR(o)
          PyDateTime_TIME_GET_MINUTE(o)
--- 1385,1389 ----
          PyDateTime_DATE_GET_MICROSECOND(o)
  
!     For \class{time} instances:
          PyDateTime_TIME_GET_HOUR(o)
          PyDateTime_TIME_GET_MINUTE(o)

Index: tzinfo-examples.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/lib/tzinfo-examples.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** tzinfo-examples.py	20 Jan 2003 22:54:38 -0000	1.6
--- tzinfo-examples.py	22 Jan 2003 04:45:50 -0000	1.7
***************
*** 28,32 ****
  
      def __init__(self, offset, name):
!         self.__offset = timdelta(minutes = offset)
          self.__name = name
  
--- 28,32 ----
  
      def __init__(self, offset, name):
!         self.__offset = timedelta(minutes = offset)
          self.__name = name
  
***************
*** 117,123 ****
          if dt is None or dt.tzinfo is None:
              # An exception may be sensible here, in one or both cases.
!             # It depends on how you want to treat them.  The astimezone()
!             # implementation always passes a datetime with
!             # dt.tzinfo == self.
              return ZERO
          assert dt.tzinfo is self
--- 117,123 ----
          if dt is None or dt.tzinfo is None:
              # An exception may be sensible here, in one or both cases.
!             # It depends on how you want to treat them.  The default
!             # fromutc() implementation (called by the default astimezone()
!             # implementation) passes a datetime with dt.tzinfo is self.
              return ZERO
          assert dt.tzinfo is self