Page 3 of 4
Posted: Mon Apr 16, 2007 10:49 am
by inghamn
Greetings all. I've been building a content manager with support for Events. I've been tackling exactly this problem. So far, my best design has involved two tables, and a stored procedure.
I store the events with the recurrance rule information. Currently split out into a few fields. The stored procedure takes a start and end date and returns all recurrances of events inside that range, substituting exceptions for given recurrances as necessary.
This lets us keep store events that recur forever and not have to store an infinite amount recurrances in a table. Forcing the use of a range when asking for the exploded events keeps the cpu time low.
Right now, my challenge is adding to the stored procedure to correctly calculate recurrances for stuff like "the 3rd Monday of every month". The stored procedure is currently using date_add() to generate each recurrance.
If you think I'm on the right track, let me know. I can post all code, and would love other ideas.
Code: Select all
create table events (
id int unsigned not null primary key auto_increment,
start datetime not null,
end datetime not null,
allDayEvent tinyint(1) unsigned,
recurrance_frequency enum('daily','weekly','monthly','yearly'),
recurrance_interval tinyint(2) unsigned,
recurrance_weekday enum('SU','MO','TU','WE','TH','FR','SA'),
) engine=InnoDB;
create table event_exceptions (
event_id int unsigned not null,
original_start datetime not null,
start datetime not null,
end datetime not null,
primary key (event_id,original_start),
foreign key (event_id) references events(id)
) engine=InnoDB;
I can post the stored procedure if people are interested.
Cliff Ingham
City of Bloomington, Indiana
Posted: Mon Apr 16, 2007 12:31 pm
by inghamn
Well, here's the stored procedure I'm using so far. I'm still trying to figure out how to increment recurrances for things like "3rd friday of every other month". But right now, it still seems possible. And storing the information's not really the hard part - just the incrementing.
Code: Select all
delimiter //
create procedure select_exploded_events(rangeStart date,rangeEnd date)
begin
declare done int default 0;
declare pk int unsigned;
declare s,e datetime;
declare f varchar(7);
declare i tinyint(2) unsigned;
declare w char(2);
declare counter int unsigned default 0;
declare period varchar(5);
declare recurrance_start datetime;
declare recurrance_end datetime;
declare r_start datetime;
declare exception_start datetime default null;
declare exception_end datetime default null;
declare cur cursor for
select id,start,end,recurrance_frequency,recurrance_interval,recurrance_weekday from events
where (start between rangeStart and rangeEnd)
or (end between rangeStart and rangeEnd)
or (start<=rangeStart and end>=rangeStart);
declare continue handler for sqlstate '02000' set done = 1;
open cur;
create temporary table eventIndex (
event_id int unsigned not null,
start datetime not null,
end datetime not null,
primary key (event_id,start)
);
# Grab all the events
repeat
fetch cur into pk,s,e,f,i,w;
if not done then
#If we've got recurring data
if !isnull(f) then
# Start creating recurrances
set recurrance_start = s;
while recurrance_start<e do
# Check for Exceptions
set exception_start = null;
set exception_end = null;
select start,end into exception_start,exception_end from event_exceptions
where event_id=pk and original_start=recurrance_start;
if !isnull(exception_start)
then
set r_start = exception_start;
set exception_end = end;
else
set r_start = recurrance_start;
set recurrance_end = concat_ws(' ',date(recurrance_start),time(e));
end if;
# Only create the recurrance if it falls within the range
if r_start between rangeStart and rangeEnd then
insert eventIndex values(pk,r_start,recurrance_end);
end if;
# Increment the recurrances
# Figure out what kind of intervals we're incrementing by
case f
when 'daily' then set recurrance_start = recurrance_start + interval i day;
when 'weekly' then set recurrance_start = recurrance_start + interval i week;
when 'monthly' then set recurrance_start = recurrance_start + interval i month;
when 'yearly' then set recurrance_start = recurrance_start + interval i year;
end case;
end while;
# Otherwise, just insert one row
else insert eventIndex values(pk,s,e);
end if;
end if;
until done end repeat;
close cur;
select * from eventIndex;
drop temporary table eventIndex;
end;
//
delimiter ;
Posted: Thu Apr 19, 2007 7:47 pm
by Luke
Ambush and others... what do you think of inghamn's method? Like it? I am still reading the rfc. It's freakin long and I don't have a whole lot of free time unfortunately.

Posted: Thu Apr 19, 2007 7:49 pm
by Ambush Commander
I've never liked mucking around with stored procedures, so I can't much help here.
Maybe I'll take a look at the RFC and figure out which segments are most useful. The way I do things like this is I print out a hard paper copy, and then read.
Posted: Thu Apr 19, 2007 7:59 pm
by Luke
EDIT: I don't like mucking around with stored procedures either
yes I do (EDIT: like to read a printed version) too. I've got 75 pages printed out and I've read the majority of it. but if you'd like to skip right to the relevant part, it starts on page 39. Here's the RFC:
http://www.ietf.org/rfc/rfc2445.txt
At this point, I've got two tables (well two that are relevant to recurring events). They look something like:
(ignore the data types / storage engines / etc. for now, I just put them like they are to get them in there... I'll fine-tune when I know for sure this is what I want to do)
events
Code: Select all
CREATE TABLE `events` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(100) NOT NULL,
`location` varchar(255) default NULL,
`url` varchar(255) default NULL,
`start_date` date default NULL,
`start_time` time default NULL,
`end_date` date default NULL,
`end_time` time default NULL,
`all_day` tinyint(1) NOT NULL default '0',
`description` text,
`recurring_id` int(11) NOT NULL default '-1',
PRIMARY KEY (`id`),
KEY `recurring_id` (`recurring_id`)
) ENGINE = innodb;
events_recurring
Code: Select all
CREATE TABLE `events_recurring` (
`frequency` VARCHAR( 255 ) NOT NULL ,
`until` DATETIME NOT NULL ,
`count` INT NOT NULL ,
`interval` INT NOT NULL ,
`bysecond` VARCHAR( 255 ) NOT NULL ,
`byminute` VARCHAR( 255 ) NOT NULL ,
`byhour` VARCHAR( 255 ) NOT NULL ,
`byday` VARCHAR( 255 ) NOT NULL ,
`bymonthday` VARCHAR( 255 ) NOT NULL ,
`byyearday` VARCHAR( 255 ) NOT NULL ,
`byweekno` VARCHAR( 255 ) NOT NULL ,
`bymonth` VARCHAR( 255 ) NOT NULL ,
`bysetpos` VARCHAR( 255 ) NOT NULL ,
`wkst` VARCHAR( 255 ) NOT NULL
) ENGINE = innodb;
Getting close....
Posted: Fri Apr 20, 2007 7:53 am
by inghamn
So, since my last post, I've been tacling the incrementing problem, as well as trying out alternatives. It seemed to me there were two ways of approaching this....
1. A crontab style mask, loop through incrementing by days, until you hit the next day that fits the mask.
or
2. try and describe the recurrance, more iCal RFC style, so that you could come up with an interval to add to the date.
In the code I posted, I was working along approach 2, the iCal RFC style. What got very hard, very fast was describing the interval in a way that would support something like: "the second to last week of every other month". Right now, I think if I can support that case, all the rest will be covered. So since then, I tried out approach 1.
Crontab is fairly descriptive, although it doesn't support week numbers, but that could be added. Looping through days until hitting the next occurance does work. (And I know you're not supposed to optimize until it really is a problem, so I'm living with the loop.) But then I realized that the values I needed in crontab sytax really ended up being easier to work with in the iCal RFC syntax.
So...I'm now on the third iteration of this. Still using the stored procedure and just focusing on the 'incrementing the recurrence' portion. Since I'm not really worried about secondly, minutely, hourly, I can ignore a lot of the RFC comlexity.
Here are the values from rrule that I'm storing in the database.
Freq (day,week,month,year)
Interval (int) examples: 1,2
Weeknum (int) examples: -2,1
ByDay set(1,2,3,4,5,6,7)
MonthDayNum int exaples: 15,-10
Using those fields in my Events table has let me use the same logic to compare each day in the loop until I hit a recurrence that works. I think I've boiled it down to 4 comparisons.
1. Is it $Interval $Freq's away from the original date?
2. Is the weekday in the set of $ByDay?
3. Is it the $weeknum of the month?
4. Is it the $monthDayNum of the month?
Comparisons 2-4 only have to occur if those fields are not null. And the only really tricky one is coming up with the week number (positive and negative) of a given date.
Anyway, today, my goal is to implement all 4 of these in the stored procedure and see it work.
Oh, and I can totally understand the aversion to stored procedures. For that matter all this logic could go into PHP, no problem. RIght now, it just feels like a data integrity thing, not something I want to pollute my php Events class with. But I may feel different in a week, who knows?
Core problem?
Posted: Fri Apr 20, 2007 8:55 am
by inghamn
What's the algorithm to explode recurring events?
In any case, whether you store the rrule and explode the events on select, or you take the $_POST, explode the events and put them all into the database,....you still have to explode the events.
I am still working on an algorithm to explode the recurring events. Now you know, people *have* already solved this before, otherwise we wouldn't have any caledar software based on iCal.
Has anyone seen the algorithm or code that other software uses for this? I haven't found anything quite yet. Google's not showing me much love.
RRule Iterator
Posted: Wed Apr 25, 2007 11:00 am
by inghamn
I've begun work on a Recurrence Iterator class in PHP. I have not been able to find one out there so far. As much as I can, I've tried to grok the code from other projects
PHP: phpicalendar, phpMyCalendar, WebCalendar, Xaraya
Java: recurrence
Python: python-dateutil
All of these go the route of generating all the recurrences within a range - at least in memory, even if they don't save them out to the database. All of these are probably perfectly good wheels. It's worth taking a look at phpicalendar, at least...
That said, I think I'm really wanting skis, instead of wheels :) Rather than trying to explode all of the recurrences, I'm working on iterating over them. The class I'm working on just figures out what the next recurrence would be. Unfortunately, this approach has meant a different set of logic than the above mentioned packages.
Because I've already got an events class, this lets me do stuff like:
Code: Select all
foreach($event->getRecurrences($startDate) as $recurrence)
{
if ($recurrence->endDate >= $endDate) break;
# Now I can show this recurrence
}
In the past two days, I've made some progress, I've got DAILY, and WEEKLY working. About to try again with MONTHLY, but it should work.
The code's now over 100 lines, but I can post it on request.
Posted: Wed Apr 25, 2007 11:05 am
by Luke
I'd love to see it.
Final Answer
Posted: Fri Apr 27, 2007 9:17 am
by inghamn
Okay, after abandoning the Itertator idea (for reasons I won't go into right now) I've dug into the code from phpicalendar. Their overall idea is to take an rrule and return an array of timestamps. As of 5 minutes ago, I have a solution working in my project where I've
heavily borrowed from their code.
I still have the two database tables, Events and EventExceptions. I now store the rrule in the Events table, but use PHP to calculate all the recurrences of that event. In my case, the framework I've been writing gives me an Events class to do this in, so I just have to write a function for that class.
Right now, I'm only supporting a small subset of the RRULE in the ical spec. Credit where credit's due: this code is from phpicalendar's ical_parser. With a bunch of the stuff I don't need stripped out, and modified to work with my database, instead of ical files.
I'm happy with this solution so far, and am moving on to adding this to my Events class in the framework. And then on to tying it into my views. Hopefully this is helpful to you as well.
The PHP code clocks in over 200 lines
Code: Select all
create table events (
id int unsigned not null primary key auto_increment,
startDate date not null,
startTime time,
endDate date,
endTime time,
allDayEvent tinyint(1) unsigned,
rrule varchar(128),
summary varchar(128) not null,
description text,
calendar_id int unsigned not null,
location_id int unsigned,
user_id int unsigned not null,
foreign key (calendar_id) references calendars(id),
foreign key (location_id) references locations(id),
foreign key (user_id) references users(id)
) engine=InnoDB;
create table event_exceptions (
event_id int unsigned not null,
original_start date not null,
start datetime not null,
end datetime not null,
primary key (event_id,original_start),
foreign key (event_id) references events(id)
) engine=InnoDB;
Code: Select all
$event_startDate = '2006-1-1';
$event_startTime = '08:00:00';
$event_endDate = '2006-4-1';
$event_endTime = '17:00:00';
$range_start = strtotime('2006-2-1');
$range_end = strtotime('2006-2-15');
$rrule = "FREQ=WEEKLY;INTERVAL=1;BYDAY=WE";
$recur_data = getRecursionData($event_startDate,$event_endDate,$event_startTime,$event_endTime,$range_start,$range_end,$rrule);
function getRecursionData ($startDate,$endDate=null,$startTime=null,$endTime=null,$start_range_time,$end_range_time,$rrule)
{
$start_date_time = strtotime("$startDate $startTime");
$end_date_time = $endDate ? strtotime("$endDate $endTime") : $end_range_time;
# We need an array of all the RRULES
$recur_data = array();
$rrule_array = array();
$rrule = explode(';',$rrule);
foreach($rrule as $rule)
{
list($key,$value) = explode('=',$rule);
switch ($key)
{
case 'FREQ':
switch ($value)
{
case 'YEARLY': $freq_type = 'year'; break;
case 'MONTHLY': $freq_type = 'month'; break;
case 'WEEKLY': $freq_type = 'week'; break;
case 'DAILY': $freq_type = 'day'; break;
default:
}
break;
case 'INTERVAL':
$interval = $value;
break;
case 'BYDAY':
$byday = split(',',$value);
break;
case 'BYMONTHDAY':
$bymonthday = split(',',$value);
break;
case 'BYMONTH':
$bymonth = split(',',$value);
break;
}
}
// If the $end_range_time is less than the $start_date_time, or $start_range_time is greater
// than $end_date_time, we may as well forget the whole thing
// It doesn't do us any good to spend time adding data we aren't even looking at
// this will prevent the year view from taking way longer than it needs to
if ($end_range_time >= $start_date_time && $start_range_time <= $end_date_time)
{
if ($start_range_time < $start_date_time) { $start_range_time = $start_date_time; }
if ($end_range_time > $end_date_time) { $end_range_time = $end_date_time; }
$next_range_time = $start_range_time;
while($next_range_time <= $end_range_time)
{
# What is the interval between $next_range_time and $start_date_time
$compare = $freq_type.'Compare';
$diff = $compare($next_range_time,$start_date_time);
if ($diff % $interval == 0)
{
switch ($freq_type)
{
case 'day':
$recur_data[] = $next_range_time;
break;
case 'week':
# Get the sunday of the current week
$d = date('w',$next_range_time);
$sunday = strtotime("-$d days",$next_range_time);
if (!isset($byday))
{
# Use the day of the week from $start_date_time
$byday = array(strtoupper(substr(date('l',$start_date_time),0,2)));
}
foreach($byday as $day)
{
# strtotime needs at least three letters for the day
# RRULES only use two letters
$next_date_time = strtotime(two2threeCharDays($day),$sunday+(12*60*60));
# Reset the $next_range_time to the first instance of the week
if ($next_date_time < $next_range_time) { $next_range_time = $next_date_time; }
$recur_data[] = $next_date_time;
}
break;
case 'month':
if (!isset($bymonth)) { $bymonth = array(1,2,3,4,5,6,7,8,9,10,11,12); }
# Go to the first day of the month
$d = getdate($next_range_time);
$next_range_time = strtotime("$d[year]-$d[mon]-1");
if (isset($bymonthday))
{
foreach($bymonthday as $day)
{
# Convert the negative monthdays into the actual daynum
if ($day < 0) { $day = date('t',$next_range_time) + $day + 1; }
if (checkdate($d['mon'],$day,$d['year']))
{
$recur_data[] = mktime(0,0,0,$d['mon'],$day,$d['year']);
}
}
}
elseif (isset($byday))
{
foreach($byday as $day)
{
$nth = substr($day,0,-2);
$day = substr($day,-2);
$day_num = two2threeCharDays($day,false);
$day = two2threeCharDays($day);
if ($nth < 0)
{
$last_day = date('t',$next_range_time);
$next_range_time = strtotime(date("Y-m-$last_day",$next_range_time));
$last = (date('w',$next_range_time) == $day_num) ? '' : 'last ';
$next_range_time = strtotime("$last$day",$next_range_time) - $nth * 604800;
$month = date('m',$next_range_time);
if (in_array(date('m',$next_range_time),$bymonth))
{
$recur_data[] = $next_range_time;
}
# Reset to the start of the month
$next_range_time = strtotime(date('Y-m-1',$next_range_time));
}
else
{
$next_date_time = strtotime($day,$next_range_time) + $nth * 604800;
if (in_array(date('m',$next_date_time),$bymonth))
{
$recur_data[] = $next_date_time;
}
}
}
}
break;
}
}
$next_range_time = strtotime("+$interval $freq_type",$next_range_time);
}
}
# Recur_data now has dates set.
# We still need to add Time information from the event
$recurringEvents = array();
foreach($recur_data as $key=>$recurrance)
{
$date = date('Y-m-d',$recurrance);
# Here's where you'd want to swap in an exception for this date
# If there's a matching event_id and $date in the EventsExceptions table
$recurringEvents[$date]['start'] = strtotime("$date $startTime");
$recurringEvents[$date]['end'] = strtotime("$date $endTime");
}
return $recurringEvents;
}
# Returns the number of weeks between two timestamps
function weekCompare($now, $then)
{
# Get the timestamps for the sundays
$d = date('w',$now);
$sunday_now = strtotime("-$d days",$now);
$d = date('w',$then);
$sunday_then = strtotime("-$d days",$then);
return round(($sunday_now - $sunday_then)/(60*60*24*7));
}
# Returns the nuimber of days between two timestamps
function dayCompare($now, $then) { return round(((($now-$then)/60)/60)/24); }
# Returns the number of months between two datse
function monthCompare($now, $then)
{
$now = getdate($now);
$then = getdate($then);
$years = $now['year'] - $then['year'];
$months = $now['mon'] - $then['mon'];
if ($now['mon'] < $then['mon'])
{
$years--;
$months = ($months + 12) % 12;
}
return ($years * 12) + $months;
}
// takes iCalendar 2 day format and makes it into 3 characters
// if $txt is true, it returns the 3 letters, otherwise it returns the
// integer of that day; 0=Sun, 1=Mon, etc.
function two2threeCharDays($day, $txt=true)
{
switch($day)
{
case 'SU': return ($txt ? 'sun' : '0');
case 'MO': return ($txt ? 'mon' : '1');
case 'TU': return ($txt ? 'tue' : '2');
case 'WE': return ($txt ? 'wed' : '3');
case 'TH': return ($txt ? 'thu' : '4');
case 'FR': return ($txt ? 'fri' : '5');
case 'SA': return ($txt ? 'sat' : '6');
}
}
Posted: Thu May 17, 2007 6:06 am
by MySchizoBuddy
are you going to add exclusion dates for reoccurring events
Posted: Thu May 17, 2007 7:18 am
by inghamn
True, I left that code out in the example. Exceptions are stored in the event_exceptions table. Then, down at the very end of the function getRecursionData, you would do a put the exception into $recurringEvents[] instead of the recurrence.
Exceptions are stored using the startDate of the recurrence. Or rather, what the startDate of the recurrence would have been, if it hadn't been excepted.
Posted: Thu May 17, 2007 2:44 pm
by MySchizoBuddy
is it possible for you to post the exceptions code as well
Do u have a demo, that i can look at. thanks
since u are implementing a subset of the RRULE. I'm assuming that you don't allow selection of weekdays if reoccurring by Week. or First tuesday of every month.
Posted: Thu May 17, 2007 3:59 pm
by inghamn
MySchizoBuddy wrote:is it possible for you to post the exceptions code as well
Do u have a demo, that i can look at. thanks
The modfied, previously mentioned foreach, that I use is:
Code: Select all
foreach($recur_data as $key=>$recurrence)
{
$date = date('Y-m-d',$recurrence);
# If there's not an exception for this recurrence, set the start and end
$r = new EventRecurrence($this,$date);
if (!$r->isException())
{
$r->setStart("$date {$this->startTime}");
$r->setEnd("$date {$this->endTime}");
}
$recurringEvents[] = $r;
}
The code I have is implemented inside my project's Event class. Which represents, Active Record style, a row from the events table. I have also written an EventRecurrence class that represents a recurrence, or, if there's an exception for that event on that date, it represents the exception from the event_exceptions table. If you modify and save an EventRecurrence, it goes into the event_exceptions table.
The Event class is over 600 lines but the code above is essentially the function that handles recurrences. I'll post the EventRecurrence class below.
MySchizoBuddy wrote:since u are implementing a subset of the RRULE. I'm assuming that you don't allow selection of weekdays if reoccurring by Week. or First tuesday of every month.
While my current html form only allows a limited set of options for the user, what gets put into the database is the raw RRULE string. To save some complexity I didn't bring in code to parse secondly, minutely, or hourly FREQs, though. But daily, weekly, monthly FREQs should be parsed correctly from the database, indepentantly of what HTML form I give the users.
I believe from my reading of the icalendar RFC that BYDAY is perfectly valid with WEEKLY. An event could occur weekly but only on mondays and tuesdays.
RRULE: FREQ=WEEKLY;BYDAY=MO,TU
Similarly, you could have an event that occurs monthly on the 1rst Tuesday and 2nd to Last Wednesday.
RRULE: FREQ=MONTHLY;BYDAY=1TU,-2WE
Code: Select all
class EventRecurrence extends ActiveRecord
{
private $event_id;
private $original_start;
private $start;
private $end;
private $event;
private $type;
public function __construct($event,$original_start)
{
global $PDO;
$this->event_id = $event->getId();
$this->event = $event;
$this->original_start = $original_start;
if ($this->event->getStartDate() == $this->original_start) { $this->type = 'Event'; }
else { $this->type = 'Recurrence'; }
# Load any exception data for this recurrence
$query = $PDO->prepare("select start,end from event_exceptions where event_id=? and original_start=?");
$query->execute(array($this->event_id,$this->original_start));
$result = $query->fetchAll();
if (count($result))
{
$this->start = $result[0]['start'];
$this->end = $result[0]['end'];
$this->type = 'Exception';
}
}
public function save()
{
global $PDO;
if ($this->isException())
{
$sql = "insert event_exceptions values($this->event_id,'$this->original_start','$this->start','$this->end')
on duplicate key update start='$this->start',end='$this->end'";
$query = $PDO->prepare($sql);
$query->execute();
}
}
public function isEvent() { return $this->type == 'Event'; }
public function isRecurrence() { return $this->type == 'Recurrence'; }
public function isException() { return $this->type == 'Exception'; }
/**
* Generic Getters
*/
public function getEvent_id() { return $this->event_id; }
public function getOriginal_start() { return $this->original_start; }
public function getStart($format=null)
{
if ($format && $this->start!=0) return strftime($format,strtotime($this->start));
else return $this->start;
}
public function getEnd($format=null)
{
if ($format && $this->end!=0) return strftime($format,strtotime($this->end));
else return $this->end;
}
public function getEvent() { return $this->event; }
public function getType() { return $this->type; }
/**
* Generic Setters
*/
public function setStart($datetime) { $this->start = is_array($datetime) ? $this->dateArrayToString($datetime) : $datetime; }
public function setEnd($datetime) { $this->end = is_array($datetime) ? $this->dateArrayToString($datetime) : $datetime; }
}
Posted: Thu May 17, 2007 6:08 pm
by MySchizoBuddy
Super. thanks. One last question.
If i set a reoccurring event in may that will reoccur every month till december. Now if my users are looking at November. how do i find out which of the numerous reoccurring events, will occur in November.
From the looks of it. I will have to parse all my reoccurring events, blow them up, and save only the ones that occur in November and discard the rest.
WebCalendar reoccurring code is pretty thorough, and feature complete.
If u want you can have a look at it. get the 1.1 development version
http://www.k5n.us/webcalendar.php
it has a RptEvent.class (includes/Classes) file plus a functions.php (includes folder) file where all the functions are stored.
the function of interest is this
Code: Select all
/**
* Returns all the dates a specific event will fall on accounting for the repeating.
*
* Any event with no end will be assigned one.
*
* @param int $date Initial date in raw format
* @param string $rpt_type Repeating type as stored in the database
* @param int $interval Interval of repetition
* @param array $ByMonth Array of ByMonth values
* @param array $ByWeekNo Array of ByWeekNo values
* @param array $ByYearDay Array of ByYearDay values
* @param array $ByMonthDay Array of ByMonthDay values
* @param array $ByDay Array of ByDay values
* @param array $BySetPos Array of BySetPos values
* @param int $Count Max number of events to return
* @param string $Until Last day of repeat
* @param string $Wkst First day of week ('MO' is default)
* @param array $ex_days Array of exception dates for this event in YYYYMMDD format
* @param array $inc_days Array of inclusion dates for this event in YYYYMMDD format
* @param int $jump Date to short cycle loop counts to, also makes output YYYYMMDD
*
* @return array Array of dates (in UNIX time format)
*/
function get_all_dates ( $date, $rpt_type, $interval=1, $ByMonth ='',
$ByWeekNo ='', $ByYearDay ='', $ByMonthDay ='', $ByDay ='',
$BySetPos ='', $Count=999,
$Until= NULL, $Wkst= 'MO', $ex_days='', $inc_days='', $jump='' )