/**
* If this calendar object, has events with recurrence rules, this method
* can be used to expand the event into multiple sub-events.
*
* Each event will be stripped from it's recurrence information, and only
* the instances of the event in the specified timerange will be left
* alone.
*
* In addition, this method will cause timezone information to be stripped,
* and normalized to UTC.
*
* This method will alter the VCalendar. This cannot be reversed.
*
* This functionality is specifically used by the CalDAV standard. It is
* possible for clients to request expand events, if they are rather simple
* clients and do not have the possibility to calculate recurrences.
*
* @param DateTime $start
* @param DateTime $end
* @param DateTimeZone $timeZone reference timezone for floating dates and
* times.
* @return void
*/
function expand(DateTime $start, DateTime $end, DateTimeZone $timeZone = null)
{
$newEvents = array();
if (!$timeZone) {
$timeZone = new DateTimeZone('UTC');
}
// An array of events. Events are indexed by UID. Each item in this
// array is a list of one or more events that match the UID.
$recurringEvents = array();
foreach ($this->select('VEVENT') as $key => $vevent) {
$uid = (string) $vevent->UID;
if (!$uid) {
throw new \LogicException('Event did not have a UID!');
}
if (isset($vevent->{'RECURRENCE-ID'}) || isset($vevent->RRULE)) {
if (isset($recurringEvents[$uid])) {
$recurringEvents[$uid][] = $vevent;
} else {
$recurringEvents[$uid] = array($vevent);
}
continue;
}
if (!isset($vevent->RRULE)) {
if ($vevent->isInTimeRange($start, $end)) {
$newEvents[] = $vevent;
}
continue;
}
}
foreach ($recurringEvents as $events) {
try {
$it = new EventIterator($events, $timeZone);
} catch (NoInstancesException $e) {
// This event is recurring, but it doesn't have a single
// instance. We are skipping this event from the output
// entirely.
continue;
}
$it->fastForward($start);
while ($it->valid() && $it->getDTStart() < $end) {
if ($it->getDTEnd() > $start) {
$newEvents[] = $it->getEventObject();
}
$it->next();
}
}
// Wiping out all old VEVENT objects
unset($this->VEVENT);
// Setting all properties to UTC time.
foreach ($newEvents as $newEvent) {
foreach ($newEvent->children as $child) {
if ($child instanceof VObject\Property\ICalendar\DateTime && $child->hasTime()) {
$dt = $child->getDateTimes($timeZone);
// We only need to update the first timezone, because
// setDateTimes will match all other timezones to the
// first.
$dt[0]->setTimeZone(new DateTimeZone('UTC'));
$child->setDateTimes($dt);
}
}
$this->add($newEvent);
}
// Removing all VTIMEZONE components
unset($this->VTIMEZONE);
}