/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param DateTime $start * @param DateTime $end * @return bool */ public function isInTimeRange(DateTime $start, DateTime $end) { if ($this->RRULE) { $it = new Sabre_VObject_RecurrenceIterator($this); $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the // recurrence instance exceeded the start of the requested // time-range. // // If the starttime of the recurrence did not exceed the // end of the time range as well, we have a match. return $it->getDTStart() < $end && $it->getDTEnd() > $start; } $effectiveStart = $this->DTSTART->getDateTime(); if (isset($this->DTEND)) { // The DTEND property is considered non inclusive. So for a 3 day // event in july, dtstart and dtend would have to be July 1st and // July 4th respectively. // // See: // http://tools.ietf.org/html/rfc5545#page-54 $effectiveEnd = $this->DTEND->getDateTime(); } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->add(Sabre_VObject_DateTimeParser::parseDuration($this->DURATION)); } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { $effectiveEnd = clone $effectiveStart; } return $start <= $effectiveEnd && $end > $effectiveStart; }
/** * Returns true or false depending on if the event falls in the specified * time-range. This is used for filtering purposes. * * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * * @param DateTime $start * @param DateTime $end * @return bool */ public function isInTimeRange(DateTime $start, DateTime $end) { if ($this->RRULE) { $it = new Sabre_VObject_RecurrenceIterator($this); $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the // recurrence instance exceeded the start of the requested // time-range. // // If the starttime of the recurrence did not exceed the // end of the time range as well, we have a match. return $it->getDTStart() < $end && $it->getDTEnd() > $start; } $effectiveStart = $this->DTSTART->getDateTime(); if (isset($this->DTEND)) { $effectiveEnd = $this->DTEND->getDateTime(); // If this was an all-day event, we should just increase the // end-date by 1. Otherwise the event will last until the second // the date changed, by increasing this by 1 day the event lasts // all of the last day as well. if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd->modify('+1 day'); } } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->add(Sabre_VObject_DateTimeParser::parseDuration($this->DURATION)); } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { $effectiveEnd = clone $effectiveStart; } return $start <= $effectiveEnd && $end > $effectiveStart; }
/** * Parses the input data and returns a correct VFREEBUSY object, wrapped in * a VCALENDAR. * * @return Sabre_VObject_Component */ public function getResult() { $busyTimes = array(); foreach ($this->objects as $object) { foreach ($object->getBaseComponents() as $component) { switch ($component->name) { case 'VEVENT': $FBTYPE = 'BUSY'; if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') { break; } if (isset($component->STATUS)) { $status = strtoupper($component->STATUS); if ($status === 'CANCELLED') { break; } if ($status === 'TENTATIVE') { $FBTYPE = 'BUSY-TENTATIVE'; } } $times = array(); if ($component->RRULE) { $iterator = new Sabre_VObject_RecurrenceIterator($object, (string) $component->uid); if ($this->start) { $iterator->fastForward($this->start); } $maxRecurrences = 200; while ($iterator->valid() && --$maxRecurrences) { $startTime = $iterator->getDTStart(); if ($this->end && $startTime > $this->end) { break; } $times[] = array($iterator->getDTStart(), $iterator->getDTEnd()); $iterator->next(); } } else { $startTime = $component->DTSTART->getDateTime(); if ($this->end && $startTime > $this->end) { break; } $endTime = null; if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime(); } elseif (isset($component->DURATION)) { $duration = Sabre_VObject_DateTimeParser::parseDuration((string) $component->DURATION); $endTime = clone $startTime; $endTime->add($duration); } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { $endTime = clone $startTime; $endTime->modify('+1 day'); } else { // The event had no duration (0 seconds) break; } $times[] = array($startTime, $endTime); } foreach ($times as $time) { if ($this->end && $time[0] > $this->end) { break; } if ($this->start && $time[1] < $this->start) { break; } $busyTimes[] = array($time[0], $time[1], $FBTYPE); } break; case 'VFREEBUSY': foreach ($component->FREEBUSY as $freebusy) { $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY'; // Skipping intervals marked as 'free' if ($fbType === 'FREE') { continue; } $values = explode(',', $freebusy); foreach ($values as $value) { list($startTime, $endTime) = explode('/', $value); $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime); if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') { $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime->add($duration); } else { $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); } if ($this->start && $this->start > $endTime) { continue; } if ($this->end && $this->end < $startTime) { continue; } $busyTimes[] = array($startTime, $endTime, $fbType); } } break; } } } if ($this->baseObject) { $calendar = $this->baseObject; } else { $calendar = new Sabre_VObject_Component('VCALENDAR'); $calendar->version = '2.0'; $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; $calendar->calscale = 'GREGORIAN'; } $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); $calendar->add($vfreebusy); if ($this->start) { $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); $dtstart->setDateTime($this->start, Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstart); } if ($this->end) { $dtend = new Sabre_VObject_Property_DateTime('DTEND'); $dtend->setDateTime($this->start, Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtend); } $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstamp); foreach ($busyTimes as $busyTime) { $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); $prop = new Sabre_VObject_Property('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')); $prop['FBTYPE'] = $busyTime[2]; $vfreebusy->add($prop); } return $calendar; }
/** * 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 * @return void */ public function expand(DateTime $start, DateTime $end) { $newEvents = array(); foreach ($this->select('VEVENT') as $key => $vevent) { if (isset($vevent->{'RECURRENCE-ID'})) { unset($this->children[$key]); continue; } if (!$vevent->rrule) { unset($this->children[$key]); if ($vevent->isInTimeRange($start, $end)) { $newEvents[] = $vevent; } continue; } $uid = (string) $vevent->uid; if (!$uid) { throw new LogicException('Event did not have a UID!'); } $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid); $it->fastForward($start); while ($it->valid() && $it->getDTStart() < $end) { if ($it->getDTEnd() > $start) { $newEvents[] = $it->getEventObject(); } $it->next(); } unset($this->children[$key]); } foreach ($newEvents as $newEvent) { foreach ($newEvent->children as $child) { if ($child instanceof Sabre_VObject_Property_DateTime && $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) { $child->setDateTime($child->getDateTime(), Sabre_VObject_Property_DateTime::UTC); } } $this->add($newEvent); } // Removing all VTIMEZONE components unset($this->VTIMEZONE); }