The bforests in the periodic module have simple policies for automatic
tree rotation based on time. Each take one additional argument beyond
the usual bforest initialization: period.  This is effectively the
minimum amount of time a given value will be kept in the bforest.

For example, consider a periodic bforest with three trees and a period of three
hours.

As with normal bforests, periodic bforests come in four flavors:
Integer-Integer (IIBForest), Integer-Object (IOBForest), Object-Integer
(OIBForest), and Object-Object (OOBForest).  The examples here will deal
with them in the abstract: we will create classes from the imaginary and
representative BForest class, and generate keys from KeyGenerator and
values from ValueGenerator.  From the examples you should be able to
extrapolate usage of all four types.

We will also imagine that we control the time with a function called setNow.

    >>> import datetime
    >>> from zope.bforest import utils
    >>> setNow(datetime.datetime(2006, 9, 11, 22, 51, tzinfo=utils.UTC))
    >>> d = BForest(datetime.timedelta(hours=3), count=3)
    >>> d.last_rotation
    datetime.datetime(2006, 9, 11, 22, 51, tzinfo=<UTC>)

Now let's put in some keys and advance time.

    >>> first_hour = {KeyGenerator(): ValueGenerator()}
    >>> d.update(first_hour)
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> second_hour = {KeyGenerator(): ValueGenerator()}
    >>> d.update(second_hour)
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> third_hour = {KeyGenerator(): ValueGenerator()}
    >>> d.update(third_hour)
    >>> current = first_hour.copy()
    >>> current.update(second_hour)
    >>> current.update(third_hour)
    >>> d == current
    True
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> d == current
    True
    >>> fourth_hour = {KeyGenerator(): ValueGenerator()}
    >>> d.update(fourth_hour)
    >>> current.update(fourth_hour)
    >>> del current[first_hour.keys()[0]]
    >>> d == current
    True

Updating isn't the only way to rotate the trees though.  Basically
any non-clear mutating operation will cause a rotation: __setitem__,
update, __delitem__, pop, and popitem.  We've seen update; here are examples
of the rest.

    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> fifth_hour = {KeyGenerator(): ValueGenerator()}
    >>> d[fifth_hour.keys()[0]] = fifth_hour.values()[0] # __setitem__
    >>> current.update(fifth_hour)
    >>> del current[second_hour.keys()[0]]
    >>> d == current
    True
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> del d[fifth_hour.keys()[0]] # __delitem__
    >>> d == fourth_hour
    True
    >>> sixth_hour = {KeyGenerator(): ValueGenerator()}
    >>> d[sixth_hour.keys()[0]] = sixth_hour.values()[0]
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> d.pop(sixth_hour.keys()[0]) == sixth_hour.values()[0] # pop
    True
    >>> d == {}
    True
    >>> seventh_hour = {KeyGenerator(): ValueGenerator()}
    >>> d[seventh_hour.keys()[0]] = seventh_hour.values()[0]
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> d.rotateBucket()
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> ninth_hour = {KeyGenerator(): ValueGenerator()}
    >>> d[ninth_hour.keys()[0]] = ninth_hour.values()[0]
    >>> setNow(datetime.datetime.now(utils.UTC) + datetime.timedelta(hours=1))
    >>> out = dict((d.popitem(),))
    >>> out == ninth_hour
    True
    >>> d == {}
    True

