/**
  * Validates if a component matches the given time range.
  *
  * This is all based on the rules specified in rfc4791, which are quite
  * complex.
  *
  * @param Sabre_VObject_Node $component
  * @param DateTime $start
  * @param DateTime $end
  * @return bool
  */
 protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end)
 {
     if (is_null($start)) {
         $start = new DateTime('1900-01-01');
     }
     if (is_null($end)) {
         $end = new DateTime('3000-01-01');
     }
     switch ($component->name) {
         case 'VEVENT':
         case 'VTODO':
         case 'VJOURNAL':
             return $component->isInTimeRange($start, $end);
         case 'VALARM':
             // If the valarm is wrapped in a recurring event, we need to
             // expand the recursions, and validate each.
             //
             // Our datamodel doesn't easily allow us to do this straight
             // in the VALARM component code, so this is a hack, and an
             // expensive one too.
             if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
                 // Fire up the iterator!
                 $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string) $component->parent->UID);
                 while ($it->valid()) {
                     $expandedEvent = $it->getEventObject();
                     // We need to check from these expanded alarms, which
                     // one is the first to trigger. Based on this, we can
                     // determine if we can 'give up' expanding events.
                     $firstAlarm = null;
                     foreach ($expandedEvent->VALARM as $expandedAlarm) {
                         $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
                         if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
                             $firstAlarm = $effectiveTrigger;
                         }
                         if ($expandedAlarm->isInTimeRange($start, $end)) {
                             return true;
                         }
                     }
                     if ($firstAlarm > $end) {
                         return false;
                     }
                     $it->next();
                 }
                 return false;
             } else {
                 return $component->isInTimeRange($start, $end);
             }
         case 'VFREEBUSY':
             throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
         case 'COMPLETED':
         case 'CREATED':
         case 'DTEND':
         case 'DTSTAMP':
         case 'DTSTART':
         case 'DUE':
         case 'LAST-MODIFIED':
             return $start <= $component->getDateTime() && $end >= $component->getDateTime();
         default:
             throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
     }
 }
 /**
  * Validates if a component matches the given time range.
  *
  * This is all based on the rules specified in rfc4791, which are quite
  * complex.
  *
  * @param Sabre_VObject_Node $component
  * @param DateTime $start
  * @param DateTime $end
  * @return bool
  */
 protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end)
 {
     if (is_null($start)) {
         $start = new DateTime('1900-01-01');
     }
     if (is_null($end)) {
         $end = new DateTime('3000-01-01');
     }
     switch ($component->name) {
         case 'VEVENT':
         case 'VTODO':
         case 'VJOURNAL':
             return $component->isInTimeRange($start, $end);
         case 'VALARM':
             // If the valarm is wrapped in a recurring event, we need to
             // expand the recursions, and validate each.
             //
             // Our datamodel doesn't easily allow us to do this straight
             // in the VALARM component code, so this is a hack, and an
             // expensive one too.
             if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
                 // Fire up the iterator!
                 $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string) $component->parent->UID);
                 while ($it->valid()) {
                     $expandedEvent = $it->getEventObject();
                     // We need to check from these expanded alarms, which
                     // one is the first to trigger. Based on this, we can
                     // determine if we can 'give up' expanding events.
                     $firstAlarm = null;
                     foreach ($expandedEvent->VALARM as $expandedAlarm) {
                         $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
                         if ($expandedAlarm->isInTimeRange($start, $end)) {
                             return true;
                         }
                         if ((string) $expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
                             // This is an alarm with a non-relative trigger
                             // time, likely created by a buggy client. The
                             // implication is that every alarm in this
                             // recurring event trigger at the exact same
                             // time. It doesn't make sense to traverse
                             // further.
                         } else {
                             // We store the first alarm as a means to
                             // figure out when we can stop traversing.
                             if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
                                 $firstAlarm = $effectiveTrigger;
                             }
                         }
                     }
                     if (is_null($firstAlarm)) {
                         // No alarm was found.
                         //
                         // Or technically: No alarm that will change for
                         // every instance of the recurrence was found,
                         // which means we can assume there was no match.
                         return false;
                     }
                     if ($firstAlarm > $end) {
                         return false;
                     }
                     $it->next();
                 }
                 return false;
             } else {
                 return $component->isInTimeRange($start, $end);
             }
         case 'VFREEBUSY':
             throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
         case 'COMPLETED':
         case 'CREATED':
         case 'DTEND':
         case 'DTSTAMP':
         case 'DTSTART':
         case 'DUE':
         case 'LAST-MODIFIED':
             return $start <= $component->getDateTime() && $end >= $component->getDateTime();
         default:
             throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
     }
 }
 /**
  * Validates if a component matches the given time range.
  *
  * This is all based on the rules specified in rfc4791, which are quite
  * complex.
  *
  * @param Sabre_VObject_Node $component
  * @param DateTime $start
  * @param DateTime $end
  * @return bool
  */
 protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end)
 {
     if (is_null($start)) {
         $start = new DateTime('1900-01-01');
     }
     if (is_null($end)) {
         $end = new DateTime('3000-01-01');
     }
     switch ($component->name) {
         case 'VEVENT':
         case 'VTODO':
         case 'VJOURNAL':
             return $component->isInTimeRange($start, $end);
         case 'VFREEBUSY':
             throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
         case 'VALARM':
             $trigger = $component->TRIGGER;
             if (!isset($trigger['TYPE']) || strtoupper($trigger['TYPE']) === 'DURATION') {
                 $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($component->TRIGGER);
                 $related = isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END' ? 'END' : 'START';
                 $parentComponent = $component->parent;
                 if ($related === 'START') {
                     $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
                     $effectiveTrigger->add($triggerDuration);
                 } else {
                     if ($parentComponent->name === 'VTODO') {
                         $endProp = 'DUE';
                     } elseif ($parentComponent->name === 'VEVENT') {
                         $endProp = 'DTEND';
                     } else {
                         throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
                     }
                     if (isset($parentComponent->{$endProp})) {
                         $effectiveTrigger = clone $parentComponent->{$endProp}->getDateTime();
                         $effectiveTrigger->add($triggerDuration);
                     } elseif (isset($parentComponent->DURATION)) {
                         $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
                         $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION);
                         $effectiveTrigger->add($duration);
                         $effectiveTrigger->add($triggerDuration);
                     } else {
                         $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
                         $effectiveTrigger->add($triggerDuration);
                     }
                 }
             } else {
                 $effectiveTrigger = $trigger->getDateTime();
             }
             if (isset($component->DURATION)) {
                 $duration = Sabre_VObject_DateTimeParser::parseDuration($component->DURATION);
                 $repeat = (string) $component->repeat;
                 if (!$repeat) {
                     $repeat = 1;
                 }
                 $period = new DatePeriod($effectiveTrigger, $duration, (int) $repeat);
                 foreach ($period as $occurrence) {
                     if ($start <= $occurrence && $end > $occurrence) {
                         return true;
                     }
                 }
                 return false;
             } else {
                 return $start <= $effectiveTrigger && $end > $effectiveTrigger;
             }
             break;
         case 'COMPLETED':
         case 'CREATED':
         case 'DTEND':
         case 'DTSTAMP':
         case 'DTSTART':
         case 'DUE':
         case 'LAST-MODIFIED':
             return $start <= $component->getDateTime() && $end >= $component->getDateTime();
         default:
             throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
     }
 }