Пример #1
0
 /**
  * select components from calendar on date or selectOption basis
  *
  * Ensure DTSTART is set for every component.
  * No date controls occurs.
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.11.22 - 2012-02-13
  * @param mixed $startY optional, start Year,  default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
  * @param int   $startM optional, start Month, default current Month
  * @param int   $startD optional, start Day,   default current Day
  * @param int   $endY   optional, end   Year,  default $startY
  * @param int   $endY   optional, end   Month, default $startM
  * @param int   $endY   optional, end   Day,   default $startD
  * @param mixed $cType  optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  * @param bool  $flat   optional, FALSE (default) => output : array[Year][Month][Day][]
  *                                TRUE            => output : array[] (ignores split)
  * @param bool  $any    optional, TRUE (default) - select component(-s) that occurs within period
  *                                FALSE          - only component(-s) that starts within period
  * @param bool  $split  optional, TRUE (default) - one component copy every DAY it occurs during the
  *                                                 period (implies flat=FALSE)
  *                                FALSE          - one occurance of component only in output array
  * @return array or FALSE
  */
 function selectComponents($startY = FALSE, $startM = FALSE, $startD = FALSE, $endY = FALSE, $endM = FALSE, $endD = FALSE, $cType = FALSE, $flat = FALSE, $any = TRUE, $split = TRUE)
 {
     /* check  if empty calendar */
     if (0 >= count($this->components)) {
         return FALSE;
     }
     if (is_array($startY)) {
         return $this->selectComponents2($startY);
     }
     /* check default dates */
     if (!$startY) {
         $startY = date('Y');
     }
     if (!$startM) {
         $startM = date('m');
     }
     if (!$startD) {
         $startD = date('d');
     }
     $startDate = mktime(0, 0, 0, $startM, $startD, $startY);
     if (!$endY) {
         $endY = $startY;
     }
     if (!$endM) {
         $endM = $startM;
     }
     if (!$endD) {
         $endD = $startD;
     }
     $endDate = mktime(23, 59, 59, $endM, $endD, $endY);
     //echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br />\n"; $tcnt = 0;// test ###
     /* check component types */
     $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy');
     if (is_array($cType)) {
         foreach ($cType as $cix => $theType) {
             $cType[$cix] = $theType = strtolower($theType);
             if (!in_array($theType, $validTypes)) {
                 $cType[$cix] = 'vevent';
             }
         }
         $cType = array_unique($cType);
     } elseif (!empty($cType)) {
         $cType = strtolower($cType);
         if (!in_array($cType, $validTypes)) {
             $cType = array('vevent');
         } else {
             $cType = array($cType);
         }
     } else {
         $cType = $validTypes;
     }
     if (0 >= count($cType)) {
         $cType = $validTypes;
     }
     if (FALSE === $flat && FALSE === $any) {
         // invalid combination
         $split = FALSE;
     }
     if (TRUE === $flat && TRUE === $split) {
         // invalid combination
         $split = FALSE;
     }
     /* iterate components */
     $result = array();
     foreach ($this->components as $cix => $component) {
         if (empty($component)) {
             continue;
         }
         unset($start);
         /* deselect unvalid type components */
         if (!in_array($component->objName, $cType)) {
             continue;
         }
         $start = $component->getProperty('dtstart');
         /* select due when dtstart is missing */
         if (empty($start) && $component->objName == 'vtodo' && FALSE === ($start = $component->getProperty('due'))) {
             continue;
         }
         if (empty($start)) {
             continue;
         }
         $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE;
         unset($end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat);
         // clean up
         $startWdate = iCalUtilityFunctions::_date2timestamp($start);
         $startDateFormat = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         /* get end date from dtend/due/duration properties */
         $end = $component->getProperty('dtend');
         if (!empty($end)) {
             $dtendExist = TRUE;
             $endDateFormat = isset($end['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         }
         if (empty($end) && $component->objName == 'vtodo') {
             $end = $component->getProperty('due');
             if (!empty($end)) {
                 $dueExist = TRUE;
                 $endDateFormat = isset($end['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             }
         }
         if (!empty($end) && !isset($end['hour'])) {
             /* a DTEND without time part regards an event that ends the day before,
                for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
             $endAllDayEvent = TRUE;
             $endWdate = mktime(23, 59, 59, $end['month'], $end['day'] - 1, $end['year']);
             $end['year'] = date('Y', $endWdate);
             $end['month'] = date('m', $endWdate);
             $end['day'] = date('d', $endWdate);
             $end['hour'] = 23;
             $end['min'] = $end['sec'] = 59;
         }
         if (empty($end)) {
             $end = $component->getProperty('duration', FALSE, FALSE, TRUE);
             // in dtend (array) format
             if (!empty($end)) {
                 $durationExist = TRUE;
             }
             $endDateFormat = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         }
         if (empty($end)) {
             // assume one day duration if missing end date
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
         }
         // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         $endWdate = iCalUtilityFunctions::_date2timestamp($end);
         if ($endWdate < $startWdate) {
             // MUST be after start date!!
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
             $endWdate = iCalUtilityFunctions::_date2timestamp($end);
         }
         $rdurWsecs = $endWdate - $startWdate;
         // compute event (component) duration in seconds
         /* make a list of optional exclude dates for component occurence from exrule and exdate */
         $exdatelist = array();
         $workstart = iCalUtilityFunctions::_timestamp2date($startDate - $rdurWsecs, 6);
         $workend = iCalUtilityFunctions::_timestamp2date($endDate + $rdurWsecs, 6);
         while (FALSE !== ($exrule = $component->getProperty('exrule'))) {
             // check exrule
             iCalUtilityFunctions::_recur2date($exdatelist, $exrule, $start, $workstart, $workend);
         }
         while (FALSE !== ($exdate = $component->getProperty('exdate'))) {
             // check exdate
             foreach ($exdate as $theExdate) {
                 $exWdate = iCalUtilityFunctions::_date2timestamp($theExdate);
                 $exWdate = mktime(0, 0, 0, date('m', $exWdate), date('d', $exWdate), date('Y', $exWdate));
                 // on a day-basis !!!
                 if ($startDate - $rdurWsecs <= $exWdate && $endDate >= $exWdate) {
                     $exdatelist[$exWdate] = TRUE;
                 }
             }
             // end - foreach( $exdate as $theExdate )
         }
         // end - check exdate
         $compUID = $component->getProperty('UID');
         /* check recurrence-id (with sequence), remove hit with reccurr-id date */
         if (FALSE !== ($recurrid = $component->getProperty('recurrence-id')) && FALSE !== ($sequence = $component->getProperty('sequence'))) {
             $recurrid = iCalUtilityFunctions::_date2timestamp($recurrid);
             $recurrid = mktime(0, 0, 0, date('m', $recurrid), date('d', $recurrid), date('Y', $recurrid));
             // on a day-basis !!!
             $endD = $recurrid + $rdurWsecs;
             do {
                 if (date('Ymd', $startWdate) != date('Ymd', $recurrid)) {
                     $exdatelist[$recurrid] = TRUE;
                 }
                 // exclude all other days than startdate
                 $wd = getdate($recurrid);
                 if (isset($result[$wd['year']][$wd['mon']][$wd['mday']][$compUID])) {
                     unset($result[$wd['year']][$wd['mon']][$wd['mday']][$compUID]);
                 }
                 // remove from output, dtstart etc added below
                 if ($split && $recurrid <= $endD) {
                     $recurrid = mktime(0, 0, 0, date('m', $recurrid), date('d', $recurrid) + 1, date('Y', $recurrid));
                 } else {
                     break;
                 }
             } while (TRUE);
         }
         // end recurrence-id/sequence test
         /* select only components with.. . */
         if (!$any && $startWdate >= $startDate && $startWdate <= $endDate || $any && $startWdate < $endDate && $endWdate >= $startDate) {
             // occurs within the period
             /* add the selected component (WITHIN valid dates) to output array */
             if ($flat) {
                 // any=true/false, ignores split
                 if (!$recurrid) {
                     $result[$compUID] = $component->copy();
                 }
                 // copy original to output (but not anyone with recurrence-id)
             } elseif ($split) {
                 // split the original component
                 if ($endWdate > $endDate) {
                     $endWdate = $endDate;
                 }
                 // use period end date
                 $rstart = $startWdate;
                 if ($rstart < $startDate) {
                     $rstart = $startDate;
                 }
                 // use period start date
                 $startYMD = date('Ymd', $rstart);
                 $endYMD = date('Ymd', $endWdate);
                 $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                 // on a day-basis !!!
                 while (date('Ymd', $rstart) <= $endYMD) {
                     // iterate
                     $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                     // on a day-basis !!!
                     if (isset($exdatelist[$checkDate])) {
                         // exclude any recurrence date, found in exdatelist
                         $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                         // step one day
                         continue;
                     }
                     if (date('Ymd', $rstart) > $startYMD) {
                         // date after dtstart
                         $datestring = date($startDateFormat, mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart)));
                     } else {
                         $datestring = date($startDateFormat, $rstart);
                     }
                     if (isset($start['tz'])) {
                         $datestring .= ' ' . $start['tz'];
                     }
                     // echo "X-CURRENT-DTSTART 3 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component->setProperty( 'X-CNT', $tcnt ); // test ###
                     $component->setProperty('X-CURRENT-DTSTART', $datestring);
                     if ($dtendExist || $dueExist || $durationExist) {
                         if (date('Ymd', $rstart) < $endYMD) {
                             // not the last day
                             $tend = mktime(23, 59, 59, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         } else {
                             $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         }
                         // on a day-basis !!!
                         if ($endAllDayEvent && $dtendExist) {
                             $tend += 24 * 3600;
                         }
                         // alldaysevents has an end date 'day after' meaning this day
                         $datestring = date($endDateFormat, $tend);
                         if (isset($end['tz'])) {
                             $datestring .= ' ' . $end['tz'];
                         }
                         $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                         $component->setProperty($propName, $datestring);
                     }
                     // end if( $dtendExist || $dueExist || $durationExist )
                     $wd = getdate($rstart);
                     $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy();
                     // copy to output
                     $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                     // step one day
                 }
                 // end while( $rstart <= $endWdate )
             } elseif ($recurrid && !$flat && !$any && !$split) {
                 $continue = TRUE;
             } else {
                 // !$flat && !$split, i.e. no flat array and DTSTART within period
                 $checkDate = mktime(0, 0, 0, date('m', $startWdate), date('d', $startWdate), date('Y', $startWdate));
                 // on a day-basis !!!
                 if (!$any || !isset($exdatelist[$checkDate])) {
                     // exclude any recurrence date, found in exdatelist
                     $wd = getdate($startWdate);
                     $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy();
                     // copy to output
                 }
             }
         }
         // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate ))
         /* if 'any' components, check components with reccurrence rules, removing all excluding dates */
         if (TRUE === $any) {
             /* make a list of optional repeating dates for component occurence, rrule, rdate */
             $recurlist = array();
             while (FALSE !== ($rrule = $component->getProperty('rrule'))) {
                 // check rrule
                 iCalUtilityFunctions::_recur2date($recurlist, $rrule, $start, $workstart, $workend);
             }
             foreach ($recurlist as $recurkey => $recurvalue) {
                 // key=match date as timestamp
                 $recurlist[$recurkey] = $rdurWsecs;
             }
             // add duration in seconds
             while (FALSE !== ($rdate = $component->getProperty('rdate'))) {
                 // check rdate
                 foreach ($rdate as $theRdate) {
                     if (is_array($theRdate) && 2 == count($theRdate) && array_key_exists('0', $theRdate) && array_key_exists('1', $theRdate)) {
                         $rstart = iCalUtilityFunctions::_date2timestamp($theRdate[0]);
                         if ($rstart < $startDate - $rdurWsecs || $rstart > $endDate) {
                             continue;
                         }
                         if (isset($theRdate[1]['year'])) {
                             // date-date period
                             $rend = iCalUtilityFunctions::_date2timestamp($theRdate[1]);
                         } else {
                             // date-duration period
                             $rend = iCalUtilityFunctions::_duration2date($theRdate[0], $theRdate[1]);
                             $rend = iCalUtilityFunctions::_date2timestamp($rend);
                         }
                         while ($rstart < $rend) {
                             $recurlist[$rstart] = $rdurWsecs;
                             // set start date for recurrence instance + rdate duration in seconds
                             $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                             // step one day
                         }
                     } else {
                         // single date
                         $theRdate = iCalUtilityFunctions::_date2timestamp($theRdate);
                         if ($startDate - $rdurWsecs <= $theRdate && $endDate >= $theRdate) {
                             $recurlist[$theRdate] = $rdurWsecs;
                         }
                         // set start date for recurrence instance + event duration in seconds
                     }
                 }
             }
             // end - check rdate
             if (0 < count($recurlist)) {
                 ksort($recurlist);
                 $xRecurrence = 1;
                 $component2 = $component->copy();
                 $compUID = $component2->getProperty('UID');
                 foreach ($recurlist as $recurkey => $durvalue) {
                     // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br />\n"; // test ###;
                     if ($startDate - $rdurWsecs > $recurkey || $endDate < $recurkey) {
                         // not within period
                         continue;
                     }
                     $checkDate = mktime(0, 0, 0, date('m', $recurkey), date('d', $recurkey), date('Y', $recurkey));
                     // on a day-basis !!!
                     if (isset($exdatelist[$checkDate])) {
                         // check excluded dates
                         continue;
                     }
                     if ($startWdate >= $recurkey) {
                         // exclude component start date
                         continue;
                     }
                     $rstart = $recurkey;
                     $rend = $recurkey + $durvalue;
                     /* add repeating components within valid dates to output array, only start date set */
                     if ($flat) {
                         if (!isset($result[$compUID])) {
                             // only one comp
                             $result[$compUID] = $component2->copy();
                         }
                         // copy to output
                     } elseif ($split) {
                         if ($rend > $endDate) {
                             $rend = $endDate;
                         }
                         $startYMD = date('Ymd', $rstart);
                         $endYMD = date('Ymd', $rend);
                         // echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."<br />\n"; // test ###;
                         while ($rstart <= $rend) {
                             // iterate.. .
                             $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                             // on a day-basis !!!
                             if (isset($exdatelist[$checkDate])) {
                                 // exclude any recurrence START date, found in exdatelist
                                 break;
                             }
                             // echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."<br />"; // test ###;
                             if ($rstart >= $startDate) {
                                 // date after dtstart
                                 if (date('Ymd', $rstart) > $startYMD) {
                                     // date after dtstart
                                     $datestring = date($startDateFormat, $checkDate);
                                 } else {
                                     $datestring = date($startDateFormat, $rstart);
                                 }
                                 if (isset($start['tz'])) {
                                     $datestring .= ' ' . $start['tz'];
                                 }
                                 //echo "X-CURRENT-DTSTART 1 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
                                 $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                                 if ($dtendExist || $dueExist || $durationExist) {
                                     if (date('Ymd', $rstart) < $endYMD) {
                                         // not the last day
                                         $tend = mktime(23, 59, 59, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                                     } else {
                                         $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                                     }
                                     // on a day-basis !!!
                                     if ($endAllDayEvent && $dtendExist) {
                                         $tend += 24 * 3600;
                                     }
                                     // alldaysevents has an end date 'day after' meaning this day
                                     $datestring = date($endDateFormat, $tend);
                                     if (isset($end['tz'])) {
                                         $datestring .= ' ' . $end['tz'];
                                     }
                                     $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                                     $component2->setProperty($propName, $datestring);
                                 }
                                 // end if( $dtendExist || $dueExist || $durationExist )
                                 $component2->setProperty('X-RECURRENCE', $xRecurrence);
                                 $wd = getdate($rstart);
                                 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy();
                                 // copy to output
                             }
                             // end if( $checkDate > $startYMD ) {    // date after dtstart
                             $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                             // step one day
                         }
                         // end while( $rstart <= $rend )
                         $xRecurrence += 1;
                     } elseif ($rstart >= $startDate) {
                         // date within period   //* flat=FALSE && split=FALSE => one comp every recur startdate *//
                         $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         // on a day-basis !!!
                         if (!isset($exdatelist[$checkDate])) {
                             // exclude any recurrence START date, found in exdatelist
                             $xRecurrence += 1;
                             $datestring = date($startDateFormat, $rstart);
                             if (isset($start['tz'])) {
                                 $datestring .= ' ' . $start['tz'];
                             }
                             //echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
                             $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                             if ($dtendExist || $dueExist || $durationExist) {
                                 $tend = $rstart + $rdurWsecs;
                                 if (date('Ymd', $tend) < date('Ymd', $endWdate)) {
                                     $tend = mktime(23, 59, 59, date('m', $tend), date('d', $tend), date('Y', $tend));
                                 } else {
                                     $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $tend), date('d', $tend), date('Y', $tend));
                                 }
                                 // on a day-basis !!!
                                 if ($endAllDayEvent && $dtendExist) {
                                     $tend += 24 * 3600;
                                 }
                                 // alldaysevents has an end date 'day after' meaning this day
                                 $datestring = date($endDateFormat, $tend);
                                 if (isset($end['tz'])) {
                                     $datestring .= ' ' . $end['tz'];
                                 }
                                 $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                                 $component2->setProperty($propName, $datestring);
                             }
                             // end if( $dtendExist || $dueExist || $durationExist )
                             $component2->setProperty('X-RECURRENCE', $xRecurrence);
                             $wd = getdate($rstart);
                             $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy();
                             // copy to output
                         }
                         // end if( !isset( $exdatelist[$checkDate] ))
                     }
                     // end elseif( $rstart >= $startDate )
                 }
                 // end foreach( $recurlist as $recurkey => $durvalue )
             }
             // end if( 0 < count( $recurlist ))
             /* deselect components with startdate/enddate not within period */
             if ($endWdate < $startDate || $startWdate > $endDate) {
                 continue;
             }
         }
         // end if( TRUE === $any )
     }
     // end foreach ( $this->components as $cix => $component )
     if (0 >= count($result)) {
         return FALSE;
     } elseif (!$flat) {
         foreach ($result as $y => $yeararr) {
             foreach ($yeararr as $m => $montharr) {
                 foreach ($montharr as $d => $dayarr) {
                     if (empty($result[$y][$m][$d])) {
                         unset($result[$y][$m][$d]);
                     } else {
                         $result[$y][$m][$d] = array_values($dayarr);
                     }
                     // skip tricky UID-index, hoping they are in hour order.. .
                 }
                 if (empty($result[$y][$m])) {
                     unset($result[$y][$m]);
                 } else {
                     ksort($result[$y][$m]);
                 }
             }
             if (empty($result[$y])) {
                 unset($result[$y]);
             } else {
                 ksort($result[$y]);
             }
         }
         if (empty($result)) {
             unset($result);
         } else {
             ksort($result);
         }
     }
     // end elseif( !$flat )
     if (0 >= count($result)) {
         return FALSE;
     }
     return $result;
 }
 /**
  * cache_event function
  *
  * Creates a new entry in the cache table for each date that the event appears
  * (and does not already have an explicit RECURRENCE-ID instance, given its
  * iCalendar UID).
  *
  * @param object $event Event to generate cache table for
  *
  * @return void
  **/
 function cache_event(&$event)
 {
     global $wpdb;
     // Convert event's timestamps to local for correct calculations of
     // recurrence. Need to also remove PHP timezone offset for each date for
     // SG_iCal to calculate correct recurring instances.
     $event->start = $this->gmt_to_local($event->start) - date('Z', $event->start);
     $event->end = $this->gmt_to_local($event->end) - date('Z', $event->end);
     $evs = array();
     $e = array('post_id' => $event->post_id, 'start' => $event->start, 'end' => $event->end);
     $duration = $event->getDuration();
     // Timestamp of today's date + 3 years
     $tif = Ai1ec_Time_Utility::current_time(true) + 94608000;
     //94 608 000 = 3 years in seconds
     // Always cache initial instance
     $evs[] = $e;
     $_start = $event->start;
     $_end = $event->end;
     if ($event->recurrence_rules) {
         $start = $event->start;
         $wdate = $startdate = iCalUtilityFunctions::_timestamp2date($_start, 6);
         $enddate = iCalUtilityFunctions::_timestamp2date($tif, 6);
         $exclude_dates = array();
         $recurrence_dates = array();
         if ($event->exception_rules) {
             // creat an array for the rules
             $exception_rules = $this->build_recurrence_rules_array($event->exception_rules);
             $exception_rules = iCalUtilityFunctions::_setRexrule($exception_rules);
             $result = array();
             // The first array is the result and it's passed by reference
             iCalUtilityFunctions::_recur2date($exclude_dates, $exception_rules, $wdate, $startdate, $enddate);
         }
         $recurrence_rules = $this->build_recurrence_rules_array($event->recurrence_rules);
         $recurrence_rules = iCalUtilityFunctions::_setRexrule($recurrence_rules);
         iCalUtilityFunctions::_recur2date($recurrence_dates, $recurrence_rules, $wdate, $startdate, $enddate);
         // Add the instances
         foreach ($recurrence_dates as $date => $bool) {
             // The arrays are in the form timestamp => true so an isset call is what we need
             if (isset($exclude_dates[$date])) {
                 continue;
             }
             $e['start'] = $date;
             $e['end'] = $date + $duration;
             $excluded = false;
             // Check if exception dates match this occurence
             if ($event->exception_dates) {
                 if ($this->date_match_exdates($date, $event->exception_dates)) {
                     $excluded = true;
                 }
             }
             // Add event only if it is not excluded
             if ($excluded == false) {
                 $evs[] = $e;
             }
         }
     }
     foreach ($evs as $e) {
         // Find out if this event instance is already accounted for by an
         // overriding 'RECURRENCE-ID' of the same iCalendar feed (by comparing the
         // UID, start date, recurrence). If so, then do not create duplicate
         // instance of event.
         $matching_event_id = $event->ical_uid ? $this->get_matching_event_id($event->ical_uid, $event->ical_feed_url, $start = $this->local_to_gmt($e['start']) - date('Z', $e['start']), false, $event->post_id) : null;
         // If no other instance was found
         if (is_null($matching_event_id)) {
             $start = getdate($e['start']);
             $end = getdate($e['end']);
             /*
             // Commented out for now
             // If event spans a day and end time is not midnight, or spans more than
             // a day, then create instance for each spanning day
             if( ( $start['mday'] != $end['mday'] &&
             			( $end['hours'] || $end['minutes'] || $end['seconds'] ) )
             		|| $e['end'] - $e['start'] > 60 * 60 * 24 ) {
             	$this->create_cache_table_entries( $e );
             // Else cache single instance of event
             } else {
             	$this->insert_event_in_cache_table( $e );
             }
             */
             $this->insert_event_in_cache_table($e);
         }
     }
 }
Пример #3
0
 /**
  * Create list of recurrent instances.
  *
  * @param Ai1ec_Event $event          Event to generate instances for.
  * @param array       $event_instance First instance contents.
  * @param int         $_start         Timestamp of first occurence.
  * @param int         $duration       Event duration in seconds.
  * @param string      $timezone       Target timezone.
  *
  * @return array List of event instances.
  */
 public function create_instances_by_recurrence(Ai1ec_Event $event, array $event_instance, $_start, $duration, $timezone)
 {
     $restore_timezone = date_default_timezone_get();
     $recurrence_parser = $this->_registry->get('recurrence.rule');
     $events = array();
     $start = $event_instance['start'];
     $wdate = $startdate = $enddate = $this->_parsed_date_array($_start, $timezone);
     $enddate['year'] = $enddate['year'] + 3;
     $exclude_dates = array();
     $recurrence_dates = array();
     if ($recurrence_dates = $event->get('recurrence_dates')) {
         $recurrence_dates = $this->_populate_recurring_dates($recurrence_dates, $startdate, $timezone);
     }
     if ($exception_dates = $event->get('exception_dates')) {
         $exclude_dates = $this->_populate_recurring_dates($exception_dates, $startdate, $timezone);
     }
     if ($event->get('exception_rules')) {
         // creat an array for the rules
         $exception_rules = $recurrence_parser->build_recurrence_rules_array($event->get('exception_rules'));
         unset($exception_rules['EXDATE']);
         if (!empty($exception_rules)) {
             $exception_rules = iCalUtilityFunctions::_setRexrule($exception_rules);
             $result = array();
             date_default_timezone_set($timezone);
             // The first array is the result and it is passed by reference
             iCalUtilityFunctions::_recur2date($exclude_dates, $exception_rules, $wdate, $startdate, $enddate);
             date_default_timezone_set($restore_timezone);
         }
     }
     $recurrence_rules = $recurrence_parser->build_recurrence_rules_array($event->get('recurrence_rules'));
     $recurrence_rules = iCalUtilityFunctions::_setRexrule($recurrence_rules);
     if ($recurrence_rules) {
         date_default_timezone_set($timezone);
         iCalUtilityFunctions::_recur2date($recurrence_dates, $recurrence_rules, $wdate, $startdate, $enddate);
         date_default_timezone_set($restore_timezone);
     }
     if (!is_array($recurrence_dates)) {
         $recurrence_dates = array();
     }
     $recurrence_dates = array_keys($recurrence_dates);
     // Add the instances
     foreach ($recurrence_dates as $timestamp) {
         // The arrays are in the form timestamp => true so an isset call is what we need
         if (!isset($exclude_dates[$timestamp])) {
             $event_instance['start'] = $timestamp;
             $event_instance['end'] = $timestamp + $duration;
             $events[$timestamp] = $event_instance;
         }
     }
     return $events;
 }
 /**
  * select components from calendar on date or selectOption basis
  *
  * Ensure DTSTART is set for every component.
  * No date controls occurs.
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.18.19 - 2014-02-01
  * @param mixed $startY optional, start Year,  default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
  * @param int   $startM optional, start Month, default current Month
  * @param int   $startD optional, start Day,   default current Day
  * @param int   $endY   optional, end   Year,  default $startY
  * @param int   $endY   optional, end   Month, default $startM
  * @param int   $endY   optional, end   Day,   default $startD
  * @param mixed $cType  optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  * @param bool  $flat   optional, FALSE (default) => output : array[Year][Month][Day][]
  *                                TRUE            => output : array[] (ignores split)
  * @param bool  $any    optional, TRUE (default) - select component(-s) that occurs within period
  *                                FALSE          - only component(-s) that starts within period
  * @param bool  $split  optional, TRUE (default) - one component copy every DAY it occurs during the
  *                                                 period (implies flat=FALSE)
  *                                FALSE          - one occurance of component only in output array
  * @return array or FALSE
  */
 function selectComponents($startY = FALSE, $startM = FALSE, $startD = FALSE, $endY = FALSE, $endM = FALSE, $endD = FALSE, $cType = FALSE, $flat = FALSE, $any = TRUE, $split = TRUE)
 {
     /* check  if empty calendar */
     if (0 >= count($this->components)) {
         return FALSE;
     }
     if (is_array($startY)) {
         return $this->selectComponents2($startY);
     }
     /* check default dates */
     if (!$startY) {
         $startY = date('Y');
     }
     if (!$startM) {
         $startM = date('m');
     }
     if (!$startD) {
         $startD = date('d');
     }
     $startDate = mktime(0, 0, 0, $startM, $startD, $startY);
     if (!$endY) {
         $endY = $startY;
     }
     if (!$endM) {
         $endM = $startM;
     }
     if (!$endD) {
         $endD = $startD;
     }
     $endDate = mktime(23, 59, 59, $endM, $endD, $endY);
     // echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br>\n"; $tcnt = 0;// test ###
     /* check component types */
     $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy');
     if (empty($cType)) {
         $cType = $validTypes;
     } else {
         if (!is_array($cType)) {
             $cType = array($cType);
         }
         $cType = array_map('strtolower', $cType);
         foreach ($cType as $cix => $theType) {
             $cType[$cix] = $theType;
             if (!in_array($theType, $validTypes)) {
                 $cType[$cix] = 'vevent';
             }
         }
         $cType = array_unique($cType);
     }
     if (FALSE === $flat && FALSE === $any) {
         // invalid combination
         $split = FALSE;
     }
     if (TRUE === $flat && TRUE === $split) {
         // invalid combination
         $split = FALSE;
     }
     /* iterate components */
     $result = array();
     $this->sort('UID');
     $compUIDcmp = null;
     $recurridList = array();
     foreach ($this->components as $cix => $component) {
         if (empty($component)) {
             continue;
         }
         unset($start);
         /* deselect unvalid type components */
         if (!in_array($component->objName, $cType)) {
             continue;
         }
         $start = $component->getProperty('dtstart', FALSE, TRUE);
         /* select due when dtstart is missing */
         if (empty($start) && $component->objName == 'vtodo' && FALSE === ($start = $component->getProperty('due', FALSE, TRUE))) {
             continue;
         }
         if (empty($start)) {
             continue;
         }
         if (!isset($start['value']['tz']) && isset($start['params']['TZID'])) {
             $start['value']['tz'] = $start['params']['TZID'];
         }
         $start = $start['value'];
         $compUID = $component->getProperty('UID');
         if ($compUIDcmp != $compUID) {
             $compUIDcmp = $compUID;
             unset($exdatelist, $recurridList);
         }
         $SCbools = array('dtendExist' => FALSE, 'dueExist' => FALSE, 'durationExist' => FALSE, 'endAllDayEvent' => FALSE);
         $recurrid = FALSE;
         $dateFormat = array();
         unset($end, $startWdate, $endWdate, $rdurWsecs, $rdur, $workstart, $workend);
         // clean up
         $startWdate = iCalUtilityFunctions::_SCsetXCurrentDateZ(iCalUtilityFunctions::_date2timestamp($start), $start);
         $dateFormat['start'] = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         /* get end date from dtend/due/duration properties */
         $end = $component->getProperty('dtend', FALSE, TRUE);
         if (!empty($end)) {
             $SCbools['dtendExist'] = TRUE;
             $dateFormat['end'] = isset($end['value']['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         }
         if (!isset($end['value']['tz']) && isset($end['params']['TZID'])) {
             $end['value']['tz'] = $end['params']['TZID'];
         }
         $end = $end['value'];
         if (empty($end) && $component->objName == 'vtodo') {
             $end = $component->getProperty('due');
             if (!empty($end)) {
                 $SCbools['dueExist'] = TRUE;
                 $dateFormat['end'] = isset($end['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             }
         }
         if (!empty($end) && !isset($end['hour'])) {
             /* a DTEND without time part regards an event that ends the day before,
                for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
             $SCbools['endAllDayEvent'] = TRUE;
             $endWdate = mktime(23, 59, 59, $end['month'], $end['day'] - 1, $end['year']);
             $end['year'] = date('Y', $endWdate);
             $end['month'] = date('m', $endWdate);
             $end['day'] = date('d', $endWdate);
             $end['hour'] = 23;
             $end['min'] = $end['sec'] = 59;
         }
         if (empty($end)) {
             $end = $component->getProperty('duration', FALSE, FALSE, TRUE);
             // in dtend (array) format
             if (!empty($end)) {
                 if (isset($start['tz'])) {
                     $end['tz'] = $start['tz'];
                 }
             }
             $SCbools['durationExist'] = TRUE;
             $dateFormat['end'] = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br>\n"; // test ###
         }
         if (empty($end)) {
             // assume one day duration if missing end date
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
             if (isset($start['tz'])) {
                 $end['tz'] = $start['tz'];
             }
         }
         // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br>\n"; // test ###
         $endWdate = iCalUtilityFunctions::_SCsetXCurrentDateZ(iCalUtilityFunctions::_date2timestamp($end), $end);
         if ($endWdate < $startWdate) {
             // MUST be after start date!!
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
             $endWdate = iCalUtilityFunctions::_date2timestamp($end);
         }
         $rdurWsecs = $endWdate - $startWdate;
         // compute event (component) duration in seconds
         /* make a list of optional exclude dates for component occurence from exrule and exdate */
         $exdatelist = array();
         $workstart = iCalUtilityFunctions::_timestamp2date($startDate - $rdurWsecs, 6);
         $workend = iCalUtilityFunctions::_timestamp2date($endDate + $rdurWsecs, 6);
         while (FALSE !== ($exrule = $component->getProperty('exrule'))) {
             // check exrule
             iCalUtilityFunctions::_recur2date($exdatelist, $exrule, $start, $workstart, $workend);
         }
         while (FALSE !== ($exdate = $component->getProperty('exdate'))) {
             // check exdate
             foreach ($exdate as $theExdate) {
                 $exWdate = iCalUtilityFunctions::_date2timestamp($theExdate);
                 $exWdate = mktime(0, 0, 0, date('m', $exWdate), date('d', $exWdate), date('Y', $exWdate));
                 // on a day-basis !!!
                 if ($startDate - $rdurWsecs <= $exWdate && $endDate >= $exWdate) {
                     $exdatelist[$exWdate] = TRUE;
                 }
             }
             // end - foreach( $exdate as $theExdate )
         }
         // end - check exdate
         /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), remove hit with reccurr-id date */
         if (FALSE !== ($recurrid = $component->getProperty('recurrence-id'))) {
             $recurrid = iCalUtilityFunctions::_date2timestamp($recurrid);
             $recurrid = mktime(0, 0, 0, date('m', $recurrid), date('d', $recurrid), date('Y', $recurrid));
             // on a day-basis !!!
             $recurridList[$recurrid] = TRUE;
             // no recurring to start this day
             // echo "adding comp no:$cix with date=".implode($start)." and recurrid=".implode($recurrid)." to recurridList id=$recurrid<br>\n"; // test ###
         }
         // end recurrence-id/sequence test
         /* select only components with.. . */
         if (!$any && $startWdate >= $startDate && $startWdate <= $endDate || $any && $startWdate < $endDate && $endWdate >= $startDate) {
             // occurs within the period
             /* add the selected component (WITHIN valid dates) to output array */
             if ($flat) {
                 // any=true/false, ignores split
                 if (!$recurrid) {
                     $result[$compUID] = $component->copy();
                 }
                 // copy original to output (but not anyone with recurrence-id)
             } elseif ($split) {
                 // split the original component
                 if ($endWdate > $endDate) {
                     $endWdate = $endDate;
                 }
                 // use period end date
                 $rstart = $startWdate < $startDate ? $startDate : $startWdate;
                 // use period start date
                 $startYMD = $rstartYMD = date('Ymd', $rstart);
                 $endYMD = date('Ymd', $endWdate);
                 $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                 // on a day-basis !!!
                 // echo "going to test comp no:$cix with rstartYMD=$rstartYMD, endYMD=$endYMD and checkDate($checkDate) with recurridList=".implode(',',array_keys($recurridList))."<br>\n"; // test ###
                 if (!isset($exdatelist[$checkDate])) {
                     // exclude any recurrence START date, found in exdatelist
                     while ($rstartYMD <= $endYMD) {
                         // iterate
                         if (isset($exdatelist[$checkDate]) || isset($recurridList[$checkDate]) && !$recurrid) {
                             // or in the recurridList, but not itself
                             // echo "skipping comp no:$cix with datestart=$rstartYMD and checkdate=$checkDate<br>\n"; // test ###
                             $rstart += 24 * 3600;
                             // step one day
                             $rstartYMD = date('Ymd', $rstart);
                             continue;
                         }
                         iCalUtilityFunctions::_SCsetXCurrentStart($component, $dateFormat, $checkDate, $rstartYMD, $rstart, $startYMD, $start);
                         iCalUtilityFunctions::_SCsetXCurrentEnd($component, $dateFormat, $rstart, $rstartYMD, $endWdate, $endYMD, $end, $SCbools);
                         $wd = getdate($rstart);
                         $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy();
                         // copy to output
                         $rstart += 24 * 3600;
                         // step one day
                         $rstartYMD = date('Ymd', $rstart);
                         $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         // on a day-basis !!!
                     }
                     // end while( $rstart <= $endWdate )
                 }
                 // end if( !isset( $exdatelist[$checkDate] ))
             } elseif ($recurrid && !$flat && !$any && !$split) {
                 $continue = TRUE;
             } else {
                 // !$flat && !$split, i.e. no flat array and DTSTART within period
                 $checkDate = mktime(0, 0, 0, date('m', $startWdate), date('d', $startWdate), date('Y', $startWdate));
                 // on a day-basis !!!
                 // echo "going to test comp no:$cix with checkDate=$checkDate with recurridList=".implode(',',array_keys($recurridList)); // test ###
                 if ((!$any || !isset($exdatelist[$checkDate])) && (!isset($recurridList[$checkDate]) || $recurrid)) {
                     // or in the recurridList, but not itself
                     // echo " and copied to output<br>\n"; // test ###
                     $wd = getdate($startWdate);
                     $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy();
                     // copy to output
                 }
             }
         }
         // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate ))
         /* if 'any' components, check components with reccurrence rules, removing all excluding dates */
         if (TRUE === $any) {
             /* make a list of optional repeating dates for component occurence, rrule, rdate */
             $recurlist = array();
             while (FALSE !== ($rrule = $component->getProperty('rrule'))) {
                 // check rrule
                 iCalUtilityFunctions::_recur2date($recurlist, $rrule, $start, $workstart, $workend);
             }
             foreach ($recurlist as $recurkey => $recurvalue) {
                 // key=match date as timestamp
                 $recurlist[$recurkey] = $rdurWsecs;
             }
             // add duration in seconds
             while (FALSE !== ($rdate = $component->getProperty('rdate'))) {
                 // check rdate
                 foreach ($rdate as $theRdate) {
                     if (is_array($theRdate) && 2 == count($theRdate) && array_key_exists('0', $theRdate) && array_key_exists('1', $theRdate)) {
                         $rstart = iCalUtilityFunctions::_date2timestamp($theRdate[0]);
                         if ($rstart < $startDate - $rdurWsecs || $rstart > $endDate) {
                             continue;
                         }
                         if (isset($theRdate[1]['year'])) {
                             // date-date period
                             $rend = iCalUtilityFunctions::_date2timestamp($theRdate[1]);
                         } else {
                             // date-duration period
                             $rend = iCalUtilityFunctions::_duration2date($theRdate[0], $theRdate[1]);
                             $rend = iCalUtilityFunctions::_date2timestamp($rend);
                         }
                         while ($rstart < $rend) {
                             $recurlist[$rstart] = $rdurWsecs;
                             // set start date for recurrence instance + rdate duration in seconds
                             $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                             // step one day
                         }
                     } else {
                         // single date
                         $theRdate = iCalUtilityFunctions::_date2timestamp($theRdate);
                         if ($startDate - $rdurWsecs <= $theRdate && $endDate >= $theRdate) {
                             $recurlist[$theRdate] = $rdurWsecs;
                         }
                         // set start date for recurrence instance + event duration in seconds
                     }
                 }
             }
             // end - check rdate
             foreach ($recurlist as $recurkey => $durvalue) {
                 // remove all recurrence START dates found in the exdatelist
                 $checkDate = mktime(0, 0, 0, date('m', $recurkey), date('d', $recurkey), date('Y', $recurkey));
                 // on a day-basis !!!
                 if (isset($exdatelist[$checkDate])) {
                     // no recurring to start this day
                     unset($recurlist[$recurkey]);
                 }
             }
             if (0 < count($recurlist)) {
                 ksort($recurlist);
                 $xRecurrence = 1;
                 $component2 = $component->copy();
                 $compUID = $component2->getProperty('UID');
                 foreach ($recurlist as $recurkey => $durvalue) {
                     // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br>\n"; // test ###;
                     if ($startDate - $rdurWsecs > $recurkey || $endDate < $recurkey) {
                         // not within period
                         continue;
                     }
                     $checkDate = mktime(0, 0, 0, date('m', $recurkey), date('d', $recurkey), date('Y', $recurkey));
                     // on a day-basis !!!
                     if (isset($recurridList[$checkDate])) {
                         // no recurring to start this day
                         continue;
                     }
                     if (isset($exdatelist[$checkDate])) {
                         // check excluded dates
                         continue;
                     }
                     if ($startWdate >= $recurkey) {
                         // exclude component start date
                         continue;
                     }
                     $rstart = $recurkey;
                     $rend = $recurkey + $durvalue;
                     /* add repeating components within valid dates to output array, only start date set */
                     if ($flat) {
                         if (!isset($result[$compUID])) {
                             // only one comp
                             $result[$compUID] = $component2->copy();
                         }
                         // copy to output
                     } elseif ($split) {
                         $xRecurrence += 1;
                         if ($rend > $endDate) {
                             $rend = $endDate;
                         }
                         $startYMD = $rstartYMD = date('Ymd', $rstart);
                         $endYMD = date('Ymd', $rend);
                         // echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."<br>\n"; // test ###;
                         while ($rstartYMD <= $endYMD) {
                             // iterate.. .
                             $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                             // on a day-basis !!!
                             if (isset($recurridList[$checkDate])) {
                                 // no recurring to start this day
                                 break;
                             }
                             if (isset($exdatelist[$checkDate])) {
                                 // exclude any recurrence START date, found in exdatelist
                                 break;
                             }
                             // echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."<br>"; // test ###;
                             if ($rstart >= $startDate) {
                                 // date after dtstart
                                 iCalUtilityFunctions::_SCsetXCurrentStart($component2, $dateFormat, $checkDate, $rstartYMD, $rstart, $startYMD, $start);
                                 iCalUtilityFunctions::_SCsetXCurrentEnd($component2, $dateFormat, $rstart, $rstartYMD, $endWdate, $endYMD, $end, $SCbools);
                                 $component2->setProperty('X-RECURRENCE', $xRecurrence);
                                 $wd = getdate($rstart);
                                 $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy();
                                 // copy to output
                             }
                             // end if$rstart >= $startDate { // date after dtstart
                             $rstart += 24 * 3600;
                             // step one day
                             $rstartYMD = date('Ymd', $rstart);
                         }
                         // end while( $rstart <= $rend )
                     } elseif ($rstart >= $startDate) {
                         // date within period   //* flat=FALSE && split=FALSE => one comp every recur startdate *//
                         $xRecurrence += 1;
                         $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         // on a day-basis !!!
                         if (!isset($exdatelist[$checkDate])) {
                             // exclude any recurrence START date, found in exdatelist
                             iCalUtilityFunctions::_SCsetXCurrentStart($component2, $dateFormat, $rstart, FALSE, FALSE, FALSE, $start);
                             $tend = $rstart + $rdurWsecs;
                             iCalUtilityFunctions::_SCsetXCurrentEnd($component2, $dateFormat, $tend, date('Ymd', $tend), $endWdate, date('Ymd', $endWdate), $end, $SCbools);
                             $component2->setProperty('X-RECURRENCE', $xRecurrence);
                             $wd = getdate($rstart);
                             $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy();
                             // copy to output
                         }
                         // end if( !isset( $exdatelist[$checkDate] ))
                     }
                     // end elseif( $rstart >= $startDate )
                 }
                 // end foreach( $recurlist as $recurkey => $durvalue )
                 unset($component2);
             }
             // end if( 0 < count( $recurlist ))
             /* deselect components with startdate/enddate not within period */
             if ($endWdate < $startDate || $startWdate > $endDate) {
                 continue;
             }
         }
         // end if( TRUE === $any )
     }
     // end foreach ( $this->components as $cix => $component )
     unset($SCbools, $recurrid, $recurridList, $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $recurlist, $workstart, $workend, $dateFormat);
     // clean up
     if (0 >= count($result)) {
         return FALSE;
     } elseif (!$flat) {
         foreach ($result as $y => $yeararr) {
             foreach ($yeararr as $m => $montharr) {
                 foreach ($montharr as $d => $dayarr) {
                     if (empty($result[$y][$m][$d])) {
                         unset($result[$y][$m][$d]);
                     } else {
                         $result[$y][$m][$d] = array_values($dayarr);
                         // skip tricky UID-index
                         if (1 < count($result[$y][$m][$d])) {
                             foreach ($result[$y][$m][$d] as &$c) {
                                 // sort
                                 iCalUtilityFunctions::_setSortArgs($c);
                             }
                             usort($result[$y][$m][$d], array('iCalUtilityFunctions', '_cmpfcn'));
                         }
                     }
                 }
                 // end foreach( $montharr as $d => $dayarr )
                 if (empty($result[$y][$m])) {
                     unset($result[$y][$m]);
                 } else {
                     ksort($result[$y][$m]);
                 }
             }
             // end foreach( $yeararr as $m => $montharr )
             if (empty($result[$y])) {
                 unset($result[$y]);
             } else {
                 ksort($result[$y]);
             }
         }
         // end foreach( $result as $y => $yeararr )
         if (empty($result)) {
             unset($result);
         } else {
             ksort($result);
         }
     }
     // end elseif( !$flat )
     if (0 >= count($result)) {
         return FALSE;
     }
     return $result;
 }
 /**
  * cache_event function
  *
  * Creates a new entry in the cache table for each date that the event appears
  * (and does not already have an explicit RECURRENCE-ID instance, given its
  * iCalendar UID).
  *
  * @param Ai1ec_Event $event Event to generate cache table for
  *
  * @return void
  **/
 public function cache_event(Ai1ec_Event &$event)
 {
     global $wpdb;
     // Convert event timestamps to local for correct calculations of
     // recurrence. Need to also remove PHP timezone offset for each date for
     // SG_iCal to calculate correct recurring instances.
     $event->start = $this->gmt_to_local($event->start) - date('Z', $event->start);
     $event->end = $this->gmt_to_local($event->end) - date('Z', $event->end);
     $evs = array();
     $e = array('post_id' => $event->post_id, 'start' => $event->start, 'end' => $event->end);
     $duration = $event->getDuration();
     // Timestamp of today date + 3 years (94608000 seconds)
     $tif = Ai1ec_Time_Utility::current_time(true) + 94608000;
     // Always cache initial instance
     $evs[] = $e;
     $_start = $event->start;
     $_end = $event->end;
     if ($event->recurrence_rules) {
         $start = $event->start;
         $wdate = $startdate = iCalUtilityFunctions::_timestamp2date($_start, 6);
         $enddate = iCalUtilityFunctions::_timestamp2date($tif, 6);
         $exclude_dates = array();
         $recurrence_dates = array();
         if ($event->exception_rules) {
             // creat an array for the rules
             $exception_rules = $this->build_recurrence_rules_array($event->exception_rules);
             $exception_rules = iCalUtilityFunctions::_setRexrule($exception_rules);
             $result = array();
             // The first array is the result and it is passed by reference
             iCalUtilityFunctions::_recur2date($exclude_dates, $exception_rules, $wdate, $startdate, $enddate);
         }
         $recurrence_rules = $this->build_recurrence_rules_array($event->recurrence_rules);
         $recurrence_rules = iCalUtilityFunctions::_setRexrule($recurrence_rules);
         iCalUtilityFunctions::_recur2date($recurrence_dates, $recurrence_rules, $wdate, $startdate, $enddate);
         // Add the instances
         foreach ($recurrence_dates as $date => $bool) {
             // The arrays are in the form timestamp => true so an isset call is what we need
             if (isset($exclude_dates[$date])) {
                 continue;
             }
             $e['start'] = $date;
             $e['end'] = $date + $duration;
             $excluded = false;
             // Check if exception dates match this occurence
             if ($event->exception_dates) {
                 if ($this->date_match_exdates($date, $event->exception_dates)) {
                     $excluded = true;
                 }
             }
             // Add event only if it is not excluded
             if ($excluded == false) {
                 $evs[] = $e;
             }
         }
     }
     // Make entries unique (sometimes recurrence generator creates duplicates?)
     $evs_unique = array();
     foreach ($evs as $ev) {
         $evs_unique[md5(serialize($ev))] = $ev;
     }
     foreach ($evs_unique as $e) {
         // Find out if this event instance is already accounted for by an
         // overriding 'RECURRENCE-ID' of the same iCalendar feed (by comparing the
         // UID, start date, recurrence). If so, then do not create duplicate
         // instance of event.
         $start = $this->local_to_gmt($e['start']) - date('Z', $e['start']);
         $matching_event_id = $event->ical_uid ? $this->get_matching_event_id($event->ical_uid, $event->ical_feed_url, $start, false, $event->post_id) : NULL;
         // If no other instance was found
         if (NULL === $matching_event_id) {
             $start = getdate($e['start']);
             $end = getdate($e['end']);
             $this->insert_event_in_cache_table($e);
         }
     }
     return Ai1ec_Events_List_Helper::get_instance()->clean_post_cache($event->post_id);
 }
 /**
  * select components from calendar on date basis
  *
  * Ensure DTSTART is set for every component.
  * No date controls occurs.
  *
  * @author Kjell-Inge Gustafsson <*****@*****.**>
  * @since 2.6.22 - 2010-10-21
  * @param int $startY optional,  start Year, default current Year
  * @param int $startM optional,  start Month, default current Month
  * @param int $startD optional,  start Day, default current Day
  * @param int $endY optional,    end Year, default $startY
  * @param int $endY optional,    end Month, default $startM
  * @param int $endY optional,    end Day, default $startD
  * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  * @param bool $flat optional,   FALSE (default) => output : array[Year][Month][Day][]
  *                               TRUE => output : array[] (ignores split)
  * @param bool $any optional,    TRUE (default) - select component that take place within period
  *                               FALSE - only components that starts within period
  * @param bool $split optional,  TRUE (default) - one component copy every day it take place during the
  *                                       period (implies flat=FALSE)
  *                               FALSE - one occurance of component only in output array
  * @return array or FALSE
  */
 function selectComponents($startY = FALSE, $startM = FALSE, $startD = FALSE, $endY = FALSE, $endM = FALSE, $endD = FALSE, $cType = FALSE, $flat = FALSE, $any = TRUE, $split = TRUE)
 {
     /* check  if empty calendar */
     if (0 >= count($this->components)) {
         return FALSE;
     }
     /* check default dates */
     if (!$startY) {
         $startY = date('Y');
     }
     if (!$startM) {
         $startM = date('m');
     }
     if (!$startD) {
         $startD = date('d');
     }
     $startDate = mktime(0, 0, 0, $startM, $startD, $startY);
     if (!$endY) {
         $endY = $startY;
     }
     if (!$endM) {
         $endM = $startM;
     }
     if (!$endD) {
         $endD = $startD;
     }
     $endDate = mktime(23, 59, 59, $endM, $endD, $endY);
     /* check component types */
     $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy');
     if (is_array($cType)) {
         foreach ($cType as $cix => $theType) {
             $cType[$cix] = $theType = strtolower($theType);
             if (!in_array($theType, $validTypes)) {
                 $cType[$cix] = 'vevent';
             }
         }
         $cType = array_unique($cType);
     } elseif (!empty($cType)) {
         $cType = strtolower($cType);
         if (!in_array($cType, $validTypes)) {
             $cType = array('vevent');
         } else {
             $cType = array($cType);
         }
     } else {
         $cType = $validTypes;
     }
     if (0 >= count($cType)) {
         $cType = $validTypes;
     }
     /* iterate components */
     $result = array();
     foreach ($this->components as $cix => $component) {
         if (empty($component)) {
             continue;
         }
         unset($component->propix, $start);
         /* deselect unvalid type components */
         if (!in_array($component->objName, $cType)) {
             continue;
         }
         $start = $component->getProperty('dtstart');
         /* select due when dtstart is missing */
         if (empty($start) && $component->objName == 'vtodo' && FALSE === ($start = $component->getProperty('due'))) {
             continue;
         }
         $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE;
         unset($end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat);
         // clean up
         $startWdate = iCalUtilityFunctions::_date2timestamp($start);
         $startDateFormat = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         /* get end date from dtend/due/duration properties */
         $end = $component->getProperty('dtend');
         if (!empty($end)) {
             $dtendExist = TRUE;
             $endDateFormat = isset($end['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
         }
         // if( !empty($end))  echo 'selectComp 1 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         if (empty($end) && $component->objName == 'vtodo') {
             $end = $component->getProperty('due');
             if (!empty($end)) {
                 $dueExist = TRUE;
                 $endDateFormat = isset($end['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             }
             // if( !empty($end))  echo 'selectComp 2 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         }
         if (!empty($end) && !isset($end['hour'])) {
             /* a DTEND without time part regards an event that ends the day before,
                for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
             $endAllDayEvent = TRUE;
             $endWdate = mktime(23, 59, 59, $end['month'], $end['day'] - 1, $end['year']);
             $end['year'] = date('Y', $endWdate);
             $end['month'] = date('m', $endWdate);
             $end['day'] = date('d', $endWdate);
             $end['hour'] = 23;
             $end['min'] = $end['sec'] = 59;
             // if( !empty($end))  echo 'selectComp 3 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         }
         if (empty($end)) {
             $end = $component->getProperty('duration', FALSE, FALSE, TRUE);
             // in dtend (array) format
             if (!empty($end)) {
                 $durationExist = TRUE;
             }
             $endDateFormat = isset($start['hour']) ? 'Y-m-d H:i:s' : 'Y-m-d';
             // if( !empty($end))  echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         }
         if (empty($end)) {
             // assume one day duration if missing end date
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
             // if( isset($end))  echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
         }
         $endWdate = iCalUtilityFunctions::_date2timestamp($end);
         if ($endWdate < $startWdate) {
             // MUST be after start date!!
             $end = array('year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59);
             $endWdate = iCalUtilityFunctions::_date2timestamp($end);
         }
         $rdurWsecs = $endWdate - $startWdate;
         // compute component duration in seconds
         /* make a list of optional exclude dates for component occurence from exrule and exdate */
         $exdatelist = array();
         $workstart = iCalUtilityFunctions::_timestamp2date($startDate - $rdurWsecs, 6);
         $workend = iCalUtilityFunctions::_timestamp2date($endDate + $rdurWsecs, 6);
         while (FALSE !== ($exrule = $component->getProperty('exrule'))) {
             // check exrule
             iCalUtilityFunctions::_recur2date($exdatelist, $exrule, $start, $workstart, $workend);
         }
         while (FALSE !== ($exdate = $component->getProperty('exdate'))) {
             // check exdate
             foreach ($exdate as $theExdate) {
                 $exWdate = iCalUtilityFunctions::_date2timestamp($theExdate);
                 $exWdate = mktime(0, 0, 0, date('m', $exWdate), date('d', $exWdate), date('Y', $exWdate));
                 // on a day-basis !!!
                 if ($startDate - $rdurWsecs <= $exWdate && $endDate >= $exWdate) {
                     $exdatelist[$exWdate] = TRUE;
                 }
             }
         }
         /* if 'any' components, check repeating components, removing all excluding dates */
         if (TRUE === $any) {
             /* make a list of optional repeating dates for component occurence, rrule, rdate */
             $recurlist = array();
             while (FALSE !== ($rrule = $component->getProperty('rrule'))) {
                 // check rrule
                 iCalUtilityFunctions::_recur2date($recurlist, $rrule, $start, $workstart, $workend);
             }
             foreach ($recurlist as $recurkey => $recurvalue) {
                 // key=match date as timestamp
                 $recurlist[$recurkey] = $rdurWsecs;
             }
             // add duration in seconds
             while (FALSE !== ($rdate = $component->getProperty('rdate'))) {
                 // check rdate
                 foreach ($rdate as $theRdate) {
                     if (is_array($theRdate) && 2 == count($theRdate) && array_key_exists('0', $theRdate) && array_key_exists('1', $theRdate)) {
                         $rstart = iCalUtilityFunctions::_date2timestamp($theRdate[0]);
                         if ($rstart < $startDate - $rdurWsecs || $rstart > $endDate) {
                             continue;
                         }
                         if (isset($theRdate[1]['year'])) {
                             // date-date period
                             $rend = iCalUtilityFunctions::_date2timestamp($theRdate[1]);
                         } else {
                             // date-duration period
                             $rend = iCalUtilityFunctions::_duration2date($theRdate[0], $theRdate[1]);
                             $rend = iCalUtilityFunctions::_date2timestamp($rend);
                         }
                         while ($rstart < $rend) {
                             $recurlist[$rstart] = $rdurWsecs;
                             // set start date for recurrence instance + rdate duration in seconds
                             $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                             // step one day
                         }
                     } else {
                         // single date
                         $theRdate = iCalUtilityFunctions::_date2timestamp($theRdate);
                         if ($startDate - $rdurWsecs <= $theRdate && $endDate >= $theRdate) {
                             $recurlist[$theRdate] = $rdurWsecs;
                         }
                         // set start date for recurrence instance + event duration in seconds
                     }
                 }
             }
             if (0 < count($recurlist)) {
                 ksort($recurlist);
                 $xRecurrence = 1;
                 foreach ($recurlist as $recurkey => $durvalue) {
                     if ($startDate - $rdurWsecs > $recurkey || $endDate < $recurkey) {
                         // not within period
                         continue;
                     }
                     $checkDate = mktime(0, 0, 0, date('m', $recurkey), date('d', $recurkey), date('Y', $recurkey));
                     // on a day-basis !!!
                     if (isset($exdatelist[$checkDate])) {
                         // check excluded dates
                         continue;
                     }
                     if ($startWdate >= $recurkey) {
                         // exclude component start date
                         continue;
                     }
                     $component2 = $component->copy();
                     $rstart = $recurkey;
                     $rend = $recurkey + $durvalue;
                     /* add repeating components within valid dates to output array, only start date set */
                     if ($flat) {
                         $datestring = date($startDateFormat, $recurkey);
                         if (isset($start['tz'])) {
                             $datestring .= ' ' . $start['tz'];
                         }
                         $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                         if ($dtendExist || $dueExist || $durationExist) {
                             $datestring = date($endDateFormat, $recurkey + $durvalue);
                             // fixa korrekt sluttid
                             if (isset($end['tz'])) {
                                 $datestring .= ' ' . $end['tz'];
                             }
                             $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                             $component2->setProperty($propName, $datestring);
                         }
                         // end if( $dtendExist || $dueExist || $durationExist )
                         $component2->setProperty('X-RECURRENCE', ++$xRecurrence);
                         $result[$component2->getProperty('UID')] = $component2->copy();
                         // copy to output
                     } elseif ($split) {
                         $xRecurrence += 1;
                         if ($rend > $endDate) {
                             $rend = $endDate;
                         }
                         while ($rstart <= $rend) {
                             // iterate.. .
                             $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                             // on a day-basis !!!
                             if (isset($exdatelist[$checkDate])) {
                                 // exclude any recurrence START date, found in exdatelist
                                 break;
                             }
                             if ($rstart > $startDate) {
                                 // date after dtstart
                                 $datestring = date($startDateFormat, $rstart);
                                 if (isset($start['tz'])) {
                                     $datestring .= ' ' . $start['tz'];
                                 }
                                 $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                                 if ($dtendExist || $dueExist || $durationExist) {
                                     $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                                     // on a day-basis !!!
                                     $datestring = date($endDateFormat, $tend);
                                     if (isset($end['tz'])) {
                                         $datestring .= ' ' . $end['tz'];
                                     }
                                     $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                                     $component2->setProperty($propName, $datestring);
                                 }
                                 // end if( $dtendExist || $dueExist || $durationExist )
                                 $component2->setProperty('X-RECURRENCE', $xRecurrence);
                                 $wd = getdate($rstart);
                                 $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty('UID')] = $component2->copy();
                                 // copy to output
                             }
                             $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                             // step one day
                         }
                         // end while( $rstart <= $rend )
                     } elseif ($rstart >= $startDate) {
                         // date within period   //* flat=FALSE && split=FALSE *//
                         $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                         // on a day-basis !!!
                         if (!isset($exdatelist[$checkDate])) {
                             // exclude any recurrence START date, found in exdatelist
                             $xRecurrence += 1;
                             $datestring = date($startDateFormat, $rstart);
                             if (isset($start['tz'])) {
                                 $datestring .= ' ' . $start['tz'];
                             }
                             $component2->setProperty('X-CURRENT-DTSTART', $datestring);
                             if ($dtendExist || $dueExist || $durationExist) {
                                 $rstart += $rdurWsecs;
                                 $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                                 // on a day-basis !!!
                                 $datestring = date($endDateFormat, $tend);
                                 if (isset($end['tz'])) {
                                     $datestring .= ' ' . $end['tz'];
                                 }
                                 $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                                 $component2->setProperty($propName, $datestring);
                             }
                             // end if( $dtendExist || $dueExist || $durationExist )
                             $component2->setProperty('X-RECURRENCE', $xRecurrence);
                             $wd = getdate($rstart);
                             $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty('UID')] = $component2->copy();
                             // copy to output
                         }
                         // end if( !isset( $exdatelist[$checkDate] ))
                     }
                     // end elseif( $rstart >= $startDate )
                 }
                 // end foreach( $recurlist as $recurkey => $durvalue )
             }
             // end if( 0 < count( $recurlist ))
             /* deselect components with startdate/enddate not within period */
             if ($endWdate < $startDate || $startWdate > $endDate) {
                 continue;
             }
         } elseif ($startWdate < $startDate || $startWdate > $endDate) {
             continue;
         }
         /* add the selected component (WITHIN valid dates) to output array */
         if ($flat) {
             $result[$component->getProperty('UID')] = $component->copy();
         } elseif ($split) {
             if ($endWdate > $endDate) {
                 $endWdate = $endDate;
             }
             // use period end date
             $rstart = $startWdate;
             if ($rstart < $startDate) {
                 $rstart = $startDate;
             }
             // use period start date
             $checkDate = mktime(0, 0, 0, date('m', $rstart), date('d', $rstart), date('Y', $rstart));
             // on a day-basis !!!
             if (!isset($exdatelist[$checkDate])) {
                 // exclude any recurrence START date, found in exdatelist
                 while ($rstart <= $endWdate) {
                     // iterate
                     if ($rstart > $startWdate) {
                         // if NOT startdate, set X-properties
                         $datestring = date($startDateFormat, $rstart);
                         if (isset($start['tz'])) {
                             $datestring .= ' ' . $start['tz'];
                         }
                         $component->setProperty('X-CURRENT-DTSTART', $datestring);
                         if ($dtendExist || $dueExist || $durationExist) {
                             $tend = mktime(date('H', $endWdate), date('i', $endWdate), date('s', $endWdate), date('m', $rstart), date('d', $rstart), date('Y', $rstart));
                             // on a day-basis !!!
                             $datestring = date($endDateFormat, $tend);
                             if (isset($end['tz'])) {
                                 $datestring .= ' ' . $end['tz'];
                             }
                             $propName = !$dueExist ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
                             $component->setProperty($propName, $datestring);
                         }
                         // end if( $dtendExist || $dueExist || $durationExist )
                     }
                     // end if( $rstart > $startWdate )
                     $wd = getdate($rstart);
                     $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty('UID')] = $component->copy();
                     // copy to output
                     $rstart = mktime(date('H', $rstart), date('i', $rstart), date('s', $rstart), date('m', $rstart), date('d', $rstart) + 1, date('Y', $rstart));
                     // step one day
                 }
                 // end while( $rstart <= $endWdate )
             }
             // end if( !isset( $exdatelist[$checkDate] ))
         } elseif ($startWdate >= $startDate) {
             // within period
             $checkDate = mktime(0, 0, 0, date('m', $startWdate), date('d', $startWdate), date('Y', $startWdate));
             // on a day-basis !!!
             if (!isset($exdatelist[$checkDate])) {
                 // exclude any recurrence START date, found in exdatelist
                 $wd = getdate($startWdate);
                 $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty('UID')] = $component->copy();
                 // copy to output
             }
         }
     }
     // end foreach ( $this->components as $cix => $component )
     if (0 >= count($result)) {
         return FALSE;
     } elseif (!$flat) {
         foreach ($result as $y => $yeararr) {
             foreach ($yeararr as $m => $montharr) {
                 foreach ($montharr as $d => $dayarr) {
                     $result[$y][$m][$d] = array_values($dayarr);
                 }
                 // skip tricky UID-index
                 ksort($result[$y][$m]);
             }
             ksort($result[$y]);
         }
         ksort($result);
     }
     // end elseif( !$flat )
     return $result;
 }
 /**
  * select components from calendar on date or selectOption basis
  *
  * Ensure DTSTART is set for every component.
  * No date controls occurs.
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.21.11 - 2015-03-31
  * @param mixed $startY optional,      (int) start Year,  default current Year
  *                                ALT. (obj) start date (datetime)
  *                                ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
  * @param mixed $startM optional,      (int) start Month, default current Month
  *                                ALT. (obj) end date (datetime)
  * @param int   $startD optional, start Day,   default current Day
  * @param int   $endY   optional, end   Year,  default $startY
  * @param int   $endM   optional, end   Month, default $startM
  * @param int   $endD   optional, end   Day,   default $startD
  * @param mixed $cType  optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  * @param bool  $flat   optional, FALSE (default) => output : array[Year][Month][Day][]
  *                                TRUE            => output : array[] (ignores split)
  * @param bool  $any    optional, TRUE (default) - select component(-s) that occurs within period
  *                                FALSE          - only component(-s) that starts within period
  * @param bool  $split  optional, TRUE (default) - one component copy every DAY it occurs during the
  *                                                 period (implies flat=FALSE)
  *                                FALSE          - one occurance of component only in output array
  * @uses vcalendar::$components
  * @uses vcalendar::selectComponents2()
  * @uses iCalUtilityFunctions::$vComps
  * @uses calendarComponent::$objName
  * @uses calendarComponent::getProperty()
  * @uses iCaldateTime::factory()
  * @uses iCaldateTime::getTimezoneName()
  * @uses iCaldateTime::getTime()
  * @uses iCalUtilityFunctions::$fmt
  * @uses iCaldateTime::$SCbools
  * @uses iCaldateTime::format()
  * @uses iCalUtilityFunctions::_strDate2arr()
  * @uses iCalUtilityFunctions::_recur2date()
  * @uses iCalUtilityFunctions::_inScope()
  * @uses calendarComponent::copy()
  * @uses calendarComponent::setProperty()
  * @uses iCalUtilityFunctions::$fmt
  * @uses calendarComponent::deleteProperty()
  * @uses iCalUtilityFunctions::_setSortArgs()
  * @uses iCalUtilityFunctions::_cmpfcn()
  * @return array or FALSE
  */
 function selectComponents($startY = FALSE, $startM = FALSE, $startD = FALSE, $endY = FALSE, $endM = FALSE, $endD = FALSE, $cType = FALSE, $flat = FALSE, $any = TRUE, $split = TRUE)
 {
     /* check  if empty calendar */
     if (0 >= count($this->components)) {
         return FALSE;
     }
     if (is_array($startY)) {
         return $this->selectComponents2($startY);
     }
     /* check default dates */
     if (is_a($startY, 'DateTime') && is_a($startM, 'DateTime')) {
         $endY = $startM->format('Y');
         $endM = $startM->format('m');
         $endD = $startM->format('d');
         $startD = $startY->format('d');
         $startM = $startY->format('m');
         $startY = $startY->format('Y');
     } else {
         if (!$startY) {
             $startY = date('Y');
         }
         if (!$startM) {
             $startM = date('m');
         }
         if (!$startD) {
             $startD = date('d');
         }
         if (!$endY) {
             $endY = $startY;
         }
         if (!$endM) {
             $endM = $startM;
         }
         if (!$endD) {
             $endD = $startD;
         }
     }
     // echo "selectComp args={$startY}-{$startM}-{$startD} - {$endY}-{$endM}-{$endD}<br>\n"; $tcnt = 0;// test ###
     /* check component types */
     if (empty($cType)) {
         $cType = iCalUtilityFunctions::$vComps;
     } else {
         if (!is_array($cType)) {
             $cType = array($cType);
         }
         $cType = array_map('strtolower', $cType);
         foreach ($cType as $cix => $theType) {
             if (!in_array($theType, iCalUtilityFunctions::$vComps)) {
                 $cType[$cix] = 'vevent';
             }
         }
         $cType = array_unique($cType);
     }
     if (FALSE === $flat && FALSE === $any) {
         // invalid combination
         $split = FALSE;
     }
     if (TRUE === $flat && TRUE === $split) {
         // invalid combination
         $split = FALSE;
     }
     /* iterate components */
     $result = array();
     $this->sort('UID');
     $compUIDcmp = null;
     $exdatelist = $recurridList = array();
     $intervalP1D = new DateInterval('P1D');
     foreach ($this->components as $cix => $component) {
         if (empty($component)) {
             continue;
         }
         /* deselect unvalid type components */
         if (!in_array($component->objName, $cType)) {
             continue;
         }
         unset($compStart, $compEnd);
         /* select start from dtstart or due if dtstart is missing */
         $prop = $component->getProperty('dtstart', FALSE, TRUE);
         if (empty($prop) && $component->objName == 'vtodo' && FALSE === ($prop = $component->getProperty('due', FALSE, TRUE))) {
             continue;
         }
         if (empty($prop)) {
             continue;
         }
         /* get UID */
         $compUID = $component->getProperty('UID');
         if ($compUIDcmp != $compUID) {
             $compUIDcmp = $compUID;
             $exdatelist = $recurridList = array();
         }
         $recurrid = FALSE;
         // file_put_contents( '/opt/work/iCal/iCalcreator/iCalcreator-2.20.x/log/log.txt', "#$cix".PHP_EOL.var_export( $component, TRUE ).PHP_EOL.PHP_EOL, FILE_APPEND ); // test ###
         $compStart = iCaldateTime::factory($prop['value'], $prop['params'], $prop['value']);
         $dtstartTz = $compStart->getTimezoneName();
         if (isset($prop['params']['VALUE']) && 'DATE' == $prop['params']['VALUE']) {
             $compStartHis = '';
         } else {
             $his = $compStart->getTime();
             $compStartHis = sprintf(iCalUtilityFunctions::$fmt['His'], (int) $his[0], (int) $his[1], (int) $his[2]);
         }
         /* get end date from dtend/due/duration properties */
         if (FALSE !== ($prop = $component->getProperty('dtend', FALSE, TRUE))) {
             $compEnd = iCaldateTime::factory($prop['value'], $prop['params'], $prop['value'], $dtstartTz);
             $compEnd->SCbools['dtendExist'] = TRUE;
         }
         if (empty($prop) && $component->objName == 'vtodo' && FALSE !== ($prop = $component->getProperty('due', FALSE, TRUE))) {
             $compEnd = iCaldateTime::factory($prop['value'], $prop['params'], $prop['value'], $dtstartTz);
             $compEnd->SCbools['dueExist'] = TRUE;
         }
         if (empty($prop) && FALSE !== ($prop = $component->getProperty('duration', FALSE, TRUE, TRUE))) {
             // in dtend (array) format
             $compEnd = iCaldateTime::factory($prop['value'], $prop['params'], $prop['value'], $dtstartTz);
             $compEnd->SCbools['durationExist'] = TRUE;
         }
         if (!empty($prop) && !isset($prop['value']['hour'])) {
             /* a DTEND without time part denotes an end of an event that actually ends the day before,
                for an all-day event DTSTART=20071201 DTEND=20071202, taking place 20071201!!! */
             $compEnd->SCbools['endAllDayEvent'] = TRUE;
             $compEnd->modify('-1 day');
             $compEnd->setTime(23, 59, 59);
         }
         unset($prop);
         if (empty($compEnd)) {
             $compDuration = FALSE;
             $compEnd = clone $compStart;
             $compEnd->setTime(23, 59, 59);
             //  23:59:59 the same day as start
         } else {
             if ($compEnd->format('Ymd') < $compStart->format('Ymd')) {
                 // MUST be after start date!!
                 $compEnd = clone $compStart;
                 $compEnd->setTime(23, 59, 59);
                 //  23:59:59 the same day as start or ???
             }
             $compDuration = $compStart->diff($compEnd);
             // DateInterval
         }
         /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), to alter when hit dtstart/recurlist */
         if (FALSE !== ($prop = $component->getProperty('recurrence-id', FALSE, TRUE))) {
             $recurrid = iCaldateTime::factory($prop['value'], $prop['params'], $prop['value'], $dtstartTz);
             $rangeSet = isset($prop['params']['RANGE']) && 'THISANDFUTURE' == $prop['params']['RANGE'] ? TRUE : FALSE;
             $recurridList[$recurrid->key] = array(clone $compStart, clone $compEnd, $compDuration, $rangeSet);
             // change recur this day to new YmdHis/duration/range
             // echo "adding comp no:$cix with date=".$compStart->format(iCalUtilityFunctions::$fmt['YmdHis2e'])." to recurridList id={$recurrid->key}, newDate={$compStart->key}<br>\n"; // test ###
             unset($prop);
             continue;
             // ignore any other props in the component
         }
         // end recurrence-id/sequence test
         // else echo "comp no:$cix with date=".$compStart->format().", NO recurrence-id<br>\n"; // test ###
         ksort($recurridList, SORT_STRING);
         // echo 'recurridList='.implode(', ', array_keys( $recurridList ))."<br>\n"; // test ###
         $fcnStart = clone $compStart;
         $fcnStart->setDate((int) $startY, (int) $startM, (int) $startD);
         $fcnStart->setTime(0, 0, 0);
         $fcnEnd = clone $compEnd;
         $fcnEnd->setDate((int) $endY, (int) $endM, (int) $endD);
         $fcnEnd->setTime(23, 59, 59);
         // echo 'compStart='.$compStart->format().', compEnd'.$compEnd->format(); if($compDuration)echo ', interval='.$compDuration->format( iCalUtilityFunctions::$fmt['durDHis'] ); echo "<br>\n"; $tcnt = 0;// test ###
         /* *************************************************************
            make a list of optional exclude dates for component occurence from exrule and exdate
            *********************************************************** */
         $workStart = clone $compStart;
         $workStart->sub($compDuration ? $compDuration : $intervalP1D);
         $workEnd = clone $fcnEnd;
         $workEnd->add($compDuration ? $compDuration : $intervalP1D);
         while (FALSE !== ($prop = $component->getProperty('EXRULE'))) {
             $exdatelist2 = array();
             if (isset($prop['UNTIL']['hour'])) {
                 // convert until date to dtstart timezone
                 $until = iCaldateTime::factory($prop['UNTIL'], array('TZID' => 'UTC'), null, $dtstartTz);
                 $until = $until->format();
                 iCalUtilityFunctions::_strDate2arr($until);
                 $prop['UNTIL'] = $until;
             }
             iCalUtilityFunctions::_recur2date($exdatelist2, $prop, $compStart, $workStart, $workEnd);
             foreach ($exdatelist2 as $k => $v) {
                 $exdatelist[$k . $compStartHis] = $v;
             }
             // point out exact every excluded ocurrence (incl. opt. His)
             unset($until, $exdatelist2);
         }
         while (FALSE !== ($prop = $component->getProperty('EXDATE', FALSE, TRUE))) {
             // - start check exdate
             foreach ($prop['value'] as $exdate) {
                 $exdate = iCaldateTime::factory($exdate, $prop['params'], $exdate, $dtstartTz);
                 $exdatelist[$exdate->key] = TRUE;
             }
             // end - foreach( $exdate as $exdate )
         }
         // end - check exdate
         unset($prop, $exdate);
         // echo 'exdatelist='  .implode(', ', array_keys( $exdatelist ))  ."<br>\n"; // test ###
         /* *************************************************************
            select only components within.. .
            *********************************************************** */
         $xRecurrence = 1;
         if (!$any && iCalUtilityFunctions::_inScope($compStart, $fcnStart, $compStart, $fcnEnd, $compStart->dateFormat) || $any && iCalUtilityFunctions::_inScope($fcnEnd, $compStart, $fcnStart, $compEnd, $compStart->dateFormat)) {
             /* add the selected component (WITHIN valid dates) to output array */
             if ($flat) {
                 // any=true/false, ignores split
                 if (!$recurrid) {
                     $result[$compUID] = $component->copy();
                 }
                 // copy original to output (but not anyone with recurrence-id)
             } elseif ($split) {
                 // split the original component
                 // echo 'split org.:'.$compStart->format().' < '.$fcnStart->format( 'Ymd His e' )."<br>\n"; // test ###
                 if ($compStart->format(iCalUtilityFunctions::$fmt['YmdHis2']) < $fcnStart->format(iCalUtilityFunctions::$fmt['YmdHis2'])) {
                     $rstart = clone $fcnStart;
                 } else {
                     $rstart = clone $compStart;
                 }
                 if ($compEnd->format(iCalUtilityFunctions::$fmt['YmdHis2']) > $fcnEnd->format(iCalUtilityFunctions::$fmt['YmdHis2'])) {
                     $rend = clone $fcnEnd;
                 } else {
                     $rend = clone $compEnd;
                 }
                 // echo "going to test comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###
                 if (!isset($exdatelist[$rstart->key])) {
                     // not excluded in exrule/exdate
                     if (isset($recurridList[$rstart->key])) {
                         // change start day to new YmdHis/duration
                         $k = $rstart->key;
                         // echo "recurridList HIT, key={$k}, recur Date=".$recurridList[$k][0]->key."<br>\n"; // test ###
                         $rstart = clone $recurridList[$k][0];
                         $startHis = $rstart->getTime();
                         $rend = clone $rstart;
                         if (FALSE !== $recurridList[$k][2]) {
                             $rend->add($recurridList[$k][2]);
                         } elseif (FALSE !== $compDuration) {
                             $rend->add($compDuration);
                         }
                         $endHis = $rend->getTime();
                         unset($recurridList[$k]);
                     } else {
                         $startHis = $compStart->getTime();
                         $endHis = $compEnd->getTime();
                     }
                     // echo "_____testing comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###
                     $cnt = 0;
                     // exclude any recurrence START date, found in exdatelist or recurridList but accept the reccurence-id comp itself
                     $occurenceDays = 1 + (int) $rstart->diff($rend)->format('%a');
                     // count the days (incl start day)
                     while ($rstart->format(iCalUtilityFunctions::$fmt['Ymd2']) <= $rend->format(iCalUtilityFunctions::$fmt['Ymd2'])) {
                         $cnt += 1;
                         if (1 < $occurenceDays) {
                             $component->setProperty('X-OCCURENCE', sprintf(iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays));
                         }
                         if (1 < $cnt) {
                             $rstart->setTime(0, 0, 0);
                         } else {
                             $rstart->setTime($startHis[0], $startHis[1], $startHis[2]);
                             $exdatelist[$rstart->key] = $compDuration;
                             // make sure to exclude start day from the recurrence pattern
                         }
                         $component->setProperty('X-CURRENT-DTSTART', $rstart->format($compStart->dateFormat));
                         if (FALSE !== $compDuration) {
                             $propName = isset($compEnd->SCbools['dueExist']) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
                             if ($cnt < $occurenceDays) {
                                 $rstart->setTime(23, 59, 59);
                             } else {
                                 $rstart->setTime($endHis[0], $endHis[1], $endHis[2]);
                             }
                             $component->setProperty($propName, $rstart->format($compEnd->dateFormat));
                         }
                         $result[(int) $rstart->format('Y')][(int) $rstart->format('m')][(int) $rstart->format('d')][$compUID] = $component->copy();
                         // copy to output
                         $rstart->add($intervalP1D);
                     }
                     // end while(( $rstart->format( 'Ymd' ) < $rend->format( 'Ymd' ))
                     unset($cnt, $occurenceDays);
                 }
                 // end if( ! isset( $exdatelist[$rstart->key] ))
                 // else echo "skip no:$cix with date=".$compStart->format()."<br>\n"; // test ###
                 unset($rstart, $rend);
             } else {
                 // !$flat && !$split, i.e. no flat array and DTSTART within period
                 $tstart = isset($recurridList[$compStart->key]) ? clone $recurridList[$k][0] : clone $compStart;
                 // echo "going to test comp no:$cix with checkDate={$compStart->key} with recurridList=".implode(',',array_keys($recurridList)); // test ###
                 if (!$any || !isset($exdatelist[$tstart->key])) {
                     // exclude any recurrence date, found in exdatelist
                     // echo " and copied to output<br>\n"; // test ###
                     $result[(int) $tstart->format('Y')][(int) $tstart->format('m')][(int) $tstart->format('d')][$compUID] = $component->copy();
                     // copy to output
                 }
                 unset($tstart);
             }
         }
         // end (dt)start within the period OR occurs within the period
         /* *************************************************************
            if 'any' components, check components with reccurrence rules, removing all excluding dates
            *********************************************************** */
         if (TRUE === $any) {
             $recurlist = array();
             /* make a list of optional repeating dates for component occurence, rrule, rdate */
             while (FALSE !== ($prop = $component->getProperty('RRULE'))) {
                 // get all rrule dates (multiple values allowed)
                 $recurlist2 = array();
                 if (isset($prop['UNTIL']['hour'])) {
                     // convert $rrule['UNTIL'] to the same timezone as DTSTART !!
                     $until = iCaldateTime::factory($prop['UNTIL'], array('TZID' => 'UTC'), null, $dtstartTz);
                     $until = $until->format();
                     iCalUtilityFunctions::_strDate2arr($until);
                     $prop['UNTIL'] = $until;
                 }
                 iCalUtilityFunctions::_recur2date($recurlist2, $prop, $compStart, $workStart, $workEnd);
                 foreach ($recurlist2 as $recurkey => $recurvalue) {
                     // recurkey=Ymd
                     $recurkey .= $compStartHis;
                     // add opt His
                     if (!isset($exdatelist[$recurkey])) {
                         $recurlist[$recurkey] = $compDuration;
                     }
                     // DateInterval or FALSE
                 }
                 unset($prop, $until, $recurlist2);
             }
             $workStart = clone $fcnStart;
             $workStart->sub($compDuration ? $compDuration : $intervalP1D);
             $format = $compStart->dateFormat;
             while (FALSE !== ($prop = $component->getProperty('RDATE', FALSE, TRUE))) {
                 $rdateFmt = isset($prop['params']['VALUE']) ? $prop['params']['VALUE'] : 'DATETIME';
                 $params = $prop['params'];
                 $prop = $prop['value'];
                 foreach ($prop as $theRdate) {
                     if ('PERIOD' == $rdateFmt) {
                         // all days within PERIOD
                         $rdate = iCaldateTime::factory($theRdate[0], $params, $theRdate[0], $dtstartTz);
                         if (!iCalUtilityFunctions::_inScope($rdate, $workStart, $rdate, $fcnEnd, $format) || isset($exdatelist[$rdate->key])) {
                             continue;
                         }
                         if (isset($theRdate[1]['year'])) {
                             // date-date period end
                             $recurlist[$rdate->key] = $rdate->diff(iCaldateTime::factory($theRdate[1], $params, $theRdate[1], $dtstartTz));
                         } else {
                             // period duration
                             $recurlist[$rdate->key] = new DateInterval(iCalUtilityFunctions::_duration2str($theRdate[1]));
                         }
                     } elseif ('DATE' == $rdateFmt) {
                         // single recurrence, date
                         $rdate = iCaldateTime::factory($theRdate, array_merge($params, array('TZID' => $dtstartTz)), null, $dtstartTz);
                         if (iCalUtilityFunctions::_inScope($rdate, $workStart, $rdate, $fcnEnd, $format) && !isset($exdatelist[$rdate->key])) {
                             $recurlist[$rdate->key . $compStartHis] = $compDuration;
                         }
                         // set start date for recurrence + DateInterval/FALSE (+opt His)
                     } else {
                         // start DATETIME
                         $rdate = iCaldateTime::factory($theRdate, $params, $theRdate, $dtstartTz);
                         if (iCalUtilityFunctions::_inScope($rdate, $workStart, $rdate, $fcnEnd, $format) && !isset($exdatelist[$rdate->key])) {
                             $recurlist[$rdate->key] = $compDuration;
                         }
                         // set start datetime for recurrence DateInterval/FALSE
                     }
                     // end DATETIME
                 }
                 // end foreach( $prop as $theRdate )
             }
             // end while( FALSE !== ( $prop = $component->getProperty( 'rdate', FALSE, TRUE )))
             unset($prop, $workStart, $format, $theRdate, $rdate, $rend);
             foreach ($recurridList as $rKey => $rVal) {
                 // check for recurrence-id, i.e. alter recur Ymd[His] and duration
                 if (isset($recurlist[$rKey])) {
                     unset($recurlist[$rKey]);
                     $recurlist[$rVal[0]->key] = FALSE !== $rVal[2] ? $rVal[2] : $compDuration;
                     // echo "alter recurfrom {$rKey} to {$rVal[0]->key} ";if(FALSE!==$dur)echo " ({$dur->format( '%a days, %h-%i-%s' )})";echo "<br>\n"; // test ###
                 }
             }
             ksort($recurlist, SORT_STRING);
             // echo 'recurlist='   .implode(', ', array_keys( $recurlist ))   ."<br>\n"; // test ###
             // echo 'recurridList='   .implode(', ', array_keys( $recurridList ))   ."<br>\n"; // test ###
             /* *************************************************************
                output all remaining components in recurlist
                *********************************************************** */
             if (0 < count($recurlist)) {
                 $component2 = $component->copy();
                 $compUID = $component2->getProperty('UID');
                 $workStart = clone $fcnStart;
                 $workStart->sub($compDuration ? $compDuration : $intervalP1D);
                 $YmdOld = null;
                 foreach ($recurlist as $recurkey => $durvalue) {
                     if ($YmdOld == substr($recurkey, 0, 8)) {
                         // skip overlapping recur the same day, i.e. RDATE before RRULE
                         continue;
                     }
                     $YmdOld = substr($recurkey, 0, 8);
                     $rstart = clone $compStart;
                     $rstart->setDate((int) substr($recurkey, 0, 4), (int) substr($recurkey, 4, 2), (int) substr($recurkey, 6, 2));
                     $rstart->setTime((int) substr($recurkey, 8, 2), (int) substr($recurkey, 10, 2), (int) substr($recurkey, 12, 2));
                     // echo "recur start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###;
                     /* add recurring components within valid dates to output array, only start date set */
                     if ($flat) {
                         if (!isset($result[$compUID])) {
                             // only one comp
                             $result[$compUID] = $component2->copy();
                         }
                         // copy to output
                     } elseif ($split) {
                         $rend = clone $rstart;
                         if (FALSE !== $durvalue) {
                             $rend->add($durvalue);
                         }
                         if ($rend->format(iCalUtilityFunctions::$fmt['Ymd2']) > $fcnEnd->format(iCalUtilityFunctions::$fmt['Ymd2'])) {
                             $rend = clone $fcnEnd;
                         }
                         // echo "recur 1={$recurkey}, start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] ).", end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] );if($durvalue) echo ", duration=".$durvalue->format( iCalUtilityFunctions::$fmt['durDHis'] );echo "<br>\n"; // test ###
                         $xRecurrence += 1;
                         $cnt = 0;
                         $occurenceDays = 1 + (int) $rstart->diff($rend)->format('%a');
                         // count the days (incl start day)
                         while ($rstart->format(iCalUtilityFunctions::$fmt['Ymd2']) <= $rend->format(iCalUtilityFunctions::$fmt['Ymd2'])) {
                             // iterate.. .
                             $cnt += 1;
                             if ($rstart->format(iCalUtilityFunctions::$fmt['Ymd2']) < $fcnStart->format(iCalUtilityFunctions::$fmt['Ymd2'])) {
                                 // date before dtstart
                                 // echo "recur 3, start=".$rstart->format( 'Y-m-d H:i:s' )." &gt;= fcnStart=".$fcnStart->format( 'Y-m-d H:i:s' )."<br>\n"; // test ###
                                 $rstart->add($intervalP1D);
                                 $rstart->setTime(0, 0, 0);
                                 continue;
                             } elseif (2 == $cnt) {
                                 $rstart->setTime(0, 0, 0);
                             }
                             $component2->setProperty('X-RECURRENCE', $xRecurrence);
                             if (1 < $occurenceDays) {
                                 $component2->setProperty('X-OCCURENCE', sprintf(iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays));
                             } else {
                                 $component2->deleteProperty('X-OCCURENCE');
                             }
                             $component2->setProperty('X-CURRENT-DTSTART', $rstart->format($compStart->dateFormat));
                             $propName = isset($compEnd->SCbools['dueExist']) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
                             if (FALSE !== $durvalue) {
                                 if ($cnt < $occurenceDays) {
                                     $rstart->setTime(23, 59, 59);
                                 } else {
                                     $His = $rend->getTime();
                                     // set end time
                                     $rstart->setTime($His[0], $His[1], $His[2]);
                                 }
                                 $component2->setProperty($propName, $rstart->format($compEnd->dateFormat));
                                 // echo "checking date, (day {$cnt} of {$occurenceDays}), _end_=".$rstart->format( 'Y-m-d H:i:s e' )."<br>"; // test ###;
                             } else {
                                 $component2->deleteProperty($propName);
                             }
                             $result[(int) $rstart->format('Y')][(int) $rstart->format('m')][(int) $rstart->format('d')][$compUID] = $component2->copy();
                             // copy to output
                             $rstart->add($intervalP1D);
                         }
                         // end while( $rstart->format( 'Ymd' ) <= $rend->format( 'Ymd' ))
                         unset($rstart, $rend);
                     } elseif ($rstart->format(iCalUtilityFunctions::$fmt['Ymd2']) >= $fcnStart->format(iCalUtilityFunctions::$fmt['Ymd2'])) {
                         $xRecurrence += 1;
                         // date within period  //* flat=FALSE && split=FALSE => one comp every recur startdate *//
                         $component2->setProperty('X-RECURRENCE', $xRecurrence);
                         $component2->setProperty('X-CURRENT-DTSTART', $rstart->format($compStart->dateFormat));
                         $propName = isset($compEnd->SCbools['dueExist']) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
                         if (FALSE !== $durvalue) {
                             $rstart->add($durvalue);
                             $component2->setProperty($propName, $rstart->format($compEnd->dateFormat));
                         } else {
                             $component2->deleteProperty($propName);
                         }
                         $result[(int) $rstart->format('Y')][(int) $rstart->format('m')][(int) $rstart->format('d')][$compUID] = $component2->copy();
                         // copy to output
                     }
                     // end elseif( $rstart >= $fcnStart )
                     unset($rstart);
                 }
                 // end foreach( $recurlist as $recurkey => $durvalue )
                 unset($component2, $xRecurrence, $compUID, $workStart, $rstart);
             }
             // end if( 0 < count( $recurlist ))
         }
         // end if( TRUE === $any )
         unset($component);
     }
     // end foreach ( $this->components as $cix => $component )
     unset($recurrid, $recurridList, $fcnStart, $fcnEnd, $compStart, $compEnd, $exdatelist, $recurlist);
     // clean up
     if (0 >= count($result)) {
         return FALSE;
     } elseif (!$flat) {
         foreach ($result as $y => $yeararr) {
             foreach ($yeararr as $m => $montharr) {
                 foreach ($montharr as $d => $dayarr) {
                     if (empty($result[$y][$m][$d])) {
                         unset($result[$y][$m][$d]);
                     } else {
                         $result[$y][$m][$d] = array_values($dayarr);
                         // skip tricky UID-index
                         if (1 < count($result[$y][$m][$d])) {
                             foreach ($result[$y][$m][$d] as &$c) {
                                 // sort
                                 iCalUtilityFunctions::_setSortArgs($c);
                             }
                             usort($result[$y][$m][$d], array('iCalUtilityFunctions', '_cmpfcn'));
                         }
                     }
                 }
                 // end foreach( $montharr as $d => $dayarr )
                 if (empty($result[$y][$m])) {
                     unset($result[$y][$m]);
                 } else {
                     ksort($result[$y][$m]);
                 }
             }
             // end foreach( $yeararr as $m => $montharr )
             if (empty($result[$y])) {
                 unset($result[$y]);
             } else {
                 ksort($result[$y]);
             }
         }
         // end foreach( $result as $y => $yeararr )
         if (empty($result)) {
             unset($result);
         } else {
             ksort($result);
         }
     }
     // end elseif( !$flat )
     if (0 >= count($result)) {
         return FALSE;
     }
     return $result;
 }
 /**
  * Create list of recurrent instances.
  *
  * @param Ai1ec_Event $event          Event to generate instances for.
  * @param array       $event_instance First instance contents.
  * @param int         $_start         Timestamp of first occurence.
  * @param int         $tif            Timestamp of last occurence.
  * @param int         $duration       Event duration in seconds.
  * @param string      $timezone       Target timezone.
  *
  * @return array List of event instances.
  */
 public function create_instances_by_recurrence(Ai1ec_Event $event, array $event_instance, $_start, $tif, $duration, $timezone)
 {
     $recurrence_parser = $this->_registry->get('recurrence.rule');
     $evs = array();
     $startdate = array('timestamp' => $_start, 'tz' => $timezone);
     $enddate = array('timestamp' => $tif, 'tz' => $timezone);
     $start = $event_instance['start'];
     $wdate = $startdate = iCalUtilityFunctions::_timestamp2date($startdate, 6);
     $enddate = iCalUtilityFunctions::_timestamp2date($enddate, 6);
     $exclude_dates = array();
     $recurrence_dates = array();
     if ($event->get('exception_rules')) {
         // creat an array for the rules
         $exception_rules = $recurrence_parser->build_recurrence_rules_array($event->get('exception_rules'));
         $exception_rules = iCalUtilityFunctions::_setRexrule($exception_rules);
         $result = array();
         // The first array is the result and it is passed by reference
         iCalUtilityFunctions::_recur2date($exclude_dates, $exception_rules, $wdate, $startdate, $enddate);
     }
     $recurrence_rules = $recurrence_parser->build_recurrence_rules_array($event->get('recurrence_rules'));
     $recurrence_rules = iCalUtilityFunctions::_setRexrule($recurrence_rules);
     iCalUtilityFunctions::_recur2date($recurrence_dates, $recurrence_rules, $wdate, $startdate, $enddate);
     $recurrence_dates = array_keys($recurrence_dates);
     // Add the instances
     foreach ($recurrence_dates as $date) {
         // The arrays are in the form timestamp => true so an isset call is what we need
         if (isset($exclude_dates[$date])) {
             continue;
         }
         $event_instance['start'] = $date;
         $event_instance['end'] = $date + $duration;
         $excluded = false;
         // Check if exception dates match this occurence
         if ($exception_dates = $event->get('exception_dates')) {
             $match_exdates = $this->date_match_exdates($date, $exception_dates, $timezone);
             if ($match_exdates) {
                 $excluded = true;
             }
         }
         // Add event only if it is not excluded
         if (false === $excluded) {
             $evs[] = $event_instance;
         }
     }
     return $evs;
 }