/** * 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); } } }
/** * 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' )." >= 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; }