/** * step date, return updated date, array and timpstamp * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.4.16 - 2008-10-18 * @param array $date, date to step * @param int $timestamp * @param array $step, default array( 'day' => 1 ) * @return void */ public static function _stepdate(&$date, &$timestamp, $step = array('day' => 1)) { foreach ($step as $stepix => $stepvalue) { $date[$stepix] += $stepvalue; } $timestamp = iCal_UtilityFunctions::_date2timestamp($date); $date = iCal_UtilityFunctions::_timestamp2date($timestamp, 6); foreach ($date as $k => $v) { if (ctype_digit($v)) { $date[$k] = (int) $v; } } }
/** * 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.10.13 - 2011-09-23 * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions * @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; } 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); } else { if (!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 (TRUE === $flat && TRUE === $split) { // invalid combination $split = FALSE; } // end validate and reset arguments.. /* iterate components */ $result = array(); foreach ($this->components as $cix => $component) { //print_r($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; } $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE; unset($end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat); // clean up $startWdate = iCal_UtilityFunctions::_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 = iCal_UtilityFunctions::_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 = iCal_UtilityFunctions::_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 = iCal_UtilityFunctions::_timestamp2date($startDate - $rdurWsecs, 6); $workend = iCal_UtilityFunctions::_timestamp2date($endDate + $rdurWsecs, 6); while (FALSE !== ($exrule = $component->getProperty('exrule'))) { // check exrule iCal_UtilityFunctions::_recur2date($exdatelist, $exrule, $start, $workstart, $workend); } while (FALSE !== ($exdate = $component->getProperty('exdate'))) { // check exdate foreach ($exdate as $theExdate) { $exWdate = iCal_UtilityFunctions::_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 //var_dump(array($startWdate ,$startDate , $startWdate , $endDate ));exit; /* select only components with startdate within period */ if ($startWdate >= $startDate && $startWdate <= $endDate) { /* add the selected component (WITHIN valid dates) to output array */ if ($flat) { $result[$component->getProperty('UID')] = $component->copy(); // copy original to output; } 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 !!! $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); if (!isset($result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty('UID')])) { $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 ) //print_R($result);exit; } // end if( $split ) - else use component date } // 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 iCal_UtilityFunctions::_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 = iCal_UtilityFunctions::_date2timestamp($theRdate[0]); if ($rstart < $startDate - $rdurWsecs || $rstart > $endDate) { continue; } if (isset($theRdate[1]['year'])) { // date-date period $rend = iCal_UtilityFunctions::_date2timestamp($theRdate[1]); } else { // date-duration period $rend = iCal_UtilityFunctions::_duration2date($theRdate[0], $theRdate[1]); $rend = iCal_UtilityFunctions::_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 = iCal_UtilityFunctions::_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; foreach ($recurlist as $recurkey => $durvalue) { // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCal_UtilityFunctions::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; } $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']; } // echo "X-CURRENT-DTSTART 0 =$datestring tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ### $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) { 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 !!! $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); if (!isset($result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty('UID')])) { $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty('UID')] = $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 *// $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) { $rstart += $rdurWsecs; if (date('Ymd', $rstart) < date('Ymd', $endWdate)) { $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 !!! $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); if (!isset($result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty('UID')])) { $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; } } // end if( TRUE === $any ) } // end foreach ( $this->components as $cix => $component ) if (0 >= count($result)) { return FALSE; } if (!$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, hoping they are in hour order.. . ksort($result[$y][$m]); } ksort($result[$y]); } ksort($result); } // end elseif( !$flat ) //print_R($result); return $result; }