[Zope3-checkins] CVS: Zope3/lib/python/datetime - _datetime.py:1.7 test_datetime.py:1.7 __init__.py:1.3

Viktorija Zaksiene ryzaja@codeworks.lt
Mon, 2 Dec 2002 04:56:12 -0500


Update of /cvs-repository/Zope3/lib/python/datetime
In directory cvs.zope.org:/tmp/cvs-serv6358/datetime

Modified Files:
	_datetime.py test_datetime.py __init__.py 
Log Message:
Synchronized with Python 2.3 datetime.


=== Zope3/lib/python/datetime/_datetime.py 1.6 => 1.7 ===
--- Zope3/lib/python/datetime/_datetime.py:1.6	Thu Nov 21 05:42:35 2002
+++ Zope3/lib/python/datetime/_datetime.py	Mon Dec  2 04:56:11 2002
@@ -693,6 +693,310 @@
 date.resolution = timedelta(days=1)
 
 
+class time(object):
+    """Concrete time type.
+
+    Constructors:
+
+    __init__()
+
+    Operators:
+
+    __repr__, __str__
+    __cmp__, __hash__
+
+    Methods:
+
+    strftime()
+    isoformat()
+
+    Properties (readonly):
+    hour, minute, second, microsecond
+    """
+
+    def __init__(self, hour, minute, second=0, microsecond=0):
+        """Constructor.
+
+        Arguments:
+
+        hour, minute (required)
+        second, microsecond (default to zero)
+        """
+        if not 0 <= hour <= 23:
+            raise ValueError('hour must be in 0..23', hour)
+        if not 0 <= minute <= 59:
+            raise ValueError('minute must be in 0..59', minute)
+        if not 0 <= second <= 59:
+            raise ValueError('second must be in 0..59', second)
+        if not 0 <= microsecond <= 999999:
+            raise ValueError('microsecond must be in 0..999999', microsecond)
+        self.__hour = hour
+        self.__minute = minute
+        self.__second = second
+        self.__microsecond = microsecond
+
+    # Read-only field accessors
+    hour = property(lambda self: self.__hour, doc="hour (0-23)")
+    minute = property(lambda self: self.__minute, doc="minute (0-59)")
+    second = property(lambda self: self.__second, doc="second (0-59)")
+    microsecond = property(lambda self: self.__microsecond,
+                           doc="microsecond (0-999999)")
+
+    # Standard conversions, __cmp__, __hash__ (and helpers)
+
+    def __cmp__(self, other):
+        """Three-way comparison."""
+        if isinstance(other, time):
+            return cmp((self.__hour, self.__minute, self.__second,
+                        self.__microsecond),
+                       (other.__hour, other.__minute, other.__second,
+                        other.__microsecond))
+        raise TypeError, ("can't compare time to %s instance" %
+                          type(other).__name__)
+
+    def __hash__(self):
+        """Hash."""
+        return hash((self.__hour, self.__minute, self.__second,
+                     self.__microsecond))
+
+    # Conversions to string
+
+    def __repr__(self):
+        """Convert to formal string, for repr()."""
+        if self.__microsecond != 0:
+            s = ", %d, %d" % (self.__second, self.__microsecond)
+        elif self.__second != 0:
+            s = ", %d" % self.__second
+        else:
+            s = ""
+        return "%s(%d, %d%s)" % (self.__class__.__name__,
+                                 self.__hour, self.__minute, s)
+
+    def __str__(self):
+        """Convert to pretty string, for str()."""
+        pretty = "%d:%02d:%02d.%06d" % (
+            self.__hour, self.__minute, self.__second,
+            self.__microsecond)
+        # trim microseconds: hh:mm:ss.xxx000 -> hh:mm:ss.xxx
+        while pretty.endswith('0'):
+            pretty = pretty[:-1]
+        # trim microseconds: hh:mm:ss.000000 -> hh:mm:ss
+        if pretty.endswith('.'):
+            pretty = pretty[:-1]
+        # trim seconds: hh:mm:00 -> hh:mm
+        if pretty.endswith(':00'):
+            pretty = pretty[:-3]
+        return pretty
+
+    def isoformat(self):
+        """Return the time formatted according to ISO.
+
+        This is 'HH:MM:SS.mmmmmm'.
+        """
+        return "%02d:%02d:%02d.%06d" % (
+            self.__hour, self.__minute, self.__second,
+            self.__microsecond)
+
+    def strftime(self, fmt):
+        """Format using strftime().  The date part of the timestamp passed
+        to underlying strftime should not be used.
+        """
+        return _time.strftime(fmt, (0, 0, 0, self.__hour, self.__minute,
+                                    self.__second, 0, 0, -1))
+
+
+time.min = time(0, 0, 0)
+time.max = time(23, 59, 59, 999999)
+time.resolution = timedelta(microseconds=1)
+
+
+class timetz(time):
+    """Time with time zone.
+
+    Constructors:
+
+    __init__()
+
+    Operators:
+
+    __repr__, __str__
+    __cmp__, __hash__
+
+    Methods:
+
+    strftime()
+    isoformat()
+    utcoffset()
+    tzname()
+    dst()
+
+    Properties (readonly):
+    hour, minute, second, microsecond, tzinfo
+    """
+
+    def __init__(self, hour, minute, second=0, microsecond=0, tzinfo=None):
+        """Constructor.
+
+        Arguments:
+
+        hour, minute (required)
+        second, microsecond (default to zero)
+        tzinfo (default to None)
+        """
+        super(timetz, self).__init__(hour, minute, second, microsecond)
+        if tzinfo is not None:
+            # Better fail now than later
+            assert hasattr(tzinfo, 'utcoffset')
+            assert hasattr(tzinfo, 'dst')
+            assert hasattr(tzinfo, 'tzname')
+        self.__tzinfo = tzinfo
+
+    # Read-only field accessors
+    tzinfo = property(lambda self: self.__tzinfo, doc="timezone info object")
+
+    # Standard conversions, __cmp__, __hash__ (and helpers)
+
+    def __cmp__(self, other):
+        """Three-way comparison."""
+        if not isinstance(other, time):
+            raise TypeError("can't compare timetz to %s instance" %
+                            type(other).__name__)
+        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 = otoff = None
+        if mytz is not None:
+            myoff = mytz.utcoffset(self)
+        if ottz is not None:
+            otoff = ottz.utcoffset(other)
+        if myoff == otoff:
+            return supercmp(other)
+        if myoff is None or otoff is None:
+            raise ValueError, "cannot mix naive and timezone-aware time"
+        myhhmm = self.hour * 60 + self.minute - myoff
+        othhmm = other.hour * 60 + other.minute - otoff
+        return cmp((myhhmm, self.second, self.microsecond),
+                   (othhmm, other.second, other.microsecond))
+
+    def __hash__(self):
+        """Hash."""
+        tz = self.__tzinfo
+        if tz == None:
+            return super(timetz, self).__hash__()
+        tzoff = tz.utcoffset(self)
+        if not tzoff: # zero or None!
+            return super(timetz, self).__hash__()
+        h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
+        # Unfortunately it is not possible to construct a new timetz object
+        # and use super().__hash__(), since hour may exceed the range of
+        # allowed values
+        return hash((h, m, self.second, self.microsecond))
+
+    # Conversion to string
+
+    def _tzstr(self, sep=":"):
+        """Return formatted timezone offset (+xx:xx) or None."""
+        if self.__tzinfo is not None:
+            off = self.__tzinfo.utcoffset(self)
+            if off is not None:
+                if off < 0:
+                    sign = "-"
+                    off = -off
+                else:
+                    sign = "+"
+                hh, mm = divmod(off, 60)
+                return "%s%02d%s%02d" % (sign, hh, sep, mm)
+
+    def __repr__(self):
+        """Convert to formal string, for repr()."""
+        s = super(timetz, self).__repr__()
+        if self.__tzinfo is not None:
+            assert s[-1:] == ")"
+            s = s[:-1] + ", tzinfo=%r" % self.__tzinfo + ")"
+        return s
+
+    def __str__(self):
+        """Convert to pretty string, for str()."""
+        s = super(timetz, self).__str__()
+        tz = self._tzstr()
+        if tz: s = "%s %s" % (s, tz)
+        return s
+
+    def isoformat(self):
+        """Return the time formatted according to ISO.
+
+        This is 'HH:MM:SS.mmmmmm+zz:zz'.
+        """
+        s = super(timetz, self).isoformat()
+        tz = self._tzstr()
+        if tz: s += tz
+        return s
+
+    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).
+        """
+        tz = self._tzstr(sep="")
+        # FIXME: this will break %%z/%%Z!
+        if tz:
+            fmt = fmt.replace("%z", tz).replace("%Z", self.tzinfo.tzname(None))
+        else:
+            fmt = fmt.replace("%z", "").replace("%Z", "")
+        return super(timetz, self).strftime(fmt)
+
+    # Timezone functions
+
+    def utcoffset(self):
+        """Return the timezone offset in minutes east of UTC (negative west of
+        UTC)."""
+        tz = self.__tzinfo
+        if tz is None:
+            return None
+        else:
+            return tz.utcoffset(self)
+
+    def tzname(self):
+        """Return the timezone name.
+
+        Note that the name is 100% informational -- 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.
+        """
+        tz = self.__tzinfo
+        if tz is None:
+            return None
+        else:
+            return tz.tzname(self)
+
+    def dst(self):
+        """Return 0 if DST is not in effect, or the DST offset (in minutes
+        eastward) if DST is in effect.
+
+        This is purely informational; the DST offset has already been added to
+        the UTC offset returned by utcoffset() if applicable, so there's no
+        need to consult dst() unless you're interested in displaying the DST
+        info.
+        """
+        tz = self.__tzinfo
+        if tz is None:
+            return None
+        else:
+            return tz.dst(self)
+
+
+timetz.min = timetz(0, 0, 0)
+timetz.max = timetz(23, 59, 59, 999999)
+timetz.resolution = timedelta(microseconds=1)
+
+
 class datetime(date):
     """Concrete date/time type, inheriting from date.
 
@@ -777,6 +1081,12 @@
         return cls.utcfromtimestamp(t)
     utcnow = classmethod(utcnow)
 
+    def combine(cls, date, time):
+        "Construct a datetime from a given date and a given time."
+        return cls(date.year, date.month, date.day,
+                   time.hour, time.minute, time.second, time.microsecond)
+    combine = classmethod(combine)
+
     # Conversions to string
 
     def __repr__(self):
@@ -807,6 +1117,15 @@
                 self.__hour, self.__minute, self.__second,
                 self.weekday(), self._yday(), -1)
 
+    def date(self):
+        "Return the date part."
+        return date(self.__year, self.__month, self.__day)
+
+    def time(self):
+        "Return the time part."
+        return time(self.__hour, self.__minute, self.__second,
+                    self.__microsecond)
+
     def __cmp__(self, other):
         "Three-way comparison."
         if isinstance(other, datetime):
@@ -941,6 +1260,14 @@
         return cls.fromtimestamp(t, tzinfo)
     now = classmethod(now)
 
+    def combine(cls, date, time):
+        "Construct a datetime from a given date and a given time."
+        return cls(date.year, date.month, date.day,
+                   time.hour, time.minute, time.second, time.microsecond,
+                   getattr(time, 'tzinfo', None))
+    combine = classmethod(combine)
+
+
     def utctimetuple(self):
         "Return UTC time tuple compatible with time.gmtime()."
         offset = self.utcoffset()
@@ -951,6 +1278,11 @@
                       self.microsecond)
         dt = timedelta(minutes=offset)
         return (ts - dt).timetuple()
+
+    def timetz(self):
+        "Return the time part."
+        return timetz(self.hour, self.minute, self.second, self.microsecond,
+                      self.__tzinfo)
 
     def isoformat(self, sep=' '):
         s = super(datetimetz, self).isoformat(sep)


=== Zope3/lib/python/datetime/test_datetime.py 1.6 => 1.7 === (571/671 lines abridged)
--- Zope3/lib/python/datetime/test_datetime.py:1.6	Thu Nov 21 05:42:35 2002
+++ Zope3/lib/python/datetime/test_datetime.py	Mon Dec  2 04:56:11 2002
@@ -6,98 +6,18 @@
 import sys
 import unittest
 
-from datetime import date, datetime, datetimetz, timedelta
-from datetime._datetime import MINYEAR, MAXYEAR
-
-
-class TestTimeDelta(unittest.TestCase):
-
-    def test_timedelta(self):
-        a = timedelta(7) # One week
-        b = timedelta(0, 60) # One minute
-        c = timedelta(0, 0, 1000) # One millisecond
-        self.assertEqual(a+b+c, timedelta(7, 60, 1000))
-        self.assertEqual(a-b, timedelta(6, 24*3600 - 60))
-        self.assertEqual(-a, timedelta(-7))
-        self.assertEqual(+a, timedelta(7))
-        self.assertEqual(-b, timedelta(-1, 24*3600 - 60))
-        self.assertEqual(-c, timedelta(-1, 24*3600 - 1, 999000))
-        self.assertEqual(abs(a), a)
-        self.assertEqual(abs(-a), a)
-        self.assertEqual(timedelta(6, 24*3600), a)
-        self.assertEqual(timedelta(0, 0, 60*1000000), b)
-        self.assertEqual(a*10, timedelta(70))
-        self.assertEqual(a*10, 10*a)
-        self.assertEqual(a*10L, 10*a)
-        self.assertEqual(b*10, timedelta(0, 600))
-        self.assertEqual(10*b, timedelta(0, 600))
-        self.assertEqual(b*10L, timedelta(0, 600))
-        self.assertEqual(c*10, timedelta(0, 0, 10000))
-        self.assertEqual(10*c, timedelta(0, 0, 10000))
-        self.assertEqual(c*10L, timedelta(0, 0, 10000))
-        self.assertEqual(a*-1, -a)
-        self.assertEqual(b*-2, -b-b)
-        self.assertEqual(c*-2, -c+-c)
-        self.assertEqual(b*(60*24), (b*60)*24)
-        self.assertEqual(b*(60*24), (60*b)*24)
-        self.assertEqual(c*1000, timedelta(0, 1))
-        self.assertEqual(1000*c, timedelta(0, 1))
-        self.assertEqual(a//7, timedelta(1))
-        self.assertEqual(b//10, timedelta(0, 6))
-        self.assertEqual(c//1000, timedelta(0, 0, 1))
-        self.assertEqual(a//10, timedelta(0, 7*24*360))
-        self.assertEqual(a//3600000, timedelta(0, 0, 7*24*1000))
-        # Add/sub ints, longs, floats should be illegal
-        for i in 1, 1L, 1.0:
-            self.assertRaises(TypeError, lambda: a+i)

[-=- -=- -=- 571 lines omitted -=- -=- -=-]

-    def test_ctime(self):
-        t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
-        self.assertEqual(t.ctime(), "Sat Mar  2 18:03:05 2002")
+    def test_combine(self):
+        d = date(2002, 3, 4)
+        t = time(18, 45, 3, 1234)
+        dt = datetime.combine(d, t)
+        self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234))
+
+    def test_extract(self):
+        dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
+        self.assertEqual(dt.date(), date(2002, 3, 4))
+        self.assertEqual(dt.time(), time(18, 45, 3, 1234))
 
 
 class FixedOffset(object):
@@ -582,13 +290,29 @@
         self.assertEqual(repr(t3),
                          "datetimetz(2002, 3, 19, 13, 47, tzinfo=met)")
 
+    def test_combine(self):
+        met = FixedOffset(60, "MET")
+        d = date(2002, 3, 4)
+        tz = timetz(18, 45, 3, 1234, tzinfo=met)
+        dt = datetimetz.combine(d, tz)
+        self.assertEqual(dt, datetimetz(2002, 3, 4, 18, 45, 3, 1234,
+                                        tzinfo=met))
+
+    def test_extract(self):
+        met = FixedOffset(60, "MET")
+        dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
+        self.assertEqual(dt.date(), date(2002, 3, 4))
+        self.assertEqual(dt.time(), time(18, 45, 3, 1234))
+        self.assertEqual(dt.timetz(), timetz(18, 45, 3, 1234, tzinfo=met))
+
 
 def test_suite():
-    s1 = unittest.makeSuite(TestTimeDelta, 'test')
     s2 = unittest.makeSuite(TestDate, 'test')
-    s3 = unittest.makeSuite(TestDateTime, 'test')
-    s4 = unittest.makeSuite(TestDateTimeTZ, 'test')
-    return unittest.TestSuite([s1, s2, s3, s4])
+    s3 = unittest.makeSuite(TestTime, 'test')
+    s4 = unittest.makeSuite(TestTimeTZ, 'test')
+    s5 = unittest.makeSuite(TestDateTime, 'test')
+    s6 = unittest.makeSuite(TestDateTimeTZ, 'test')
+    return unittest.TestSuite([s2, s3, s4, s5, s6])
 
 def test_main():
     r = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)


=== Zope3/lib/python/datetime/__init__.py 1.2 => 1.3 ===
--- Zope3/lib/python/datetime/__init__.py:1.2	Fri Oct  4 14:05:19 2002
+++ Zope3/lib/python/datetime/__init__.py	Mon Dec  2 04:56:11 2002
@@ -5,4 +5,4 @@
 # deal with in CVS for now.  This __init__ file makes the package look
 # like the eventual module.
 
-from _datetime import timedelta, date, datetime, datetimetz
+from _datetime import timedelta, date, time, timetz, datetime, datetimetz