[Zope-Checkins] SVN: Zope/branches/2.12/ Backported fix for timezone issues in date index tests from trunk.

Tres Seaver tseaver at palladion.com
Tue Sep 22 14:52:25 EDT 2009


Log message for revision 104432:
  Backported fix for timezone issues in date index tests from trunk.
  

Changed:
  U   Zope/branches/2.12/doc/CHANGES.rst
  U   Zope/branches/2.12/src/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py
  U   Zope/branches/2.12/src/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py

-=-
Modified: Zope/branches/2.12/doc/CHANGES.rst
===================================================================
--- Zope/branches/2.12/doc/CHANGES.rst	2009-09-22 17:14:00 UTC (rev 104431)
+++ Zope/branches/2.12/doc/CHANGES.rst	2009-09-22 18:52:25 UTC (rev 104432)
@@ -18,6 +18,8 @@
 Bugs Fixed
 ++++++++++
 
+- Backported fix for timezone issues in date index tests from trunk.
+
 - LP #414757 (backported from Zope trunk): don't emit a IEndRequestEvent when
   clearing a cloned request.
 

Modified: Zope/branches/2.12/src/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py
===================================================================
--- Zope/branches/2.12/src/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py	2009-09-22 17:14:00 UTC (rev 104431)
+++ Zope/branches/2.12/src/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py	2009-09-22 18:52:25 UTC (rev 104432)
@@ -16,19 +16,7 @@
 """
 
 import unittest
-import Testing
-import Zope2
-Zope2.startup()
 
-from datetime import date, datetime, tzinfo, timedelta
-import time
-from types import IntType, FloatType
-
-from DateTime import DateTime
-
-from Products.PluginIndexes.DateIndex.DateIndex import DateIndex, Local
-
-
 class Dummy:
 
     def __init__(self, name, date):
@@ -48,67 +36,86 @@
 ###############################################################################
 # excerpted from the Python module docs
 ###############################################################################
-ZERO = timedelta(0)
-HOUR = timedelta(hours=1)
-def first_sunday_on_or_after(dt):
-    days_to_go = 6 - dt.weekday()
-    if days_to_go:
-        dt += timedelta(days_to_go)
-    return dt
 
-# In the US, DST starts at 2am (standard time) on the first Sunday in April.
-DSTSTART = datetime(1, 4, 1, 2)
-# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
-# which is the first Sunday on or after Oct 25.
-DSTEND = datetime(1, 10, 25, 1)
+def _getEastern():
+    from datetime import date
+    from datetime import datetime
+    from datetime import timedelta
+    from datetime import tzinfo
+    ZERO = timedelta(0)
+    HOUR = timedelta(hours=1)
+    def first_sunday_on_or_after(dt):
+        days_to_go = 6 - dt.weekday()
+        if days_to_go:
+            dt += timedelta(days_to_go)
+        return dt
 
-class USTimeZone(tzinfo):
+    # In the US, DST starts at 2am (standard time) on the first Sunday in
+    # April...
+    DSTSTART = datetime(1, 4, 1, 2)
+    # and ends at 2am (DST time; 1am standard time) on the last Sunday of
+    # October, which is the first Sunday on or after Oct 25.
+    DSTEND = datetime(1, 10, 25, 1)
 
-    def __init__(self, hours, reprname, stdname, dstname):
-        self.stdoffset = timedelta(hours=hours)
-        self.reprname = reprname
-        self.stdname = stdname
-        self.dstname = dstname
+    class USTimeZone(tzinfo):
 
-    def __repr__(self):
-        return self.reprname
+        def __init__(self, hours, reprname, stdname, dstname):
+            self.stdoffset = timedelta(hours=hours)
+            self.reprname = reprname
+            self.stdname = stdname
+            self.dstname = dstname
 
-    def tzname(self, dt):
-        if self.dst(dt):
-            return self.dstname
-        else:
-            return self.stdname
+        def __repr__(self):
+            return self.reprname
 
-    def utcoffset(self, dt):
-        return self.stdoffset + self.dst(dt)
+        def tzname(self, dt):
+            if self.dst(dt):
+                return self.dstname
+            else:
+                return self.stdname
 
-    def dst(self, dt):
-        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
+        def utcoffset(self, dt):
+            return self.stdoffset + self.dst(dt)
 
-        # Find first Sunday in April & the last in October.
-        start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
-        end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
+        def dst(self, dt):
+            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
 
-        # Can't compare naive to aware objects, so strip the timezone from
-        # dt first.
-        if start <= dt.replace(tzinfo=None) < end:
-            return HOUR
-        else:
-            return ZERO
+            # Find first Sunday in April & the last in October.
+            start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
+            end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
 
-Eastern  = USTimeZone(-5, "Eastern",  "EST", "EDT")
+            # Can't compare naive to aware objects, so strip the timezone from
+            # dt first.
+            if start <= dt.replace(tzinfo=None) < end:
+                return HOUR
+            else:
+                return ZERO
+
+    return USTimeZone(-5, "Eastern",  "EST", "EDT")
+
 ###############################################################################
 
 
 class DI_Tests(unittest.TestCase):
-    def setUp(self):
-        self._values = (
+
+    def _getTargetClass(self):
+        from Products.PluginIndexes.DateIndex.DateIndex import DateIndex
+        return DateIndex
+
+    def _makeOne(self, id='date'):
+        return self._getTargetClass()(id)
+
+    def _getValues(self):
+        from DateTime import DateTime
+        from datetime import date
+        from datetime import datetime
+        return [
             (0, Dummy('a', None)),                            # None
             (1, Dummy('b', DateTime(0))),                     # 1055335680
             (2, Dummy('c', DateTime('2002-05-08 15:16:17'))), # 1072667236
@@ -120,29 +127,15 @@
             (8, Dummy('g', date(2034,2,5))),                  # 1073599200
             (9, Dummy('h', datetime(2034,2,5,15,20,5))),      # (varies)
             (10, Dummy('i', datetime(2034,2,5,10,17,5,
-                                     tzinfo=Eastern))),       # 1073600117
-        )
-        self._index = DateIndex('date')
-        self._noop_req  = {'bar': 123}
-        self._request   = {'date': DateTime(0)}
-        self._min_req   = {'date': {'query': DateTime('2032-05-08 15:16:17'),
-            'range': 'min'}}
-        self._max_req   = {'date': {'query': DateTime('2032-05-08 15:16:17'),
-            'range': 'max'}}
-        self._range_req = {'date': {'query':(DateTime('2002-05-08 15:16:17'),
-                                    DateTime('2062-05-08 15:16:17')),
-                           'range': 'min:max'}}
-        self._zero_req  = {'date': 0}
-        self._none_req  = {'date': None}
-        self._float_req = {'date': 1072742620.0}
-        self._int_req   = {'date': 1072742900}
+                                     tzinfo=_getEastern()))), # 1073600117
+        ]
 
-    def _populateIndex( self ):
-        for k, v in self._values:
-            self._index.index_object(k, v)
+    def _populateIndex(self, index):
+        for k, v in self._getValues():
+            index.index_object(k, v)
 
-    def _checkApply(self, req, expectedValues):
-        result, used = self._index._apply_index(req)
+    def _checkApply(self, index, req, expectedValues):
+        result, used = index._apply_index(req)
         if hasattr(result, 'keys'):
             result = result.keys()
         self.failUnlessEqual(used, ('date',))
@@ -152,8 +145,12 @@
             self.failUnless(k in result)
 
     def _convert(self, dt):
-        if type(dt) in (FloatType, IntType):
-            yr, mo, dy, hr, mn = time.gmtime(dt)[:5]
+        from time import gmtime
+        from datetime import date
+        from datetime import datetime
+        from Products.PluginIndexes.DateIndex.DateIndex import Local
+        if isinstance(dt, (float, int)):
+            yr, mo, dy, hr, mn = gmtime(dt)[:5]
         elif type(dt) is date:
             yr, mo, dy, hr, mn = dt.timetuple()[:5]
         elif type(dt) is datetime:
@@ -171,37 +168,50 @@
         from Products.PluginIndexes.interfaces import IUniqueValueIndex
         from zope.interface.verify import verifyClass
 
-        verifyClass(IDateIndex, DateIndex)
-        verifyClass(IPluggableIndex, DateIndex)
-        verifyClass(ISortIndex, DateIndex)
-        verifyClass(IUniqueValueIndex, DateIndex)
+        verifyClass(IDateIndex, self._getTargetClass())
+        verifyClass(IPluggableIndex, self._getTargetClass())
+        verifyClass(ISortIndex, self._getTargetClass())
+        verifyClass(IUniqueValueIndex, self._getTargetClass())
 
     def test_empty(self):
-        empty = self._index
+        from DateTime import DateTime
+        index = self._makeOne()
 
-        self.failUnlessEqual(len(empty), 0)
-        self.failUnlessEqual(len(empty.referencedObjects()), 0)
+        self.failUnlessEqual(len(index), 0)
+        self.failUnlessEqual(len(index.referencedObjects()), 0)
 
-        self.failUnless(empty.getEntryForObject(1234) is None)
+        self.failUnless(index.getEntryForObject(1234) is None)
         marker = []
-        self.failUnless(empty.getEntryForObject(1234, marker) is marker)
-        empty.unindex_object(1234) # shouldn't throw
+        self.failUnless(index.getEntryForObject(1234, marker) is marker)
+        index.unindex_object(1234) # shouldn't throw
 
-        self.failUnless(empty.hasUniqueValuesFor('date'))
-        self.failIf(empty.hasUniqueValuesFor('foo'))
-        self.failUnlessEqual(len(empty.uniqueValues('date')), 0)
+        self.failUnless(index.hasUniqueValuesFor('date'))
+        self.failIf(index.hasUniqueValuesFor('foo'))
+        self.failUnlessEqual(len(index.uniqueValues('date')), 0)
 
-        self.failUnless(empty._apply_index({'zed': 12345}) is None)
+        self.failUnless(index._apply_index({'zed': 12345}) is None)
 
-        self._checkApply(self._request, [])
-        self._checkApply(self._min_req, [])
-        self._checkApply(self._max_req, [])
-        self._checkApply(self._range_req, [])
+        self._checkApply(index,
+                         {'date': DateTime(0)}, [])
+        self._checkApply(index,
+                         {'date': {'query': DateTime('2032-05-08 15:16:17'),
+                                   'range': 'min'}},
+                         [])
+        self._checkApply(index,
+                         {'date': {'query': DateTime('2032-05-08 15:16:17'),
+                                   'range': 'max'}},
+                         [])
+        self._checkApply(index,
+                         {'date': {'query':(DateTime('2002-05-08 15:16:17'),
+                                            DateTime('2062-05-08 15:16:17')),
+                                   'range': 'min:max'}},
+                         [])
 
     def test_retrieval( self ):
-        self._populateIndex()
-        values = self._values
-        index = self._index
+        from DateTime import DateTime
+        index = self._makeOne()
+        self._populateIndex(index)
+        values = self._getValues()
 
         self.failUnlessEqual(len(index), len(values) - 2) # One dupe, one empty
         self.failUnlessEqual(len(index.referencedObjects()), len(values) - 1)
@@ -214,30 +224,43 @@
 
         for k, v in values:
             if v.date():
-                self.failUnlessEqual(self._index.getEntryForObject(k),
+                self.failUnlessEqual(index.getEntryForObject(k),
                     self._convert(v.date()))
 
         self.failUnlessEqual(len(index.uniqueValues('date')), len(values) - 2)
-        self.failUnless(index._apply_index(self._noop_req) is None)
+        self.failUnless(index._apply_index({'bar': 123}) is None)
 
-        self._checkApply(self._request, values[1:2])
-        self._checkApply(self._min_req, values[3:6] + values[8:])
-        self._checkApply(self._max_req, values[1:4] + values[6:8])
-        self._checkApply(self._range_req, values[2:] )
-        self._checkApply(self._float_req, [values[6]] )
-        self._checkApply(self._int_req, [values[7]] )
+        self._checkApply(index,
+                         {'date': DateTime(0)}, values[1:2])
+        self._checkApply(index,
+                         {'date': {'query': DateTime('2032-05-08 15:16:17'),
+                                   'range': 'min'}},
+                         values[3:6] + values[8:])
+        self._checkApply(index,
+                         {'date': {'query': DateTime('2032-05-08 15:16:17'),
+                                   'range': 'max'}},
+                         values[1:4] + values[6:8])
+        self._checkApply(index,
+                         {'date': {'query':(DateTime('2002-05-08 15:16:17'),
+                                            DateTime('2062-05-08 15:16:17')),
+                                   'range': 'min:max'}},
+                         values[2:] )
+        self._checkApply(index,
+                         {'date': 1072742620.0}, [values[6]])
+        self._checkApply(index,
+                         {'date': 1072742900}, [values[7]])
 
     def test_naive_convert_to_utc(self):
-        values = self._values
-        index = self._index
+        index = self._makeOne()
+        values = self._getValues()
         index.index_naive_time_as_local = False
-        self._populateIndex()
+        self._populateIndex(index)
         for k, v in values[9:]:
             # assert that the timezone is effectively UTC for item 9,
             # and still correct for item 10
             yr, mo, dy, hr, mn = v.date().utctimetuple()[:5]
             val = (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn
-            self.failUnlessEqual(self._index.getEntryForObject(k), val)
+            self.failUnlessEqual(index.getEntryForObject(k), val)
 
     def test_removal(self):
         """ DateIndex would hand back spurious entries when used as a
@@ -246,10 +269,11 @@
             None. The catalog consults a sort_index's
             documentToKeyMap() to build the brains.
         """
-        values = self._values
-        index = self._index
-        self._populateIndex()
-        self._checkApply(self._int_req, [values[7]])
+        values = self._getValues()
+        index = self._makeOne()
+        self._populateIndex(index)
+        self._checkApply(index,
+                         {'date': 1072742900}, [values[7]])
         index.index_object(7, None)
         self.failIf(7 in index.documentToKeyMap().keys())
 

Modified: Zope/branches/2.12/src/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py
===================================================================
--- Zope/branches/2.12/src/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py	2009-09-22 17:14:00 UTC (rev 104431)
+++ Zope/branches/2.12/src/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py	2009-09-22 18:52:25 UTC (rev 104432)
@@ -16,15 +16,7 @@
 """
 
 import unittest
-import Testing
-import Zope2
-Zope2.startup()
 
-import sys
-
-from Products.PluginIndexes.DateRangeIndex.DateRangeIndex import DateRangeIndex
-
-
 class Dummy:
 
     def __init__( self, name, start, stop ):
@@ -74,12 +66,23 @@
 
 class DRI_Tests( unittest.TestCase ):
 
-    def setUp( self ):
-        pass
 
-    def tearDown( self ):
-        pass
+    def _getTargetClass(self):
+        from Products.PluginIndexes.DateRangeIndex.DateRangeIndex \
+            import DateRangeIndex
+        return DateRangeIndex
 
+    def _makeOne(self,
+                 id,
+                 since_field=None,
+                 until_field=None,
+                 caller=None,
+                 extra=None,
+                ):
+        klass = self._getTargetClass()
+        return klass(id, since_field, until_field, caller, extra)
+
+
     def test_z3interfaces(self):
         from Products.PluginIndexes.interfaces import IDateRangeIndex
         from Products.PluginIndexes.interfaces import IPluggableIndex
@@ -87,14 +90,14 @@
         from Products.PluginIndexes.interfaces import IUniqueValueIndex
         from zope.interface.verify import verifyClass
 
-        verifyClass(IDateRangeIndex, DateRangeIndex)
-        verifyClass(IPluggableIndex, DateRangeIndex)
-        verifyClass(ISortIndex, DateRangeIndex)
-        verifyClass(IUniqueValueIndex, DateRangeIndex)
+        verifyClass(IDateRangeIndex, self._getTargetClass())
+        verifyClass(IPluggableIndex, self._getTargetClass())
+        verifyClass(ISortIndex, self._getTargetClass())
+        verifyClass(IUniqueValueIndex, self._getTargetClass())
 
     def test_empty( self ):
 
-        empty = DateRangeIndex( 'empty' )
+        empty = self._makeOne( 'empty' )
 
         assert empty.getEntryForObject( 1234 ) is None
         empty.unindex_object( 1234 ) # shouldn't throw
@@ -111,18 +114,18 @@
 
     def test_retrieval( self ):
 
-        work = DateRangeIndex( 'work', 'start', 'stop' )
+        index = self._makeOne( 'work', 'start', 'stop' )
 
         for i in range( len( dummies ) ):
-            work.index_object( i, dummies[i] )
+            index.index_object( i, dummies[i] )
 
         for i in range( len( dummies ) ):
-            assert work.getEntryForObject( i ) == dummies[i].datum()
+            self.assertEqual(index.getEntryForObject( i ), dummies[i].datum())
 
         for value in range( -1, 15 ):
 
             matches = matchingDummies( value )
-            results, used = work._apply_index( { 'work' : value } )
+            results, used = index._apply_index( { 'work' : value } )
             assert used == ( 'start', 'stop' )
 
             assert len( matches ) == len( results ), ( '%s: %s == %s'
@@ -131,46 +134,87 @@
             matches.sort( lambda x, y: cmp( x.name(), y.name() ) )
 
             for result, match in map( None, results, matches ):
-                assert work.getEntryForObject( result ) == match.datum()
+                self.assertEqual(index.getEntryForObject(result), match.datum())
 
     def test_longdates( self ):
         self.assertRaises(OverflowError, self._badlong )
 
     def _badlong(self):
-        work = DateRangeIndex ('work', 'start', 'stop' )
+        import sys
+        index = self._makeOne ('work', 'start', 'stop' )
         bad = Dummy( 'bad', long(sys.maxint) + 1, long(sys.maxint) + 1 )
-        work.index_object( 0, bad )
+        index.index_object( 0, bad )
 
     def test_datetime(self):
         from datetime import datetime
+        from DateTime.DateTime import DateTime
+        from Products.PluginIndexes.DateIndex.tests.test_DateIndex \
+            import _getEastern
+        before = datetime(2009, 7, 11, 0, 0, tzinfo=_getEastern())
+        start = datetime(2009, 7, 13, 5, 15, tzinfo=_getEastern())
+        between = datetime(2009, 7, 13, 5, 45, tzinfo=_getEastern())
+        stop = datetime(2009, 7, 13, 6, 30, tzinfo=_getEastern())
+        after = datetime(2009, 7, 14, 0, 0, tzinfo=_getEastern())
+
+        dummy = Dummy('test', start, stop)
+        index = self._makeOne( 'work', 'start', 'stop' )
+        index.index_object(0, dummy)
+
+        self.assertEqual(index.getEntryForObject(0),
+                        (DateTime(start).millis() / 60000,
+                         DateTime(stop).millis() / 60000))
+
+        results, used = index._apply_index( { 'work' : before } )
+        self.assertEqual(len(results), 0)
+
+        results, used = index._apply_index( { 'work' : start } )
+        self.assertEqual(len(results), 1)
+
+        results, used = index._apply_index( { 'work' : between } )
+        self.assertEqual(len(results), 1)
+
+        results, used = index._apply_index( { 'work' : stop } )
+        self.assertEqual(len(results), 1)
+
+        results, used = index._apply_index( { 'work' : after } )
+        self.assertEqual(len(results), 0)
+
+    def test_datetime_naive_timezone(self):
+        from datetime import datetime
+        from DateTime.DateTime import DateTime
+        from Products.PluginIndexes.DateIndex.DateIndex import Local
         before = datetime(2009, 7, 11, 0, 0)
         start = datetime(2009, 7, 13, 5, 15)
+        start_local = datetime(2009, 7, 13, 5, 15, tzinfo=Local)
         between = datetime(2009, 7, 13, 5, 45)
         stop = datetime(2009, 7, 13, 6, 30)
+        stop_local = datetime(2009, 7, 13, 6, 30, tzinfo=Local)
         after = datetime(2009, 7, 14, 0, 0)
 
         dummy = Dummy('test', start, stop)
-        work = DateRangeIndex( 'work', 'start', 'stop' )
-        work.index_object(0, dummy)
+        index = self._makeOne( 'work', 'start', 'stop' )
+        index.index_object(0, dummy)
+  
+        self.assertEqual(index.getEntryForObject(0),
+                        (DateTime(start_local).millis() / 60000,
+                         DateTime(stop_local).millis() / 60000))
+  
+        results, used = index._apply_index( { 'work' : before } )
+        self.assertEqual(len(results), 0)
 
-        assert work.getEntryForObject(0) == (20790915, 20790990)
+        results, used = index._apply_index( { 'work' : start } )
+        self.assertEqual(len(results), 1)
 
-        results, used = work._apply_index( { 'work' : before } )
-        assert len(results) == 0
+        results, used = index._apply_index( { 'work' : between } )
+        self.assertEqual(len(results), 1)
 
-        results, used = work._apply_index( { 'work' : start } )
-        assert len(results) == 1
+        results, used = index._apply_index( { 'work' : stop } )
+        self.assertEqual(len(results), 1)
 
-        results, used = work._apply_index( { 'work' : between } )
-        assert len(results) == 1
+        results, used = index._apply_index( { 'work' : after } )
+        self.assertEqual(len(results), 0)
 
-        results, used = work._apply_index( { 'work' : stop } )
-        assert len(results) == 1
 
-        results, used = work._apply_index( { 'work' : after } )
-        assert len(results) == 0
-
-
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest( unittest.makeSuite( DRI_Tests ) )



More information about the Zope-Checkins mailing list