/**
  * computes monthly (bymonthday) recurring events and inserts them into given $_recurSet
  *
  * @param Calendar_Model_Event      $_event
  * @param Calendar_Model_Rrule      $_rrule
  * @param array                     $_exceptionRecurIds
  * @param Tinebase_DateTime                 $_from
  * @param Tinebase_DateTime                 $_until
  * @param Tinebase_Record_RecordSet $_recurSet
  * @return void
  */
 protected static function _computeRecurMonthlyByMonthDay($_event, $_rrule, $_exceptionRecurIds, $_from, $_until, $_recurSet)
 {
     $eventInOrganizerTZ = clone $_event;
     $eventInOrganizerTZ->setTimezone($_event->originator_tz);
     // some clients skip the monthday e.g. for yearly rrules
     if (!$_rrule->bymonthday) {
         $_rrule->bymonthday = $eventInOrganizerTZ->dtstart->format('j');
     }
     // NOTE: non existing dates will be discarded (e.g. 31. Feb.)
     //       for correct computations we deal with virtual dates, represented as arrays
     $computationStartDateArray = self::date2array($eventInOrganizerTZ->dtstart);
     // adopt startdate if rrule monthday != dtstart monthday
     // in this case, the first instance is not the base event!
     if ($_rrule->bymonthday != $computationStartDateArray['day']) {
         $computationStartDateArray['day'] = $_rrule->bymonthday;
         $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, -1 * $_rrule->interval);
     }
     $computationEndDate = $_event->rrule_until instanceof DateTime && $_until->isLater($_event->rrule_until) ? $_event->rrule_until : $_until;
     // if dtstart is before $_from, we compute the offset where to start our calculations
     if ($eventInOrganizerTZ->dtstart->isEarlier($_from)) {
         $computationOffsetMonth = self::getMonthDiff($eventInOrganizerTZ->dtend, $_from);
         // NOTE: $computationOffsetMonth must be multiple of interval!
         $computationOffsetMonth = floor($computationOffsetMonth / $_rrule->interval) * $_rrule->interval;
         $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, $computationOffsetMonth - $_rrule->interval);
     }
     $eventLength = $eventInOrganizerTZ->dtstart->diff($eventInOrganizerTZ->dtend);
     $originatorsOriginalDtstart = clone $eventInOrganizerTZ->dtstart;
     while (true) {
         $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, $_rrule->interval);
         $recurEvent = self::cloneEvent($eventInOrganizerTZ);
         $recurEvent->dtstart = self::array2date($computationStartDateArray, $eventInOrganizerTZ->originator_tz);
         // we calculate dtend from the event length, as events during a dst boundary could get dtend less than dtstart otherwise
         $recurEvent->dtend = clone $recurEvent->dtstart;
         $recurEvent->dtend->add($eventLength);
         $recurEvent->setTimezone('UTC');
         if ($computationEndDate->isEarlier($recurEvent->dtstart)) {
             break;
         }
         // skip non existing dates
         if (!Tinebase_DateTime::isDate(self::array2string($computationStartDateArray))) {
             continue;
         }
         // skip events ending before our period.
         // NOTE: such events could be included, cause our offset only calcs months and not seconds
         if ($_from->compare($recurEvent->dtend) >= 0) {
             continue;
         }
         $recurEvent->setRecurId($_event->getId());
         if (!in_array($recurEvent->recurid, $_exceptionRecurIds)) {
             self::addRecurrence($recurEvent, $_recurSet);
         }
     }
 }
 /**
  * returns the original dtstart of a recur series exception event 
  *  -> when the event should have started with no exception
  * 
  * @return Tinebase_DateTime
  */
 public function getOriginalDtStart()
 {
     $origianlDtStart = $this->dtstart instanceof stdClass ? clone $this->dtstart : $this->dtstart;
     if ($this->isRecurException()) {
         if ($this->recurid instanceof DateTime) {
             $origianlDtStart = clone $this->recurid;
         } else {
             if (is_string($this->recurid)) {
                 $origianlDtStartString = substr($this->recurid, -19);
                 if (!Tinebase_DateTime::isDate($origianlDtStartString)) {
                     throw new Tinebase_Exception_InvalidArgument('recurid does not contain a valid original start date');
                 }
                 $origianlDtStart = new Tinebase_DateTime($origianlDtStartString, 'UTC');
             }
         }
     }
     return $origianlDtStart;
 }