===========================
recurring sequence producer
===========================

The recurringSequence calculates a sequence from a datetime, an interval in 
minutes until a end datetime.

	>>> from Products.DateRecurringIndex.recurring import recurringSequence
	>>> from Products.DateRecurringIndex.recurring import DSTADJUST
	>>> from Products.DateRecurringIndex.recurring import DSTKEEP
	>>> from Products.DateRecurringIndex.recurring import DSTAUTO
	>>> from datetime import timedelta
	>>> from datetime import datetime
	>>> from pprint import pprint
	>>> import pytz
	
general
-------

The simplest case is to just have a one-time events.

	>>> start = datetime(2008,01,01,0,0,0,0,pytz.timezone('CET'))
	>>> list(recurringSequence(start, 0, None))
	[datetime.datetime(2008, 1, 1, 0, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>)]
		
We want all hours of a day:
	>>> until = start + timedelta(days=1) - timedelta(microseconds=1)	
	>>> pprint(list(recurringSequence(start, 60, until)))
	[datetime.datetime(2008, 1, 1, 0, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 1, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 2, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 3, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 4, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 5, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 6, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 7, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 8, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 9, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 10, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 11, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 12, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 13, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 14, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 15, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 16, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 17, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 18, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 19, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 20, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 21, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 22, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 1, 1, 23, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>)]
	
different daylight saving time behaviours
-----------------------------------------

First lets change from winter to summertime (EU rules with CET):

	>>> start = datetime(2008,03,29,21,0,0,0,pytz.timezone('CET'))
	>>> until = datetime(2008,03,30,21,0,0,0,pytz.timezone('CET')) - timedelta(microseconds=1)
	>>> until = until.replace(tzinfo=until.tzinfo.normalize(until).tzinfo)
	>>> until
	datetime.datetime(2008, 3, 30, 20, 59, 59, 999999, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)

	>>> seq = recurringSequence(start, 60, until, dst=DSTADJUST)
	>>> seq = list(seq)
	>>> len(seq)
	24

This sequence has one hour to much, so DSTADJUST on a granularity below one day 
is wrong! 1:00 CET is the same as 2:00 CEST!

	>>> pprint(seq)
	[datetime.datetime(2008, 3, 29, 21, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 29, 22, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 29, 23, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 0, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 1, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 2, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 3, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 4, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 5, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 6, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 7, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 8, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 9, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 10, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 12, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 13, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 14, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 15, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 16, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 17, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 18, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 19, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 20, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]
	
Anyway, we want this behaviour on a day-level, see next example, a event should 
recurr every day 11:00am.

	>>> start = datetime(2008,03,29,11,0,0,0,pytz.timezone('CET'))
	>>> until = datetime(2008,03,31,11,0,0,0,pytz.timezone('CET')) 
	>>> until = until.replace(tzinfo=until.tzinfo.normalize(until).tzinfo)
	>>> seq = recurringSequence(start, 24*60, until, dst=DSTADJUST)
	>>> seq = list(seq)
	>>> pprint(seq)
	[datetime.datetime(2008, 3, 29, 11, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 31, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]

see what DSTKEEP do:

	>>> seq = recurringSequence(start, 24*60, until, dst=DSTKEEP)
	>>> seq = list(seq)
	>>> pprint(seq)
	[datetime.datetime(2008, 3, 29, 11, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 12, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]

Here the correct behaviour on day level with DSTKEEP.
	
	>>> start = datetime(2008,03,29,21,0,0,0,pytz.timezone('CET'))
	>>> until = datetime(2008,03,30,21,0,0,0,pytz.timezone('CET')) - timedelta(microseconds=1)
	>>> until = until.replace(tzinfo=until.tzinfo.normalize(until).tzinfo)
	>>> seq = recurringSequence(start, 60, until, dst=DSTKEEP)
	>>> seq = list(seq)
	>>> len(seq) 
	23
	>>> pprint(seq)
	[datetime.datetime(2008, 3, 29, 21, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 29, 22, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 29, 23, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 0, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 1, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 3, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 4, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 5, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 6, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 7, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 8, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 9, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 10, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 12, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 13, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 14, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 15, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 16, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 17, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 18, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 19, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 30, 20, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]

Usally we want an clever behaviour, dependend on delta. This is implemented 
with DSTAUTO, which is the default behaviour.

Here the correct behaviour on day level it chooses DSTADJUST.

	>>> start = datetime(2008,03,29,11,0,0,0,pytz.timezone('CET'))
	>>> until = datetime(2008,03,31,11,0,0,0,pytz.timezone('CET')) 
	>>> until = until.replace(tzinfo=until.tzinfo.normalize(until).tzinfo)
	>>> seq = recurringSequence(start, 24*60, until)
	>>> seq = list(seq)
	>>> pprint(seq)
	[datetime.datetime(2008, 3, 29, 11, 0, tzinfo=<DstTzInfo 'CET' CET+1:00:00 STD>),
	 datetime.datetime(2008, 3, 30, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>),
	 datetime.datetime(2008, 3, 31, 11, 0, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]
     
The correct behaviour on below day level: it chooses DSTKEEP.
	
	>>> start = datetime(2008,03,29,21,0,0,0,pytz.timezone('CET'))
	>>> until = datetime(2008,03,30,21,0,0,0,pytz.timezone('CET')) - timedelta(microseconds=1)
	>>> until = until.replace(tzinfo=until.tzinfo.normalize(until).tzinfo)
	>>> seq = recurringSequence(start, 60, until)
	>>> seq = list(seq)
	>>> len(seq) 
	23	

recurringIntSequence
--------------------

taking the above results in integers if recurringIntSequence is called:

	>>> from Products.DateRecurringIndex.recurring import recurringIntSequence
	>>> seq = recurringIntSequence(start, 60, until)
	>>> seq = list(seq)
	>>> pprint(seq)
	[1075822320,
	 1075822380,
	 1075822440,
	 1075822500,
	 1075822560,
	 1075822620,
	 1075822680,
	 1075822740,
	 1075822800,
	 1075822860,
	 1075822920,
	 1075822980,
	 1075823040,
	 1075823100,
	 1075823160,
	 1075823220,
	 1075823280,
	 1075823340,
	 1075823400,
	 1075823460,
	 1075823520,
	 1075823580,
	 1075823640]	


Zope DateTime support
=====================		

Now some tests with Zopes ugly DateTime

	>>> from DateTime import DateTime

First with UTC

	>>> DT = DateTime('2008-08-26 23:59:00 GMT+0')
	>>> dt = datetime(2008, 8, 26, 23, 59, 0, 0, pytz.timezone('utc'))
	>>> seqDT = list(recurringIntSequence(DT, 0, None))
	>>> seqdt = list(recurringIntSequence(dt, 0, None))
	>>> seqDT[0] == seqdt[0]
	True

Now with GMT+2 (CET with DST)

	>>> DT = DateTime('2008-08-26 23:59:00 GMT+2')
	>>> dt = datetime(2008, 8, 26, 23, 59, 0, 0, pytz.timezone('CET'))
	>>> seqDT = list(recurringSequence(DT, 0, None))
	>>> seqDT 
	[datetime.datetime(2008, 8, 26, 23, 59, tzinfo=<UTC>)]
	
	>>> seqdt = list(recurringSequence(dt, 0, None))
	>>> seqdt 
	[datetime.datetime(2008, 8, 26, 23, 59, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)]

this fails! mainly zope DateTimes fault
	>>> seqDT[0] == seqdt[0]
	True

Does integer represenation work?

	>>> seqDT = list(recurringIntSequence(DT, 0, None))
	>>> seqdt = list(recurringIntSequence(dt, 0, None))

this fails too! see above
	>>> seqDT[0] == seqdt[0]
	True
	
	