/** * set calendar component property rdate * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.21.11 - 2015-03-10 * @param array $rdates * @param array $params * @param integer $index * @uses calendarComponent::getConfig() * @uses iCalUtilityFunctions::_setMval() * @uses calendarComponent::$rdate * @uses iCalUtilityFunctions::_setParams() * @uses calendarComponent::$objName * @uses iCalUtilityFunctions::_isArrayDate() * @uses iCalUtilityFunctions::_chkdatecfg() * @uses iCalUtilityFunctions::_existRem() * @uses iCalUtilityFunctions::_strDate2arr() * @uses iCalUtilityFunctions::_isArrayTimestampDate() * @uses iCalUtilityFunctions::_isOffset() * @uses iCalUtilityFunctions::_timestamp2date() * @uses iCalUtilityFunctions::_chkDateArr() * @uses iCalUtilityFunctions::$fmt * @uses iCalUtilityFunctions::_strdate2date() * @uses iCalUtilityFunctions::_duration2arr() * @uses iCalUtilityFunctions::_durationStr2arr() * @return bool */ function setRdate($rdates, $params = FALSE, $index = FALSE) { if (empty($rdates)) { if ($this->getConfig('allowEmpty')) { iCalUtilityFunctions::_setMval($this->rdate, '', $params, FALSE, $index); return TRUE; } else { return FALSE; } } $input = array('params' => iCalUtilityFunctions::_setParams($params, array('VALUE' => 'DATE-TIME'))); if (in_array($this->objName, array('vtimezone', 'standard', 'daylight'))) { unset($input['params']['TZID']); $input['params']['VALUE'] = 'DATE-TIME'; } $zArr = array('GMT', 'UTC', 'Z'); $toZ = isset($params['TZID']) && in_array(strtoupper($params['TZID']), $zArr) ? TRUE : FALSE; /* check if PERIOD, if not set */ if ((!isset($input['params']['VALUE']) || !in_array($input['params']['VALUE'], array('DATE', 'PERIOD'))) && isset($rdates[0]) && is_array($rdates[0]) && 2 == count($rdates[0]) && isset($rdates[0][0]) && isset($rdates[0][1]) && !isset($rdates[0]['timestamp']) && (is_array($rdates[0][0]) && (isset($rdates[0][0]['timestamp']) || iCalUtilityFunctions::_isArrayDate($rdates[0][0])) || is_string($rdates[0][0]) && 8 <= strlen(trim($rdates[0][0]))) && (is_array($rdates[0][1]) || is_string($rdates[0][1]) && 3 <= strlen(trim($rdates[0][1])))) { $input['params']['VALUE'] = 'PERIOD'; } /* check 1:st date, upd. $parno (opt) and save ev. timezone **/ $date = reset($rdates); if (isset($input['params']['VALUE']) && 'PERIOD' == $input['params']['VALUE']) { // PERIOD $date = reset($date); } iCalUtilityFunctions::_chkdatecfg($date, $parno, $input['params']); iCalUtilityFunctions::_existRem($input['params'], 'VALUE', 'DATE-TIME'); // remove default foreach ($rdates as $rpix => $theRdate) { $inputa = null; iCalUtilityFunctions::_strDate2arr($theRdate); if (is_array($theRdate)) { if (isset($input['params']['VALUE']) && 'PERIOD' == $input['params']['VALUE']) { // PERIOD foreach ($theRdate as $rix => $rPeriod) { iCalUtilityFunctions::_strDate2arr($theRdate); if (is_array($rPeriod)) { if (iCalUtilityFunctions::_isArrayTimestampDate($rPeriod)) { // timestamp if (isset($rPeriod['tz']) && !iCalUtilityFunctions::_isOffset($rPeriod['tz'])) { if (isset($input['params']['TZID'])) { $rPeriod['tz'] = $input['params']['TZID']; } else { $input['params']['TZID'] = $rPeriod['tz']; } } $inputab = iCalUtilityFunctions::_timestamp2date($rPeriod, $parno); } elseif (iCalUtilityFunctions::_isArrayDate($rPeriod)) { $d = 3 < count($rPeriod) ? iCalUtilityFunctions::_chkDateArr($rPeriod, $parno) : iCalUtilityFunctions::_chkDateArr($rPeriod, 6); if (isset($d['tz']) && 'Z' != $d['tz'] && iCalUtilityFunctions::_isOffset($d['tz'])) { $strdate = sprintf(iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz']); $inputab = iCalUtilityFunctions::_strdate2date($strdate, 7); unset($inputab['unparsedtext']); } else { $inputab = $d; } } elseif (1 == count($rPeriod) && 8 <= strlen(reset($rPeriod))) { // text-date $inputab = iCalUtilityFunctions::_strdate2date(reset($rPeriod), $parno); unset($inputab['unparsedtext']); } else { // array format duration $inputab = iCalUtilityFunctions::_duration2arr($rPeriod); } } elseif (3 <= strlen(trim($rPeriod)) && in_array($rPeriod[0], array('P', '+', '-'))) { if ('P' != $rPeriod[0]) { $rPeriod = substr($rPeriod, 1); } $inputab = iCalUtilityFunctions::_durationStr2arr($rPeriod); } elseif (8 <= strlen(trim($rPeriod))) { // text date ex. 2006-08-03 10:12:18 $inputab = iCalUtilityFunctions::_strdate2date($rPeriod, $parno); unset($inputab['unparsedtext']); } if (0 == $rpix && 0 == $rix) { if (isset($inputab['tz']) && in_array(strtoupper($inputab['tz']), $zArr)) { $inputab['tz'] = 'Z'; $toZ = TRUE; } } else { if (isset($inputa[0]['tz']) && 'Z' == $inputa[0]['tz'] && isset($inputab['year'])) { $inputab['tz'] = 'Z'; } else { unset($inputab['tz']); } } if ($toZ && isset($inputab['year'])) { $inputab['tz'] = 'Z'; } $inputa[] = $inputab; } } elseif (iCalUtilityFunctions::_isArrayTimestampDate($theRdate)) { // timestamp if (isset($theRdate['tz']) && !iCalUtilityFunctions::_isOffset($theRdate['tz'])) { if (isset($input['params']['TZID'])) { $theRdate['tz'] = $input['params']['TZID']; } else { $input['params']['TZID'] = $theRdate['tz']; } } $inputa = iCalUtilityFunctions::_timestamp2date($theRdate, $parno); } else { // date[-time] $inputa = iCalUtilityFunctions::_chkDateArr($theRdate, $parno); if (isset($inputa['tz']) && 'Z' != $inputa['tz'] && iCalUtilityFunctions::_isOffset($inputa['tz'])) { $strdate = sprintf(iCalUtilityFunctions::$fmt['YmdHise'], (int) $inputa['year'], (int) $inputa['month'], (int) $inputa['day'], (int) $inputa['hour'], (int) $inputa['min'], (int) $inputa['sec'], $inputa['tz']); $inputa = iCalUtilityFunctions::_strdate2date($strdate, 7); unset($inputa['unparsedtext']); } } } elseif (8 <= strlen(trim($theRdate))) { // text date ex. 2006-08-03 10:12:18 $inputa = iCalUtilityFunctions::_strdate2date($theRdate, $parno); unset($inputa['unparsedtext']); if ($toZ) { $inputa['tz'] = 'Z'; } } if (!isset($input['params']['VALUE']) || 'PERIOD' != $input['params']['VALUE']) { // no PERIOD if (0 == $rpix && !$toZ) { $toZ = isset($inputa['tz']) && in_array(strtoupper($inputa['tz']), $zArr) ? TRUE : FALSE; } if ($toZ) { $inputa['tz'] = 'Z'; } if (3 == $parno) { unset($inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz']); } elseif (isset($inputa['tz'])) { $inputa['tz'] = (string) $inputa['tz']; } if (isset($input['params']['TZID']) || isset($input['value'][0]) && !isset($input['value'][0]['tz'])) { if (!$toZ) { unset($inputa['tz']); } } } $input['value'][] = $inputa; } if (3 == $parno) { $input['params']['VALUE'] = 'DATE'; unset($input['params']['TZID']); } if ($toZ) { unset($input['params']['TZID']); } iCalUtilityFunctions::_setMval($this->rdate, $input['value'], $input['params'], FALSE, $index); return TRUE; }
public static function _strdate2date($datetime, $parno = FALSE, $wtz = null) { // save original input string to return it later $unparseddatetime = $datetime; $datetime = (string) trim($datetime); $tz = null; $offset = 0; $tzSts = FALSE; $len = strlen($datetime); if ('Z' == substr($datetime, -1)) { $tz = 'Z'; $datetime = trim(substr($datetime, 0, $len - 1)); $tzSts = TRUE; $len = 88; } if (iCalUtilityFunctions::_isOffset(substr($datetime, -5, 5))) { // [+/-]NNNN offset $tz = substr($datetime, -5, 5); $datetime = trim(substr($datetime, 0, $len - 5)); $len = strlen($datetime); } elseif (iCalUtilityFunctions::_isOffset(substr($datetime, -7, 7))) { // [+/-]NNNNNN offset $tz = substr($datetime, -7, 7); $datetime = trim(substr($datetime, 0, $len - 7)); $len = strlen($datetime); } elseif (empty($wtz) && ctype_digit(substr($datetime, 0, 4)) && ctype_digit(substr($datetime, -2, 2)) && iCalUtilityFunctions::_strDate2arr($datetime)) { $output = $datetime; if (!empty($tz)) { $output['tz'] = 'Z'; } $output['unparsedtext'] = $unparseddatetime; return $output; } else { $cx = $tx = 0; // find any trailing timezone or offset for ($cx = -1; $cx > 9 - $len; $cx--) { $char = substr($datetime, $cx, 1); if (' ' == $char || ctype_digit($char)) { break; } else { $tx--; } // tz length counter } if (0 > $tx) { // if any $tz = substr($datetime, $tx); $datetime = trim(substr($datetime, 0, $len + $tx)); $len = strlen($datetime); } if (17 <= $len || ctype_digit(substr($datetime, 0, 8)) && 'T' == substr($datetime, 8, 1) && ctype_digit(substr($datetime, -6, 6)) || ctype_digit(substr($datetime, 0, 14))) { $len = 88; $tzSts = TRUE; } else { $tz = null; } // no tz for Y-m-d dates } if (empty($tz) && !empty($wtz)) { $tz = $wtz; } if (17 >= $len) { // any Y-m-d textual date $tz = null; } if (!empty($tz) && 17 < $len) { // tz set AND long textual datetime if ('Z' != $tz && iCalUtilityFunctions::_isOffset($tz)) { $offset = (string) iCalUtilityFunctions::_tz2offset($tz) * -1; $tz = 'UTC'; $tzSts = TRUE; } elseif (!empty($wtz)) { $tzSts = TRUE; } $tz = trim($tz); if ('Z' == $tz || 'GMT' == strtoupper($tz)) { $tz = 'UTC'; } if (0 < substr_count($datetime, '-')) { $datetime = str_replace('-', '/', $datetime); } try { $d = new DateTime($datetime, new DateTimeZone($tz)); if (0 != $offset) { // adjust for offset $d->modify($offset . ' seconds'); } $datestring = $d->format('Y-m-d-H-i-s'); unset($d); } catch (Exception $e) { $datestring = date('Y-m-d-H-i-s', strtotime($datetime)); } } else { $datestring = date('Y-m-d-H-i-s', strtotime($datetime)); } // echo "<tr><td> <td colspan='3'>_strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n"; if ('UTC' == $tz) { $tz = 'Z'; } $d = explode('-', $datestring); $output = array('year' => $d[0], 'month' => $d[1], 'day' => $d[2]); if (FALSE !== $parno && 3 != $parno || FALSE === $parno && 'Z' == $tz || FALSE === $parno && 'Z' != $tz && 0 != $d[3] + $d[4] + $d[5] && 17 < $len) { // !parno and !UTC and 0 != hour+min+sec and long input text $output['hour'] = $d[3]; $output['min'] = $d[4]; $output['sec'] = $d[5]; if (($tzSts || 7 == $parno) && !empty($tz)) { $output['tz'] = $tz; } } // return original string in the array in case strtotime failed to make sense of it $output['unparsedtext'] = $unparseddatetime; return $output; }
/** * ensures internal date-time/date format for input date-time/date in string fromat * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.21.11 - 2015-03-15 * Modified to also return original string value by Yitzchok Lavi <*****@*****.**> * @param array $datetime * @param int $parno optional, default FALSE * @param moxed $wtz optional, default null * @uses iCalUtilityFunctions::_isOffset() * @uses iCalUtilityFunctions::_strDate2arr() * @uses iCalUtilityFunctions::_isOffset() * @uses iCalUtilityFunctions::_tz2offset() * @uses iCalUtilityFunctions::$fmt * @return array */ public static function _strdate2date($datetime, $parno = FALSE, $wtz = null) { $unparseddatetime = $datetime; $datetime = (string) trim($datetime); $tz = null; $offset = 0; $tzSts = FALSE; $len = strlen($datetime); if ('Z' == substr($datetime, -1)) { $tz = 'Z'; $datetime = trim(substr($datetime, 0, $len - 1)); $tzSts = TRUE; } if (iCalUtilityFunctions::_isOffset(substr($datetime, -5, 5))) { // [+/-]NNNN offset $tz = substr($datetime, -5, 5); $datetime = trim(substr($datetime, 0, $len - 5)); } elseif (iCalUtilityFunctions::_isOffset(substr($datetime, -7, 7))) { // [+/-]NNNNNN offset $tz = substr($datetime, -7, 7); $datetime = trim(substr($datetime, 0, $len - 7)); } elseif (empty($wtz) && ctype_digit(substr($datetime, 0, 4)) && ctype_digit(substr($datetime, -2, 2)) && iCalUtilityFunctions::_strDate2arr($datetime)) { $output = $datetime; if (!empty($tz)) { $output['tz'] = 'Z'; } $output['unparsedtext'] = $unparseddatetime; return $output; } else { $cx = $tx = 0; // find any trailing timezone or offset $len = strlen($datetime); for ($cx = -1; $cx > 9 - $len; $cx--) { $char = substr($datetime, $cx, 1); if (' ' == $char || ctype_digit($char)) { break; } else { $tx--; } // tz length counter } if (0 > $tx) { // if any $tz = substr($datetime, $tx); $datetime = trim(substr($datetime, 0, $len + $tx)); } if (ctype_digit(substr($datetime, 0, 8)) && 'T' == substr($datetime, 8, 1) && ctype_digit(substr($datetime, -6, 6)) || ctype_digit(substr($datetime, 0, 14))) { $tzSts = TRUE; } } if (empty($tz) && !empty($wtz)) { $tz = $wtz; } if (3 == $parno) { $tz = null; } if (!empty($tz)) { // tz set if ('Z' != $tz && iCalUtilityFunctions::_isOffset($tz)) { $offset = (string) iCalUtilityFunctions::_tz2offset($tz) * -1; $tz = 'UTC'; $tzSts = TRUE; } elseif (!empty($wtz)) { $tzSts = TRUE; } $tz = trim($tz); if ('Z' == $tz || 'GMT' == strtoupper($tz)) { $tz = 'UTC'; } if (0 < substr_count($datetime, '-')) { $datetime = str_replace('-', '/', $datetime); } try { $d = new DateTime($datetime, new DateTimeZone($tz)); if (0 != $offset) { // adjust for offset $d->modify($offset . ' seconds'); } $datestring = $d->format(iCalUtilityFunctions::$fmt['YmdHis3']); unset($d); } catch (Exception $e) { $datestring = date(iCalUtilityFunctions::$fmt['YmdHis3'], strtotime($datetime)); } } else { $datestring = date(iCalUtilityFunctions::$fmt['YmdHis3'], strtotime($datetime)); } if ('UTC' == $tz) { $tz = 'Z'; } $d = explode('-', $datestring); $output = array('year' => $d[0], 'month' => $d[1], 'day' => $d[2]); if (!$parno || 3 != $parno) { // parno is set to 6 or 7 $output['hour'] = $d[3]; $output['min'] = $d[4]; $output['sec'] = $d[5]; if (($tzSts || 7 == $parno) && !empty($tz)) { $output['tz'] = $tz; } } // return original string in the array in case strtotime failed to make sense of it $output['unparsedtext'] = $unparseddatetime; return $output; }
/** * convert format for input date (UTC) to internal date with parameters * * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**> * @since 2.11.8 - 2012-01-19 * @param mixed $year * @param mixed $month optional * @param int $day optional * @param int $hour optional * @param int $min optional * @param int $sec optional * @param array $params optional * @return array */ public static function _setDate2($year, $month = FALSE, $day = FALSE, $hour = FALSE, $min = FALSE, $sec = FALSE, $params = FALSE) { $input = null; iCalUtilityFunctions::_strDate2arr($year); if (iCalUtilityFunctions::_isArrayDate($year)) { $input['value'] = iCalUtilityFunctions::_date_time_array($year, 7); $input['params'] = iCalUtilityFunctions::_setParams($month, array('VALUE' => 'DATE-TIME')); } elseif (iCalUtilityFunctions::_isArrayTimestampDate($year)) { $input['value'] = iCalUtilityFunctions::_timestamp2date($year, 7); $input['params'] = iCalUtilityFunctions::_setParams($month, array('VALUE' => 'DATE-TIME')); } elseif (8 <= strlen(trim($year))) { // ex. 2006-08-03 10:12:18 $input['value'] = iCalUtilityFunctions::_date_time_string($year, 7); unset($input['value']['unparsedtext']); $input['params'] = iCalUtilityFunctions::_setParams($month, array('VALUE' => 'DATE-TIME')); } else { $input['value'] = array('year' => $year, 'month' => $month, 'day' => $day, 'hour' => $hour, 'min' => $min, 'sec' => $sec); $input['params'] = iCalUtilityFunctions::_setParams($params, array('VALUE' => 'DATE-TIME')); } $parno = iCalUtilityFunctions::_existRem($input['params'], 'VALUE', 'DATE-TIME', 7); // remove default if (!isset($input['value']['hour'])) { $input['value']['hour'] = 0; } if (!isset($input['value']['min'])) { $input['value']['min'] = 0; } if (!isset($input['value']['sec'])) { $input['value']['sec'] = 0; } if (isset($input['params']['TZID']) && !empty($input['params']['TZID'])) { if ('Z' != $input['params']['TZID'] && iCalUtilityFunctions::_isOffset($input['params']['TZID'])) { // utc offset in TZID to tz $input['value']['tz'] = $input['params']['TZID']; unset($input['params']['TZID']); } elseif (in_array(strtoupper($input['params']['TZID']), array('GMT', 'UTC', 'Z'))) { // time zone Z $input['value']['tz'] = 'Z'; unset($input['params']['TZID']); } } if (!isset($input['value']['tz']) || !iCalUtilityFunctions::_isOffset($input['value']['tz'])) { $input['value']['tz'] = 'Z'; } return $input; }
/** * 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; }