[Zope-Checkins] SVN: Zope/trunk/lib/python/DateTime/ The DateTime function may now be invoked with a single argument

Laurence Rowe l at lrowe.co.uk
Thu Oct 18 04:41:39 EDT 2007


Log message for revision 80912:
  The DateTime function may now be invoked with a single argument
  that is a datetime.datetime instance. Timezone naive DateTimes may
  be converted back to timezone naive datetime.datetime objects with
  asdatetime(). All DateTime instances may be converted to a timezone
  naive datetime.datetime in UTC with utcdatetime().
  
  

Changed:
  U   Zope/trunk/lib/python/DateTime/DateTime.py
  U   Zope/trunk/lib/python/DateTime/tests/testDateTime.py

-=-
Modified: Zope/trunk/lib/python/DateTime/DateTime.py
===================================================================
--- Zope/trunk/lib/python/DateTime/DateTime.py	2007-10-18 01:22:24 UTC (rev 80911)
+++ Zope/trunk/lib/python/DateTime/DateTime.py	2007-10-18 08:41:38 UTC (rev 80912)
@@ -49,11 +49,11 @@
     tzname=('UNKNOWN','UNKNOWN')
 
 # To control rounding errors, we round system time to the nearest
-# millisecond.  Then delicate calculations can rely on that the
+# microsecond.  Then delicate calculations can rely on that the
 # maximum precision that needs to be preserved is known.
 _system_time = time
 def time():
-    return round(_system_time(), 3)
+    return round(_system_time(), 6)
 
 # Determine machine epoch
 tm=((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334),
@@ -97,7 +97,7 @@
     )?                              # after minute is optional
    )?                               # after hour is optional
    (?:                              # timezone:
-    Z                               #  one Z
+    (?P<Z>Z)                        #  one Z
    |                                # or:
     (?P<signal>[-+])                #  one plus or one minus as signal
     (?P<hour_off>\d                 #  one digit for hour offset...
@@ -373,10 +373,10 @@
     x_adjusted = x - fset + ms
     d = x_adjusted / 86400.0
     t = x_adjusted - long(EPOCH) + 86400L
-    millis = (x + 86400 - fset) * 1000 + \
-             long(round(ms * 1000.0)) - long(EPOCH * 1000.0)
+    micros = (x + 86400 - fset) * 1000000 + \
+             long(round(ms * 1000000.0)) - long(EPOCH * 1000000.0)
     s = d - math.floor(d)
-    return s,d,t,millis
+    return s,d,t,micros
 
 def _calcHMS(x, ms):
     # hours, minutes, seconds from integer and float.
@@ -527,7 +527,8 @@
         local machine timezone). DateTime objects also provide access
         to their value in a float format usable with the python time
         module, provided that the value of the object falls in the
-        range of the epoch-based time module.
+        range of the epoch-based time module, and as a datetime.datetime
+        object.
 
         A DateTime object should be considered immutable; all conversion
         and numeric operations return a new DateTime object rather than
@@ -665,6 +666,13 @@
           - If the DateTime function is invoked with a single argument
             that is a DateTime instane, a copy of the passed object will
             be created.
+            
+          - New in 2.11:
+            The DateTime function may now be invoked with a single argument
+            that is a datetime.datetime instance. Timezone naive DateTimes may
+            be converted back to timezone naive datetime.datetime objects with
+            asdatetime(). All DateTime instances may be converted to a timezone
+            naive datetime.datetime in UTC with utcdatetime().
 
           - If the function is invoked with two numeric arguments, then
             the first is taken to be an integer year and the second
@@ -738,14 +746,23 @@
         datefmt = kw.get('datefmt', getDefaultDateFormat())
         d=t=s=None
         ac=len(args)
-        millisecs = None
+        microsecs = None
 
         if ac==10:
             # Internal format called only by DateTime
             yr,mo,dy,hr,mn,sc,tz,t,d,s=args
         elif ac == 11:
-            # Internal format that includes milliseconds.
+            # Internal format that includes milliseconds (from the epoch)
             yr,mo,dy,hr,mn,sc,tz,t,d,s,millisecs=args
+            microsecs = millisecs * 1000
+        
+        elif ac == 12:
+            # Internal format that includes microseconds (from the epoch) and a
+            # flag indicating whether this was constructed in a timezone naive
+            # manner
+            yr,mo,dy,hr,mn,sc,tz,t,d,s,microsecs,tznaive=args
+            if tznaive is not None: # preserve this information
+                self._timezone_naive = tznaive
 
         elif not args or (ac and args[0]==None):
             # Current time, to be displayed in local timezone
@@ -756,7 +773,8 @@
             s,d = _calcSD(t)
             yr,mo,dy,hr,mn,sc=lt[:6]
             sc=sc+ms
-
+            self._timezone_naive = False
+        
         elif ac==1:
             arg=args[0]
 
@@ -774,7 +792,23 @@
                 s,d = _calcSD(t)
                 yr,mo,dy,hr,mn,sc=lt[:6]
                 sc=sc+ms
+            
+            elif isinstance(arg, datetime):
+                yr,mo,dy,hr,mn,sc,tz,tznaive=self._parse_iso8601_preserving_tznaive(arg.isoformat())
+                self._timezone_naive = tznaive
+                ms = sc - math.floor(sc)
+                x = _calcDependentSecond2(yr,mo,dy,hr,mn,sc)
 
+                if tz:
+                    try: tz=self._tzinfo._zmap[tz.lower()]
+                    except KeyError:
+                        if numericTimeZoneMatch(tz) is None:
+                            raise DateTimeError, \
+                                  'Unknown time zone in date: %s' % arg
+                else:
+                    tz = self._calcTimezoneName(x, ms)
+                s,d,t,microsecs = _calcIndependentSecondEtc(tz, x, ms)
+
             elif isinstance(arg, (unicode, str)) and arg.lower() in self._tzinfo._zidx:
                 # Current time, to be displayed in specified timezone
                 t,tz=time(),self._tzinfo._zmap[arg.lower()]
@@ -783,6 +817,7 @@
                 s,d = _calcSD(t)
                 x = _calcDependentSecond(tz, t)
                 yr,mo,dy,hr,mn,sc = _calcYMDHMS(x, ms)
+                
 
             elif isinstance(arg, (unicode, str)):
                 # Date/time string
@@ -790,7 +825,8 @@
                 iso8601 = iso8601Match(arg.strip())
                 fields_iso8601 = iso8601 and iso8601.groupdict() or {}
                 if fields_iso8601 and not fields_iso8601.get('garbage'):
-                    yr,mo,dy,hr,mn,sc,tz=self._parse_iso8601(arg)
+                    yr,mo,dy,hr,mn,sc,tz,tznaive=self._parse_iso8601_preserving_tznaive(arg)
+                    self._timezone_naive = tznaive
                 else:
                     yr,mo,dy,hr,mn,sc,tz=self._parse(arg, datefmt)
 
@@ -809,7 +845,7 @@
                                   'Unknown time zone in date: %s' % arg
                 else:
                     tz = self._calcTimezoneName(x, ms)
-                s,d,t,millisecs = _calcIndependentSecondEtc(tz, x, ms)
+                s,d,t,microsecs = _calcIndependentSecondEtc(tz, x, ms)
 
             else:
                 # Seconds from epoch, gmt
@@ -844,7 +880,7 @@
                 ms = x_float - x_floor
                 x = long(x_floor)
                 yr,mo,dy,hr,mn,sc = _calcYMDHMS(x, ms)
-                s,d,t,millisecs = _calcIndependentSecondEtc(tz, x, ms)
+                s,d,t,microsecs = _calcIndependentSecondEtc(tz, x, ms)
         else:
             # Explicit format
             yr,mo,dy=args[:3]
@@ -878,7 +914,7 @@
             else:
                 # Get local time zone name
                 tz = self._calcTimezoneName(x, ms)
-            s,d,t,millisecs = _calcIndependentSecondEtc(tz, x, ms)
+            s,d,t,microsecs = _calcIndependentSecondEtc(tz, x, ms)
 
         if hr>12:
             self._pmhour=hr-12
@@ -891,24 +927,25 @@
             self._months[mo],self._months_a[mo],self._months_p[mo]
         self._fday,self._aday,self._pday= \
             self._days[dx],self._days_a[dx],self._days_p[dx]
-        # Round to nearest millisecond in platform-independent way.  You
+        # Round to nearest microsecond in platform-independent way.  You
         # cannot rely on C sprintf (Python '%') formatting to round
         # consistently; doing it ourselves ensures that all but truly
         # horrid C sprintf implementations will yield the same result
-        # x-platform, provided the format asks for exactly 3 digits after
+        # x-platform, provided the format asks for exactly 6 digits after
         # the decimal point.
-        sc = round(sc, 3)
-        if sc >= 60.0:  # can happen if, e.g., orig sc was 59.9999
-            sc = 59.999
+        sc = round(sc, 6)
+        if sc >= 60.0:  # can happen if, e.g., orig sc was 59.9999999
+            sc = 59.999999
         self._nearsec=math.floor(sc)
         self._year,self._month,self._day     =yr,mo,dy
         self._hour,self._minute,self._second =hr,mn,sc
         self.time,self._d,self._t,self._tz   =s,d,t,tz
-        if millisecs is None:
-            millisecs = long(math.floor(t * 1000.0))
-        self._millis = millisecs
-        # self._millis is the time since the epoch
-        # in long integer milliseconds.
+        if microsecs is None:
+            microsecs = long(math.floor(t * 1000000.0))
+        self._micros = microsecs
+        # self._micros is the time since the epoch
+        # in long integer microseconds.
+        
 
     int_pattern  =re.compile(r'([0-9]+)') #AJ
     flt_pattern  =re.compile(r':([0-9]+\.[0-9]+)') #AJ
@@ -1019,8 +1056,10 @@
         sp=st.split()
         tz=sp[-1]
         if tz and (tz.lower() in ValidZones):
+            self._timezone_naive = False
             st=' '.join(sp[:-1])
         else:
+            self._timezone_naive = True
             tz = None  # Decide later, since the default time zone
         # could depend on the date.
 
@@ -1219,14 +1258,15 @@
         object, represented in the indicated timezone.
         """
         t,tz=self._t,self._tzinfo._zmap[z.lower()]
-        millis = self.millis()
+        micros = self.micros()
+        tznaive = False # you're performing a timzone change, can't be naive
 
         try:
             # Try to use time module for speed.
             yr,mo,dy,hr,mn,sc=safegmtime(t+_tzoffset(tz, t))[:6]
             sc=self._second
             return self.__class__(yr,mo,dy,hr,mn,sc,tz,t,
-                                  self._d,self.time,millis)
+                                  self._d,self.time,micros,tznaive)
         except:  # gmtime can't perform the calculation in the given range.
             # Calculate the difference between the two time zones.
             tzdiff = _tzoffset(tz, t) - _tzoffset(self._tz, t)
@@ -1239,7 +1279,7 @@
             x_new = x + tzdiff
             yr,mo,dy,hr,mn,sc = _calcYMDHMS(x_new, ms)
             return self.__class__(yr,mo,dy,hr,mn,sc,tz,t,
-                                  self._d,self.time,millis)
+                                  self._d,self.time,micros,tznaive)
 
     def isFuture(self):
         """Return true if this object represents a date/time
@@ -1324,13 +1364,13 @@
         than the specified DateTime or time module style time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis > t._millis)
+            return (self._micros > t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t > t)
 
@@ -1346,13 +1386,13 @@
         time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis >= t._millis)
+            return (self._micros >= t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t >= t)
 
@@ -1367,13 +1407,13 @@
         the specified DateTime or time module style time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis == t._millis)
+            return (self._micros == t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t == t)
 
@@ -1388,13 +1428,13 @@
         to the specified DateTime or time module style time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis != t._millis)
+            return (self._micros != t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t != t)
 
@@ -1409,13 +1449,13 @@
         the specified DateTime or time module style time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis < t._millis)
+            return (self._micros < t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t < t)
 
@@ -1430,13 +1470,13 @@
         or equal to the specified DateTime or time module style time.
 
         Revised to give more correct results through comparison of
-        long integer milliseconds.
+        long integer microseconds.
         """
         # Optimized for sorting speed
         try:
-            return (self._millis <= t._millis)
+            return (self._micros <= t._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return (self._t <= t)
 
@@ -1559,15 +1599,35 @@
     def millis(self):
         """Return the millisecond since the epoch in GMT."""
         try:
-            return self._millis
+            micros = self._micros
         except AttributeError:
+            micros = self._upgrade_old()
+        return micros / 1000
+    
+    def micros(self):
+        """Return the microsecond since the epoch in GMT."""
+        try:
+            return self._micros
+        except AttributeError:
             return self._upgrade_old()
+    
+    def timezoneNaive(self):
+        """The python datetime module introduces the idea of distinguishing
+        between timezone aware and timezone naive datetime values. For lossless
+        conversion to and from datetime.datetime record if we record this
+        information using True / False. DateTime makes no distinction, when we
+        don't have any information we return None here.
+        """
+        try:
+            return self._timezone_naive
+        except AttributeError:
+            return None
 
     def _upgrade_old(self):
         """Upgrades a previously pickled DateTime object."""
-        millis = long(math.floor(self._t * 1000.0))
-        self._millis = millis
-        return millis
+        micros = long(math.floor(self._t * 1000000.0))
+        #self._micros = micros # don't upgrade instances in place
+        return micros
 
     def strftime(self, format):
         """Format the date/time using the *current timezone representation*."""
@@ -1712,10 +1772,17 @@
         Dates are output as: YYYY-MM-DDTHH:MM:SSTZD
             T is a literal character.
             TZD is Time Zone Designator, format +HH:MM or -HH:MM
+        
+        If the instance is timezone naive (it was not specified with a timezone
+        when it was constructed) then the timezone is ommitted.
 
         The HTML4 method below offers the same formatting, but converts
         to UTC before returning the value and sets the TZD "Z".
         """
+        if self.timezoneNaive():
+            return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d" % (
+                self._year, self._month, self._day,
+                self._hour, self._minute, self._second)
         tzoffset = _tzoffset2iso8601zone(_tzoffset(self._tz, self._t))
         return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d%s" % (
             self._year, self._month, self._day,
@@ -1735,6 +1802,30 @@
         return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2dZ" % (
             newdate._year, newdate._month, newdate._day,
             newdate._hour, newdate._minute, newdate._second)
+    
+    def asdatetime(self):
+        """Return a standard libary datetime.datetime
+        """
+        tznaive = self.timezoneNaive()
+        if tznaive is True:
+            # we were either converted from an ISO8601 timezone naive string or
+            # a timezone naive datetime
+            second = int(self._second)
+            microsec = self.micros() % 1000000
+            dt = datetime(self._year, self._month, self._day, self._hour,
+                          self._minute, second, microsec)
+            return dt
+        else:
+            raise NotImplementedError('conversion of datetime aware DateTime to datetime unsupported')
+    
+    def utcdatetime(self):
+        """Convert the time to UTC then return a timezone naive datetime object"""
+        utc = self.toZone('UTC')
+        second = int(utc._second)
+        microsec = utc.micros() % 1000000
+        dt = datetime(utc._year, utc._month, utc._day, utc._hour,
+                      utc._minute, second, microsec)
+        return dt
 
     def __add__(self,other):
         """A DateTime may be added to a number and a number may be
@@ -1744,13 +1835,17 @@
             raise DateTimeError,'Cannot add two DateTimes'
         o=float(other)
         tz = self._tz
-        t = (self._t + (o*86400.0))
-        d = (self._d + o)
+        #t = (self._t + (o*86400.0))
+        omicros = round(o*86400000000)
+        tmicros = self.micros() + omicros
+        #d = (self._d + o)
+        t = tmicros / 1000000.0
+        d = (tmicros + long(EPOCH*1000000)) / 86400000000.0
         s = d - math.floor(d)
         ms = t - math.floor(t)
         x = _calcDependentSecond(tz, t)
         yr,mo,dy,hr,mn,sc = _calcYMDHMS(x, ms)
-        return self.__class__(yr,mo,dy,hr,mn,sc,self._tz,t,d,s)
+        return self.__class__(yr,mo,dy,hr,mn,sc,self._tz,t,d,s, None, self.timezoneNaive())
 
     __radd__=__add__
 
@@ -1760,11 +1855,7 @@
         a number.
         """
         if hasattr(other, '_d'):
-            if 0:  # This logic seems right but is incorrect.
-                my_t = self._t + _tzoffset(self._tz, self._t)
-                ob_t = other._t + _tzoffset(other._tz, other._t)
-                return (my_t - ob_t) / 86400.0
-            return self._d - other._d
+            return (self.micros() - other.micros()) / 86400000000.0
         else:
             return self.__add__(-(other))
 
@@ -1786,10 +1877,10 @@
             return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d %s' % (
                     y, m, d, h, mn, s, t)
         else:
-            # s is already rounded to the nearest millisecond, and
+            # s is already rounded to the nearest microsecond, and
             # it's not a whole number of seconds.  Be sure to print
             # 2 digits before the decimal point.
-            return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%06.3f %s' % (
+            return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%06.6f %s' % (
                     y, m, d, h, mn, s, t)
 
     def __cmp__(self,obj):
@@ -1806,9 +1897,9 @@
         """
         # Optimized for sorting speed.
         try:
-            return cmp(self._millis, obj._millis)
+            return cmp(self._micros, obj._micros)
         except AttributeError:
-            try: self._millis
+            try: self._micros
             except AttributeError: self._upgrade_old()
         return cmp(self._t,obj)
 
@@ -1819,17 +1910,22 @@
 
     def __int__(self):
         """Convert to an integer number of seconds since the epoch (gmt)."""
-        return int(self.millis() / 1000)
+        return int(self.micros() / 1000000)
 
     def __long__(self):
         """Convert to a long-int number of seconds since the epoch (gmt)."""
-        return long(self.millis() / 1000)
+        return long(self.micros() / 1000000)
 
     def __float__(self):
         """Convert to floating-point number of seconds since the epoch (gmt)."""
         return float(self._t)
 
     def _parse_iso8601(self,s):
+        # preserve the previously implied contract
+        # who know where this could be used...
+        return _parse_iso8601_preserving_tznaive(s)[:7]
+
+    def _parse_iso8601_preserving_tznaive(self,s):
         try:
             return self.__parse_iso8601(s)
         except IndexError:
@@ -1839,10 +1935,11 @@
     def __parse_iso8601(self,s):
         """Parse an ISO 8601 compliant date.
 
-        See: http://www.omg.org/docs/ISO-stds/06-08-01.pdf
+        See: http://en.wikipedia.org/wiki/ISO_8601
         """
         month = day = week_day = 1
         year = hour = minute = seconds = hour_off = min_off = 0
+        tznaive = True
 
         iso8601 = iso8601Match(s.strip())
         fields = iso8601 and iso8601.groupdict() or {}
@@ -1898,9 +1995,18 @@
         if fields['min_off']:
             min_off = int(fields['min_off'])
 
-        tz = 'GMT%+03d%02d' % (hour_off, min_off)
+        if fields['signal'] or fields['Z']:
+            tznaive = False
+            tz = 'GMT%+03d%02d' % (hour_off, min_off)
+        else:
+            tznaive = True
+            # Figure out what time zone it is in the local area
+            # on the given date.
+            ms = seconds - math.floor(seconds)
+            x = _calcDependentSecond2(year,month,day,hour,minute,seconds)
+            tz = self._calcTimezoneName(x, ms)
 
-        return year, month, day, hour, minute, seconds, tz
+        return year, month, day, hour, minute, seconds, tz, tznaive
 
     def JulianDay(self):
         """Return the Julian day.

Modified: Zope/trunk/lib/python/DateTime/tests/testDateTime.py
===================================================================
--- Zope/trunk/lib/python/DateTime/tests/testDateTime.py	2007-10-18 01:22:24 UTC (rev 80911)
+++ Zope/trunk/lib/python/DateTime/tests/testDateTime.py	2007-10-18 08:41:38 UTC (rev 80912)
@@ -19,6 +19,8 @@
 
 from DateTime.DateTime import _findLocalTimeZoneName
 from DateTime import DateTime
+from datetime import datetime
+import pytz
 
 try:
     __file__
@@ -135,6 +137,7 @@
 
     def testSubtraction(self):
         # Reconstruction of a DateTime from its parts, with subtraction
+        # this also tests the accuracy of addition and reconstruction
         dt = DateTime()
         dt1 = dt - 3.141592653
         dt2 = DateTime(
@@ -190,11 +193,11 @@
         self.failUnless(not (dt == dt1))
 
     def testUpgradeOldInstances(self):
-        # Compare dates that don't have the _millis attribute yet
+        # Compare dates that don't have the _micros attribute yet
         dt = DateTime('1997/1/1')
         dt1 = DateTime('1997/2/2')
-        del dt._millis
-        del dt1._millis
+        del dt._micros
+        del dt1._micros
         self.testCompareOperations(dt, dt1)
 
     def testTZ2(self):
@@ -258,17 +261,21 @@
 
     def testISO8601(self):
         # ISO8601 reference dates
-        ref0 = DateTime('2002/5/2 8:00am GMT')
+        ref0 = DateTime('2002/5/2 8:00am')
         ref1 = DateTime('2002/5/2 8:00am US/Eastern')
         ref2 = DateTime('2006/11/6 10:30 UTC')
         ref3 = DateTime('2004/06/14 14:30:15 GMT-3')
         ref4 = DateTime('2006/01/01 UTC')
+        ref5 = DateTime('2002/5/2 8:00am GMT')
 
         # Basic tests
+        # this is timezone naive and should be interpreted in the local timezone
         isoDt = DateTime('2002-05-02T08:00:00')
         self.assertEqual(ref0, isoDt)
         isoDt = DateTime('2002-05-02T08:00:00Z')
-        self.assertEqual(ref0, isoDt)
+        self.assertEqual(ref5, isoDt)
+        isoDt = DateTime('2002-05-02T08:00:00+00:00')
+        self.assertEqual(ref5, isoDt)
         isoDt = DateTime('2002-05-02T08:00:00-04:00')
         self.assertEqual(ref1, isoDt)
         isoDt = DateTime('2002-05-02 08:00:00-04:00')
@@ -302,7 +309,7 @@
 
         # Bug 2191: timezones with only one digit for hour
         isoDt = DateTime('20020502T080000+0')
-        self.assertEqual(ref0, isoDt)
+        self.assertEqual(ref5, isoDt)
         isoDt = DateTime('20020502 080000-4')
         self.assertEqual(ref1, isoDt)
         isoDt = DateTime('20020502T080000-400')
@@ -459,6 +466,45 @@
         dt = DateTime('2002-05-02T08:00:00+00:00')
         ok = dt.strftime('Le %d/%m/%Y a %Hh%M').replace('a', u'\xe0')
         self.assertEqual(dt.strftime(u'Le %d/%m/%Y \xe0 %Hh%M'), ok)
+    
+    def testTimezoneNaiveHandling(self):
+        # checks that we assign timezone naivity correctly
+        dt = DateTime('2007-10-04T08:00:00+00:00')
+        assert dt.timezoneNaive() is False, 'error with naivity handling in __parse_iso8601'
+        dt = DateTime('2007-10-04T08:00:00Z')
+        assert dt.timezoneNaive() is False, 'error with naivity handling in __parse_iso8601'
+        dt = DateTime('2007-10-04T08:00:00')
+        assert dt.timezoneNaive() is True, 'error with naivity handling in __parse_iso8601'
+        dt = DateTime('2007/10/04 15:12:33.487618 GMT+1')
+        assert dt.timezoneNaive() is False, 'error with naivity handling in _parse'
+        dt = DateTime('2007/10/04 15:12:33.487618')
+        assert dt.timezoneNaive() is True, 'error with naivity handling in _parse'
+        dt = DateTime()
+        assert dt.timezoneNaive() is False, 'error with naivity for current time'
+        s = '2007-10-04T08:00:00'
+        dt = DateTime(s)
+        self.assertEqual(s, dt.ISO8601())
+        s = '2007-10-04T08:00:00+00:00'
+        dt = DateTime(s)
+        self.assertEqual(s, dt.ISO8601())
+    
+    def testConversions(self):
+        sdt0 = datetime.now() # this is a timezone naive datetime
+        dt0 = DateTime(sdt0)
+        assert dt0.timezoneNaive() is True, (sdt0, dt0)
+        sdt1 = datetime(2007, 10, 4, 18, 14, 42, 580, pytz.utc)
+        dt1 = DateTime(sdt1)
+        assert dt1.timezoneNaive() is False, (sdt1, dt1)
+        
+        # convert back
+        sdt2 = dt0.asdatetime()
+        self.assertEqual(sdt0, sdt2)
+        sdt3 = dt1.utcdatetime() # this returns a timezone naive datetime
+        self.assertEqual(sdt1.hour, sdt3.hour)
+        
+        dt4 = DateTime('2007-10-04T10:00:00+05:00')
+        sdt4 = datetime(2007, 10, 4, 5, 0)
+        self.assertEqual(dt4.utcdatetime(), sdt4)
 
 
 def test_suite():



More information about the Zope-Checkins mailing list