public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null)
    {
        $newChildren = [];
        $recurringEvents = [];

        if (!$timeZone) {
            $timeZone = new DateTimeZone('UTC');
        }

        $stripTimezones = function (Component $component) use ($timeZone, &$stripTimezones) {
            foreach ($component->children() as $componentChild) {
                if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
                    $dt = $componentChild->getDateTimes($timeZone);
                    // We only need to update the first timezone, because
                    // setDateTimes will match all other timezones to the
                    // first.
                    $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
                    $componentChild->setDateTimes($dt);
                } elseif ($componentChild instanceof Component) {
                    $stripTimezones($componentChild);
                }
            }

            return $component;
        };

        foreach ($this->children() as $child) {
            if ($child instanceof Property && 'PRODID' !== $child->name) {
                // We explictly want to ignore PRODID, because we want to
                // overwrite it with our own.
                $newChildren[] = clone $child;
            } elseif ($child instanceof Component && 'VTIMEZONE' !== $child->name) {
                // We're also stripping all VTIMEZONE objects because we're
                // converting everything to UTC.
                if ('VEVENT' === $child->name && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
                    // Handle these a bit later.
                    $uid = (string) $child->UID;
                    if (!$uid) {
                        throw new InvalidDataException('Every VEVENT object must have a UID property');
                    }
                    if (isset($recurringEvents[$uid])) {
                        $recurringEvents[$uid][] = clone $child;
                    } else {
                        $recurringEvents[$uid] = [clone $child];
                    }
                } elseif ('VEVENT' === $child->name && $child->isInTimeRange($start, $end)) {
                    $newChildren[] = $stripTimezones(clone $child);
                }
            }
        }

        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) {
                    $newChildren[] = $stripTimezones($it->getEventObject());
                }
                $it->next();
            }
        }

        return new self($newChildren);
    }
public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null)
    {
        $newChildren = [];
        $recurringEvents = [];

        if (!$timeZone) {
            $timeZone = new DateTimeZone('UTC');
        }

        $stripTimezones = function (Component $component) use ($timeZone, &$stripTimezones) {
            foreach ($component->children() as $componentChild) {
                if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
                    $dt = $componentChild->getDateTimes($timeZone);
                    // We only need to update the first timezone, because
                    // setDateTimes will match all other timezones to the
                    // first.
                    $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
                    $componentChild->setDateTimes($dt);
                } elseif ($componentChild instanceof Component) {
                    $stripTimezones($componentChild);
                }
            }

            return $component;
        };

        foreach ($this->children() as $child) {
            if ($child instanceof Property && 'PRODID' !== $child->name) {
                // We explictly want to ignore PRODID, because we want to
                // overwrite it with our own.
                $newChildren[] = clone $child;
            } elseif ($child instanceof Component && 'VTIMEZONE' !== $child->name) {
                // We're also stripping all VTIMEZONE objects because we're
                // converting everything to UTC.
                if ('VEVENT' === $child->name && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
                    // Handle these a bit later.
                    $uid = (string) $child->UID;
                    if (!$uid) {
                        throw new InvalidDataException('Every VEVENT object must have a UID property');
                    }
                    if (isset($recurringEvents[$uid])) {
                        $recurringEvents[$uid][] = clone $child;
                    } else {
                        $recurringEvents[$uid] = [clone $child];
                    }
                } elseif ('VEVENT' === $child->name && $child->isInTimeRange($start, $end)) {
                    $newChildren[] = $stripTimezones(clone $child);
                }
            }
        }

        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) {
                    $newChildren[] = $stripTimezones($it->getEventObject());
                }
                $it->next();
            }
        }

        return new self($newChildren);
    }
public function expand ( DateTimeInterface $ start , DateTimeInterface $ end , DateTimeZone $ timeZone = null ) { $ newChildren = [ ] ; $ recurringEvents = [ ] ; if ( ! $ timeZone ) { $ timeZone = new DateTimeZone ( 'UTC' ) ; } $ stripTimezones = function ( Component $ component ) use ( $ timeZone , & $ stripTimezones ) { foreach ( $ component -> children ( ) as $ componentChild ) { if ( $ componentChild instanceof Property \ ICalendar \ DateTime && $ componentChild -> hasTime ( ) ) { $ dt = $ componentChild -> getDateTimes ( $ timeZone ) ; // We only need to update the first timezone, because // setDateTimes will match all other timezones to the // first. $ dt [ 0 ] = $ dt [ 0 ] -> setTimeZone ( new DateTimeZone ( 'UTC' ) ) ; $ componentChild -> setDateTimes ( $ dt ) ; } elseif ( $ componentChild instanceof Component ) { $ stripTimezones ( $ componentChild ) ; } } return $ component ; } ; foreach ( $ this -> children ( ) as $ child ) { if ( $ child instanceof Property && 'PRODID' !== $ child -> name ) { // We explictly want to ignore PRODID, because we want to // overwrite it with our own. $ newChildren [ ] = clone $ child ; } elseif ( $ child instanceof Component && 'VTIMEZONE' !== $ child -> name ) { // We're also stripping all VTIMEZONE objects because we're // converting everything to UTC. if ( 'VEVENT' === $ child -> name && ( isset ( $ child -> { 'RECURRENCE-ID' } ) || isset ( $ child -> RRULE ) || isset ( $ child -> RDATE ) ) ) { // Handle these a bit later. $ uid = ( string ) $ child -> UID ; if ( ! $ uid ) { throw new InvalidDataException ( 'Every VEVENT object must have a UID property' ) ; } if ( isset ( $ recurringEvents [ $ uid ] ) ) { $ recurringEvents [ $ uid ] [ ] = clone $ child ; } else { $ recurringEvents [ $ uid ] = [ clone $ child ] ; } } elseif ( 'VEVENT' === $ child -> name && $ child -> isInTimeRange ( $ start , $ end ) ) { $ newChildren [ ] = $ stripTimezones ( clone $ child ) ; } } } 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 ) { $ newChildren [ ] = $ stripTimezones ( $ it -> getEventObject ( ) ) ; } $ it -> next ( ) ; } } return new self ( $ newChildren ) ; }
Expand all events in this VCalendar object and return a new VCalendar
with the expanded events.

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 its 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.

@param DateTimeInterface $start
@param DateTimeInterface $end
@param DateTimeZone      $timeZone reference timezone for floating dates and
times

@return VCalendar
Expand all events in this VCalendar object and return a new VCalendar with the expanded events .