Source for file iCalcreator.class.php
Documentation is available at iCalcreator.class.php
/*********************************************************************************/
* iCalcreator class v2.4.3
* copyright (c) 2007-2008 Kjell-Inge Gustafsson kigkonsult
* www.kigkonsult.se/iCalcreator/index.php
* This file is a PHP implementation of RFC 2445.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/*********************************************************************************/
/*********************************************************************************/
/*********************************************************************************/
/* your local language code */
// define( 'ICAL_LANG', 'sv' );
$langstr = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$pos = strpos( $langstr, ';' );
$langstr = substr( $langstr, 0, $pos );
$pos = strpos( $langstr, ',' );
$pos = strpos( $langstr, ',' );
$langstr = substr( $langstr, 0, $pos );
define( 'ICAL_LANG', $langstr );
/* only for phpversion 5.x, date management, default timezone setting */
if( substr( phpversion(), 0, 1) >= '5' ) // && ( 'UTC' == date_default_timezone_get() )) {
/* version string, do NOT remove!! */
define( 'ICALCREATOR_VERSION', 'iCalcreator 2.4.3' );
/*********************************************************************************/
/*********************************************************************************/
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-12-30
// calendar property variables
// container for calendar components
// component config variables
// component internal variables
// component xCal declaration container
* constructor for calendar object
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-12-30
* language = <Text identifying a language, as defined in [RFC 1766]>
$this->setConfig( 'delimiter', DIRECTORY_SEPARATOR );
/*********************************************************************************/
* Property Name: CALSCALE
* creates formatted output for calendar property calscale
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
return ' calscale="'. $this->calscale. '"'. $this->nl;
* set calendar property calscale
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-13
/*********************************************************************************/
* creates formatted output for calendar property method
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
return ' method="'. $this->method. '"'. $this->nl;
return 'METHOD:'. $this->method. $this->nl;
* set calendar property method
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-13
/*********************************************************************************/
* The identifier is RECOMMENDED to be the identical syntax to the
* [RFC 822] addr-spec. A good method to assure uniqueness is to put the
* domain name or a domain literal IP address of the host on which.. .
* creates formatted output for calendar property prodid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
return ' prodid="'. $this->prodid. '"'. $this->nl;
return 'PRODID:'. $this->prodid. $this->nl;
* make default value for calendar prodid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-10
* Conformance: The property MUST be specified once in an iCalendar object.
* Description: The vendor of the implementation SHOULD assure that this
* is a globally unique identifier; using some technique such as an FPI
* value, as defined in [ISO 9070].
* make default unique_id for calendar prodid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-10
/*********************************************************************************/
* Description: A value of "2.0" corresponds to this memo.
* creates formatted output for calendar property version
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
return ' version="'. $this->version. '"'. $this->nl;
* set default calendar version
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-10
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-10
/*********************************************************************************/
* creates formatted output for calendar property x-prop, iCal format only
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
$toolbox->setConfig( 'language', $this->getConfig( 'language' ));
$toolbox->setConfig( 'nl', $this->getConfig( 'nl' ));
$toolbox->_createFormat( $this->getConfig( 'format' ));
foreach( $this->xprop as $label => $xpropPart ) {
$attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
foreach( $xpropPart['value'] as $pix => $theXpart )
$xpropPart['value'][$pix] = $toolbox->_strrep( $theXpart );
$xpropPart['value'] = implode( ',', $xpropPart['value'] );
$xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] );
$xprop .= $toolbox->_createElement( strtoupper( $label ), $attributes, $xpropPart['value'] );
* set calendar property x-prop
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.7 - 2007-06-21
* @param array $params optional
function setXprop( $label, $value, $params= FALSE ) {
if( empty( $label ) || empty( $value ))
$xprop = array( 'value' => $value );
$xprop['params'] = $toolbox->_setParams( $params );
$this->xprop[$label] = $xprop;
/*********************************************************************************/
* delete calendar property value
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.15 - 2007-10-29
* @param string $propName
* @param int @propix, optional, if specific property is wanted in case of multiply occurences
* @return bool, if successfull delete
$propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
$propix = ( isset ( $this->propdelix[$propName] )) ? $this->propdelix[$propName] + 2 : 1;
$this->propdelix[$propName] = -- $propix;
if( $propName != 'X-PROP' ) {
if( !isset ( $this->xprop[$propName] )) return FALSE;
unset ( $this->xprop[$propName] );
if( count( $this->xprop ) <= $propix ) return FALSE;
foreach( $this->xprop as $xpropkey => $xpropvalue ) {
if( $propix == $xpropno ) {
unset ( $this->xprop[$xpropkey] );
* get calendar property value/params
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.8 - 2007-07-07
* @param string $propName, optional
* @param int @propix, optional, if specific property is wanted in case of multiply occurences
* @param bool $inclParam=FALSE
function getProperty( $propName= FALSE, $propix= FALSE, $inclParam= FALSE ) {
$propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
$propix = ( isset ( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
$this->propix[$propName] = -- $propix;
if( 0 < $propix ) return FALSE;
if( 0 < $propix ) return FALSE;
if( 0 < $propix ) return FALSE;
if( 0 < $propix ) return FALSE;
if( $propName != 'X-PROP' ) {
if( !isset ( $this->xprop[$propName] )) return FALSE;
return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
: array( $propName, $this->xprop[$propName]['value'] );
if( count( $this->xprop ) <= $propix ) return FALSE;
foreach( $this->xprop as $xpropkey => $xpropvalue ) {
if( $propix == $xpropno )
return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
: array( $xpropkey, $this->xprop[$xpropkey]['value'] );
return FALSE; // not found ??
* general vcalendar property setting
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-10-23
* @param mixed $args variable number of function arguments,
* first argument is ALWAYS component name,
* second ALWAYS component value!
if( !$this->getConfig( 'allowEmpty' ) && ( !isset ( $arglist[1] ) || empty( $arglist[1] )))
for( $argix= $numargs; $argix < 3; $argix++ ) {
if( !isset ( $arglist[$argix] ))
$this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
/*********************************************************************************/
* get vcalendar config values or * calendar components
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-12-30
foreach( $this->components as $cix => $component ) {
unset ( $component->propix );
$info[$cix]['ordno'] = $cix + 1;
$info[$cix]['type'] = $component->objName;
$info[$cix]['uid'] = $component->getProperty( 'uid' );
$info[$cix]['props'] = $component->getConfig( 'propinfo' );
$info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
unset ( $component->propix );
return array( $this->getConfig( 'directory' )
$this->filename = date( 'YmdHis' ). '.xml'; // recommended xcs.. .
if( empty( $this->url )) {
if( FALSE === ( $size = filesize( $dirfile )))
/* get language for calendar component as defined in [RFC 1766] */
if( !empty( $this->url ))
* general vcalendar config setting
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-12-30
if( !empty( $this->url )) {
/* remote directory+file - URL */
/* local existing file */
elseif( FALSE !== touch( $dirfile )) {
/* new local file created */
// set language for calendar component as defined in [RFC 1766]
$value = str_replace( 'WEBCAL://', 'http://', $value );
$value = str_replace( 'webcal://', 'http://', $value );
return $this->setConfig( 'filename', $parts['basename'] );
/*********************************************************************************/
* convert input parameters to (valid) iCalcreator date in array format (or FALSE)
* if $utc=TRUE and $tz = utc offset ([[+/]-]HHmm) input (local) date array + UTC offset
* returns ouput in UTC format date
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param mixed $tz optional
* @param bool $utc optional
* @return bool false / array $date
function validDate( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $utc= FALSE ) {
if( is_array( $year ) && isset ( $year['timestamp'] )) {
$input = $toolbox->_date_time_string( date( 'Y-m-d H:i:s', $year['timestamp'] ), 6 );
$input['tz'] = ( isset ( $year['tz'] )) ? $year['tz'] : null;
$utc = ( TRUE === $month ) ? TRUE : FALSE;
if( isset ( $year['tz'] ) || ( 4 == count( $year )) || ( 7 == count( $year )))
elseif( isset ( $year['hour'] ) || isset ( $year['min'] ) || isset ( $year['sec'] ) ||
$input = $toolbox->_date_time_array( $year, $parno );
$utc = ( TRUE === $month ) ? TRUE : FALSE;
elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
$input = $toolbox->_date_time_string( $year );
$utc = ( TRUE === $month ) ? TRUE : FALSE;
elseif(( $year !== FALSE ) && ( $month !== FALSE ) && ( $day !== FALSE )) {
if(( 0 > (int) $year ) || (2100 < (int) $year ))
if(( 1 > $month ) || ( 12 < $month ))
if(( 1 > $day ) || ( 31 < $day ))
$input['month'] = $month;
if(( $hour !== FALSE ) || ( $min !== FALSE ) || ( $sec !== FALSE )) {
$input = $toolbox->_date_time_array( $input, $parno );
if( !checkdate ( $input['month'], $input['day'], $input['year'] ))
if( isset ( $input['hour'] ) &&
(( 0 > $input['hour'] ) || ( 23 < $input['hour'] )))
if( isset ( $input['min'] ) &&
(( 0 > $input['min'] ) || ( 59 < $input['min'] )))
if( isset ( $input['sec'] ) &&
(( 0 > $input['sec'] ) || ( 59 < $input['sec'] )))
if( isset ( $input['tz'] ) && ( '' < trim ( $input['tz'] ))) {
$input['tz'] = (string) trim( $input['tz'] );
if( ctype_digit( $input['tz']{1} )) { // only numeric tz=offset
$input['tz'] = '+'. $input['tz'];
$offset = $toolbox->_tz2offset( $input['tz'] );
if( !isset ( $input['hour'] ))
if( !isset ( $input['min'] ))
if( !isset ( $input['sec'] ))
$input = date('Y-m-d H:i:s\Z', mktime( $input['hour']
, $input['sec'] + $offset
$parno = ( $utc ) ? 7 : 6 ;
$input = $toolbox->_date_time_string( $input, $parno );
if( !$utc && isset ( $input['tz'] ) && ( 'Z' == $input['tz'] ))
/*********************************************************************************/
* add calendar component to container
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-04-24
* @param object $component calendar component
* delete calendar component from container
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.4 - 2007-06-14
* @param mixed $arg1 ordno / component type / component uid
* @param mixed $arg2 optional, ordno if arg1 = component type
$argType = $index = null;
$index = (int) $arg1 - 1;
elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
$index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 1;
foreach ( $this->components as $cix => $component) {
unset ( $component->propix );
if(( 'INDEX' == $argType ) && ( $index == $cix )) {
elseif( $argType == $component->objName ) {
if( $index == $cix1dC ) {
elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
* get calendar component from container
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.16 - 2007-11-11
* @param mixed $arg1 optional, ordno/component type/ component uid
* @param mixed $arg2 optional, ordno if arg1 = component type
$index = $argType = null;
$index = $this->compix['INDEX'] =
( isset ( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
unset ( $this->compix['INDEX'] );
$index = $this->compix[$argType] =
( isset ( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
if( !empty( $index) && ( $index > end( $ckeys )))
foreach ( $this->components as $cix => $component) {
unset ( $component->propix );
if(( 'INDEX' == $argType ) && ( $index == $cix ))
return $component->copy();
elseif( $argType == $component->objName ) {
return $component->copy();
elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
unset ( $component->propix );
return $component->copy();
* select components from calendar on date basis
* Ensure DTSTART is set for every component.
* No date controls occurs.
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.4.1 - 2008-02-05
* @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</tr>
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 */
/* 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' );
foreach( $cType as $cix => $theType ) {
elseif( !empty( $cType )) {
$cType = array( 'vevent' );
$cType = array( $cType );
if( 0 >= count( $cType ))
foreach ( $this->components as $cix => $component ) {
unset ( $component->propix, $start );
/* deselect unvalid type components */
if( !in_array( $component->objName, $cType ))
/* deselect components without dtstart set */
if( FALSE === ( $start = $component->getProperty( 'dtstart' )))
$dtendExist = $dueExist = FALSE;
unset ( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend ); // clean up
$startWdate = $component->_date2timestamp( $start );
/* get end date from dtend/due/duration properties */
$end = $component->getProperty( 'dtend' );
//if( !empty($end)) echo 'selectComponents 1 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
if( empty($end) && ( $component->objName == 'vtodo' )) {
$end = $component->getProperty( 'due' );
//if( !empty($end)) echo 'selectComponents 2 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
elseif( !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!!! */
$end = array( 'year' => $end['year'], 'month' => $end['month'], 'day' => ($end['day'] - 1), 'hour' => 23, 'min' => 59, 'sec' => 59 );
//if( !empty($end)) echo 'selectComponents 3 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
$end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
//if( !empty($end)) echo 'selectComponents 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 'selectComponents 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
$endWdate = $component->_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 = $component->_date2timestamp( $end );
$rdurWsecs = $endWdate - $startWdate; // compute component duration in seconds
$rdur = $component->_date2duration( $start, $end ); // compute component duration, array
/* make a list of optional exclude dates for component occurence, exrule, exdate */
$workstart = $component->_date_time_string( date('Y-m-d H:i:s', ( $startDate - $rdurWsecs )), 6);
$workend = $component->_date_time_string( date('Y-m-d H:i:s', ( $endDate + $rdurWsecs )), 6);
while( FALSE !== ( $exrule = $component->getProperty( 'exrule' )))
$component->_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) {
foreach( $exdate as $theExdate ) {
$exWdate = $component->_date2timestamp( $theExdate );
if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
$exdatelist[$exWdate] = TRUE;
/* if 'any' components, check repeating components, removing all excluding dates */
/* make a list of optional repeating dates for component occurence, rrule, rdate */
while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule
$component->_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 )) && // PERIOD
$rstart = $component->_date2timestamp( $theRdate[0] );
if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
if( isset ( $theRdate[1]['year'] )) // date-date period
$rend = $component->_date2timestamp( $theRdate[1] );
else { // date-duration period
$rend = $component->duration2date( $theRdate[0], $theRdate[1] );
$rend = $component->_date2timestamp( $rend );
if((( $startDate - $rdurWsecs ) <= $rstart ) && ( $endDate >= $rstart ))
$recurlist[$rstart] = ( $rstart - $rend ); // set start date + rdate duration in seconds
$theRdate = $component->_date2timestamp( $theRdate );
if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
$recurlist[$theRdate] = $rdurWsecs; // set start date + event duration in seconds
if( 0 < count( $recurlist )) {
foreach( $recurlist as $recurkey => $durvalue ) {
if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
if( isset ( $exdatelist[$recurkey] )) // check excluded dates
if( $startWdate >= $recurkey ) // exclude component start date
$component2 = $component->copy();
$rstart = $component2->_date_time_string( date('Y-m-d H:i:s', $recurkey ), 6);
$datevalue = $rstart['month']. '/'. $rstart['day']. '/'. $rstart['year'];
if( isset ( $start['hour'] ) || isset ( $start['min'] ) || isset ( $start['sec'] )) {
$datevalue .= ( isset ( $rstart['hour'] )) ? ' '. $rstart['hour'] : ' 00';
$datevalue .= ( isset ( $rstart['min'] )) ? ':'. $rstart['min'] : ':00';
$datevalue .= ( isset ( $rstart['sec'] )) ? ':'. $rstart['sec'] : ':00';
if( isset ( $start['tz'] ))
$datestring .= ' '. $start['tz'];
$component2->setProperty( 'x-current-dtstart', $datestring );
$rend = $component2->_date_time_string( date('Y-m-d H:i:s', ( $recurkey + $durvalue )), 6);
if( isset ( $datevalue ) && ( $dtendExist || $dueExist )) {
$datevalue = $rend['month']. '/'. $rend['day']. '/'. $rend['year'];
if( isset ( $end['hour'] ) || isset ( $end['min'] ) || isset ( $end['sec'] )) {
$datevalue .= ( isset ( $rend['hour'] )) ? ' '. $rend['hour'] : ' 00';
$datevalue .= ( isset ( $rend['min'] )) ? ':'. $rend['min'] : ':00';
$datevalue .= ( isset ( $rend['sec'] )) ? ':'. $rend['sec'] : ':00';
$datestring .= ' '. $end['tz'];
$component2->setProperty( 'x-current-dtend', $datestring );
$component2->setProperty( 'x-current-due', $datestring );
$rend = $component2->_date2timestamp( $rend );
/* add repeating components within valid dates to output array, only start date */
$result[] = $component2; // copy to output
while( $rstart <= $rend ) { // iterate
// if(( $rstart >= $startDate ) && // date within period
if(( $rstart > $startDate ) && // date after dtstart
!isset ( $exdatelist[$rstart] )) // check exclude date
$result[$wd['year']][$wd['mon']][$wd['mday']][] = $component2->copy(); // copy to output
$rstart += ( 24* 60* 60 ); // step one day
elseif(( $rstart >= $startDate ) && // date within period
!isset ( $exdatelist[$rstart] )) { // check exclude date
$result[$wd['year']][$wd['mon']][$wd['mday']][] = $component2->copy(); // copy to output
/* deselect components with startdate/enddate not within period */
if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
/* deselect components with startdate not within period */
elseif(( $startWdate < $startDate ) || ( $startWdate > $endDate ))
/* add selected components within valid dates to output array */
$result[] = $component->copy(); // copy to output;
if( $endWdate > $endDate )
$endWdate = $endDate; // use period end date
if( !isset ( $exdatelist[$startWdate] )) { // check excluded dates
if( $startWdate < $startDate )
$startWdate = $startDate; // use period start date
while( $startWdate <= $endWdate ) { // iterate
$result[$wd['year']][$wd['mon']][$wd['mday']][] = $component->copy(); // copy to output
$startWdate += ( 24* 60* 60 ); // step one day
elseif( !isset ( $exdatelist[$startWdate] ) && // check excluded dates
( $startWdate >= $startDate )) { // within period
$result[$wd['year']][$wd['mon']][$wd['mday']][] = $component->copy(); // copy to output
if( 0 >= count( $result ))
foreach( $result as $y => $yeararr ) {
foreach( $yeararr as $m => $montharr ) {
ksort( $result[$y][$m] );
* add calendar component to container
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.16 - 2007-11-11
* @param object $component calendar component
* @param mixed $arg1 optional, ordno/component type/ component uid
* @param mixed $arg2 optional, ordno if arg1 = component type
function setComponent( $component, $arg1= FALSE, $arg2= FALSE ) {
if( '' >= $component->getConfig( 'language'))
$component->setConfig( 'language', $this->getConfig( 'language' ));
$component->setConfig( 'allowEmpty', $this->getConfig( 'allowEmpty' ));
$component->setConfig( 'nl', $this->getConfig( 'nl' ));
$component->setConfig( 'unique_id', $this->getConfig( 'unique_id' ));
$component->setConfig( 'format', $this->getConfig( 'format' ));
if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
unset ( $component->propix );
/* make sure dtstamp and uid is set */
$dummy = $component->getProperty( 'dtstamp' );
$dummy = $component->getProperty( 'uid' );
$argType = $index = null;
$index = (int) $arg1 - 1;
elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
$index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
foreach ( $this->components as $cix => $component2) {
unset ( $component2->propix );
if(( 'INDEX' == $argType ) && ( $index == $cix )) {
elseif( $argType == $component2->objName ) {
if( $index == $cix1sC ) {
elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) {
/* not found.. . insert last in chain anyway .. .*/
* sort iCal compoments, only local date sort
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.9 - 2007-10-02
if( 'vtimezone' == $a->objName) return - 1;
if( 'vtimezone' == $b->objName) return 1;
if( isset ( $a->dtstart['value'] ) ||
isset ( $b->dtstart['value'] )) {
foreach( $this->_sortkeys as $key ) {
if( !isset ( $a->dtstart['value'][$key] )) return - 1;
elseif( !isset ( $b->dtstart['value'][$key] )) return 1;
if ( $a->dtstart['value'][$key]
== $b->dtstart['value'][$key] )
if (( (int) $a->dtstart['value'][$key] ) <
( (int) $b->dtstart['value'][$key] )) return - 1;
elseif(( (int) $a->dtstart['value'][$key] ) >
( (int) $b->dtstart['value'][$key] )) return 1;
if( isset ( $a->dtend['value'] ))
if( empty( $c ) && !empty( $a->due['value'] ))
if( empty( $c ) && !empty( $a->duration['value'] ))
$c = $a->duration2date();
if( isset ( $b->dtend['value'] ))
if( empty( $d ) && !empty( $b->due['value'] ))
if( empty( $d ) && !empty( $b->duration['value'] ))
$d = $b->duration2date();
if( isset ( $c ) || isset ( $d )) {
if( !isset ( $c ) && isset ( $d )) return - 1;
if( isset ( $c ) && !isset ( $d )) return 1;
foreach( $this->_sortkeys as $key ) {
if ( !isset ( $c[$key] )) return - 1;
elseif( !isset ( $d[$key] )) return 1;
( (int) $d[$key] )) return - 1;
elseif(( (int) $c[$key] ) >
( (int) $d[$key] )) return 1;
$c = ( isset ( $a->created['value'] )) ? $a->created['value'] : $a->dtstamp['value'];
$d = ( isset ( $b->created['value'] )) ? $b->created['value'] : $b->dtstamp['value'];
foreach( $this->_sortkeys as $key ) {
if( !isset ( $a->created['value'][$key] )) return - 1;
elseif( !isset ( $b->created['value'][$key] )) return 1;
if ( $a->created['value'][$key]
== $b->created['value'][$key] )
if (( (int) $a->created['value'][$key] ) <
( (int) $b->created['value'][$key] )) return - 1;
elseif(( (int) $a->created['value'][$key] ) >
( (int) $b->created['value'][$key] )) return 1;
if (( $a->uid['value'] ) <
( $b->uid['value'] )) return - 1;
elseif(( $a->uid['value'] ) >
( $b->uid['value'] )) return 1;
$this->_sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
* parse iCal file into vcalendar, components, properties and parameters
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.4.3 - 2008-02-13
* @param string $filename optional filname (incl. opt. directory/path) or URL
* @return bool FALSE if error occurs during parsing
function parse( $filename= FALSE ) {
/* directory/filename previous set via setConfig directory+filename / url */
if( FALSE === ( $filename = $this->getConfig( 'url' )))
return FALSE; /* err 2 */
/* local directory/filename */
if( !empty( $parts['dirname'] ) && ( '.' != $parts['dirname'] )) {
if( !$this->setConfig( 'directory', $parts['dirname'] ))
return FALSE; /* err 3 */
if( !$this->setConfig( 'filename', $parts['basename'] ))
return FALSE; /* err 4 */
if( 'http://' != substr( $filename, 0, 7 )) {
/* local file error tests */
if( !is_file( $filename )) /* err 5 */
return FALSE; /* err 6 */
return FALSE; /* err 7 */
if( FALSE === ( $rows = file( $filename )))
return FALSE; /* err 1 */
/* identify BEGIN:VCALENDAR, MUST be first row */
return FALSE; /* err 8 */
/* remove empty trailing lines */
while( '' == trim( $rows[count( $rows ) - 1] )) {
unset ( $rows[count( $rows ) - 1] );
/* identify ending END:VCALENDAR row */
return FALSE; /* err 9 */
return FALSE; /* err 10 */
/* identify components and update unparsed data within component */
foreach( $rows as $line ) {
return FALSE; /* err 20 */
$comp->setComponent( $subcomp );
} // end - if ( 'END:' ==.. .
if( 'VALARM' == $compname )
elseif( 'STANDARD' == $compname )
elseif( 'DAYLIGHT' == $compname )
return FALSE; /* err 6 */
} // end - elsif ( 'BEGIN:'.. .
/* update selected component with unparsed data */
$actcomp->unparsed[] = $line;
} // end - foreach( rows.. .
/* parse data for calendar (this) object */
if( is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
/* concatenate property values spread over several lines */
$propnames = array( 'calscale','method','prodid','version','x-' );
foreach( $this->unparsed as $line ) {
foreach ( $propnames as $propname ) {
$proprows[$lastix] = $line;
if(( '\n' == substr( $proprows[$lastix], - 2 )) &&
( ' ' == substr( $line, 0, 1 ))) {
$proprows[$lastix] = substr( $proprows[$lastix], 0, strlen( $proprows[$lastix] ) - 2 );
$proprows[$lastix] .= $line;
foreach( $proprows as $line ) {
if( '\n' == substr( $line, - 2 ))
for( $cix= 0; $cix < strlen( $line ); $cix++ ) {
if( in_array( $line{$cix}, array( ':', ';' )))
$propname .= $line{$cix};
/* ignore version/prodid properties */
/* separate attributes from value */
for( $cix= 0; $cix < $strlen; $cix++ ) {
if(( ':' == $line{$cix} ) &&
( '://' != substr( $line, $cix, 3 )) &&
if(( $cix < ( $strlen - 4 )) &&
for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
if( '://' == substr( $line, $c2ix - 2, 3 )) {
break; // an URI with a portnr!!
$line = substr( $line, $cix + 1 );
$attr[$attrix] .= $line{$cix};
/* make attributes in array format */
foreach( $attr as $attribute ) {
$attrsplit = explode( '=', $attribute, 2 );
if( 1 < count( $attrsplit ))
$propattr[$attrsplit[0]] = $attrsplit[1];
$propattr[] = $attribute;
if( FALSE !== strpos( $line, ',' )) {
$clen = count( $content );
for( $cix = 0; $cix < $clen; $cix++ ) {
if( "\\" == substr( $content[$cix], - 1 )) {
$content[$cix] .= ','. $content[$cix + 1];
unset ( $content[$cix + 1] );
if( 1 < count( $content )) {
foreach( $content as $cix => $contentPart )
$content[$cix] = $toolbox->_strunrep( $contentPart );
$line = reset( $content );
$line = $toolbox->_strunrep( $line );
} // end - foreach( $this->unparsed.. .
} // end - if( is_array( $this->unparsed.. .
return FALSE; /* err 91 or something.. . */
/*********************************************************************************/
* creates formatted output for calendar object instance
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-10-25
$calendarInit1 = $calendarInit2 = $calendarxCaldecl = $calendarStart = $calendar = null;
$calendarInit1 = '<?xml version="1.0" encoding="UTF-8"?>'. $this->nl.
'<!DOCTYPE iCalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'. $this->nl.
'"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
$calendarInit2 = '>'. $this->nl;
$calendarStart = '<vcalendar';
$calendarStart = 'BEGIN:VCALENDAR'. $this->nl;
if( $this->nl == substr( $calendarStart, ( 0 - $nlstrlen )))
$calendarStart = substr( $calendarStart, 0, ( strlen( $calendarStart ) - $nlstrlen ));
$calendarStart .= '>'. $this->nl;
if( '' >= $component->getConfig( 'language'))
$component->setConfig( 'language', $this->getConfig( 'language' ));
$component->setConfig( 'allowEmpty', $this->getConfig( 'allowEmpty' ));
$component->setConfig( 'nl', $this->getConfig( 'nl' ));
$component->setConfig( 'unique_id', $this->getConfig( 'unique_id' ));
$component->setConfig( 'format', $this->getConfig( 'format' ));
$calendar .= $component->createComponent( $this->xcaldecl );
$calendarInit1 .= $this->nl. '['. $this->nl;
foreach( $this->xcaldecl as $declix => $declPart ) {
if(( 0 < count( $old_xcaldecl)) &&
( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) &&
( in_array( $declPart['external'], $old_xcaldecl['external'] )))
continue; // no duplicate uri and ext. references
$calendarxCaldecl .= '<!';
foreach( $declPart as $declKey => $declValue ) {
switch( $declKey ) { // index
$calendarxCaldecl .= $declValue. ' ';
$calendarxCaldecl .= $declValue. ' ';
$old_xcaldecl['uri'][] = $declValue;
$calendarxCaldecl .= $declValue. ' ';
$calendarxCaldecl .= '"'. $declValue. '" ';
$old_xcaldecl['external'][] = $declValue;
$calendarxCaldecl .= $declValue. ' ';
$calendarxCaldecl .= $declValue;
$calendarxCaldecl .= '>'. $this->nl;
$calendarInit2 = ']'. $calendarInit2;
$calendar .= '</vcalendar>'. $this->nl;
$calendar .= 'END:VCALENDAR'. $this->nl;
return $calendarInit1. $calendarxCaldecl. $calendarInit2. $calendarStart. $calendar;
* a HTTP redirect header is sent with created, updated and/or parsed calendar
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.12 - 2007-10-23
$filesize = strlen( $output );
// if( headers_sent( $filename, $linenum ))
// die( "Headers already sent in $filename on line $linenum\n" );
header( 'Content-Type: application/calendar+xml; charset=utf-8' );
header( 'Content-Type: text/calendar; charset=utf-8' );
header( 'Content-Length: '. $filesize );
header( 'Content-Disposition: attachment; filename="'. $filename. '"' );
header( 'Cache-Control: max-age=10' );
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.12 - 2007-12-30
* @param string $directory optional
* @param string $filename optional
* @param string $delimiter optional
function saveCalendar( $directory= FALSE, $filename= FALSE, $delimiter= FALSE ) {
if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
$iCalFile = @fopen( $dirfile, 'w' );
* if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.12 - 2007-10-28
* @param string $directory optional alt. int timeout
* @param string $filename optional
* @param string $delimiter optional
* @param int timeout optional, default 3600 sec
function useCachedCalendar( $directory= FALSE, $filename= FALSE, $delimiter= FALSE, $timeout= 3600) {
if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
$timeout = (int) $directory;
if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
// if( headers_sent( $filename, $linenum ))
// die( "Headers already sent in $filename on line $linenum\n" );
header( 'Content-Type: application/calendar+xml; charset=utf-8' );
header( 'Content-Type: text/calendar; charset=utf-8' );
header( 'Content-Length: '. $filesize );
header( 'Content-Disposition: attachment; filename="'. $filename. '"' );
header( 'Cache-Control: max-age=10' );
$fp = @$fopen( $dirfile, 'r' );
/*********************************************************************************/
/*********************************************************************************/
* abstract class for calendar components
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.1 - 2007-11-19
// component property variables
// component subcomponents container
// component config variables
var $objName; // created automatically at instance creation
// component internal variables
// component xCal declaration container
* constructor for calendar component object
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.1 - 2007-11-19
$this->objName = ( isset ( $this->timezonetype )) ?
/*********************************************************************************/
* creates formatted output for calendar component property action
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
if( !isset ( $this->action['value'] ))
* set calendar component property action
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.2 - 2006-11-16
* @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
function setAction( $value, $params= FALSE ) {
$this->action['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property attach
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
foreach( $this->attach as $attachPart ) {
$output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
* set calendar component property attach
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.22 - 2007-04-10
$attach['value'] = $value ;
/*********************************************************************************/
* Property Name: ATTENDEE
* creates formatted output for calendar component property attendee
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-20
foreach( $this->attendee as $attendeePart ) { // start foreach 1
$attendee1 = $attendee2 = $attendeeLANG = $attendeeCN = null;
foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2
if( 'value' == $paramlabel ) {
$attendee2 .= 'MAILTO:'. $paramvalue;
elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
$attendee11 = $attendee12 = null;
if( is_int( $optparamlabel )) {
switch( $optparamlabel ) { // start switch
$attendee11 = ( !$attendee11 ) ? $this->intAttrDelimiter. 'DELEGATED-TO=' : $attendee11;
$attendee11 = ( !$attendee11 ) ? $this->intAttrDelimiter. 'DELEGATED-FROM=' : $attendee11;
foreach( $optparamvalue as $cix => $calUserAddress ) {
$attendee12 .= ( $cix ) ? ',' : null;
$attendee12 .= '"MAILTO:'. $calUserAddress. '"';
$attendee1 .= $attendee11. $attendee12;
$attendees .= $this->_createElement( 'ATTENDEE', $attendee1. $attendeeLANG. $attendeeCN, $attendee2 );
* set calendar component property attach
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-24
* @param array $params optional
$attendee = array( 'value' => $value, 'params' => array() );
foreach( $params as $optparamlabel => $optparamvalue ) {
switch( $optparamlabel ) {
foreach( $optparamvalue as $part ) {
if(( '"' == $part{0} ) && ( '"' == $part{strlen($part)- 1} ))
$optarrays[$optparamlabel][] = $part;
if(( '"' == $part{0} ) && ( '"' == $part{strlen($part)- 1} ))
$optarrays[$optparamlabel][] = $part;
if( 'SENT-BY' == $optparamlabel ) {
$optparamvalue = str_replace( 'MAILTO:', '', $optparamvalue );
$optparamvalue = str_replace( 'mailto:', '', $optparamvalue );
if(( '"' == substr( $optparamvalue, 0, 1 )) &&
( '"' == substr( $optparamvalue, - 1 )))
$optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
$attendee['params'][$optparamlabel] = $optparamvalue;
} // end switch( $optparamlabel.. .
} // end foreach( $optparam.. .
foreach( $optarrays as $optparamlabel => $optparams )
$attendee['params'][$optparamlabel] = $optparams;
if( isset ( $attendee['params']['CUTYPE' ] ) &&
( strtoupper( $attendee['params']['CUTYPE' ] ) == 'INDIVIDUAL' ))
unset ( $attendee['params']['CUTYPE' ] );
if( isset ( $attendee['params']['PARTSTAT'] ) &&
( strtoupper( $attendee['params']['PARTSTAT'] ) == 'NEEDS-ACTION' ))
unset ( $attendee['params']['PARTSTAT'] );
if( isset ( $attendee['params']['ROLE'] ) &&
( strtoupper( $attendee['params']['ROLE'] ) == 'REQ-PARTICIPANT' ))
unset ( $attendee['params']['ROLE'] );
if( isset ( $attendee['params']['RSVP'] ) &&
( strtoupper( $attendee['params']['RSVP'] ) == 'FALSE' ))
unset ( $attendee['params']['RSVP'] );
// check language setting
if( isset ( $attendee['params']['CN' ] )) {
if( !isset ( $attendee['params']['LANGUAGE' ] ) && !empty( $lang ))
$attendee['params']['LANGUAGE' ] = $lang;
/*********************************************************************************/
* Property Name: CATEGORIES
* creates formatted output for calendar component property categories
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-24
$attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
foreach( $category['value'] as $cix => $categoryPart )
$category['value'][$cix] = $this->_strrep( $categoryPart );
$content = implode( ',', $category['value'] );
$content = $this->_strrep( $category['value'] );
$output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
* set calendar component property categories
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-24
* @param array $params optional
$category['value'] = $value;
$category['params'] = $this->_setParams( $params );
/*********************************************************************************/
* creates formatted output for calendar component property class
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property class
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2003-03-18
* @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
* @param array $params optional
function setClass( $value, $params= FALSE ) {
$this->class['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property comment
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
foreach( $this->comment as $commentPart ) {
$attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
$content = $this->_strrep( $commentPart['value'] );
$comment .= $this->_createElement( 'COMMENT', $attributes, $content );
* set calendar component property comment
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
$comment['value'] = $value;
$comment['params'] = $this->_setParams( $params );
/*********************************************************************************/
* Property Name: COMPLETED
* creates formatted output for calendar component property completed
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
if( !isset ( $this->completed['value']['year'] ) &&
!isset ( $this->completed['value']['month'] ) &&
!isset ( $this->completed['value']['day'] ) &&
!isset ( $this->completed['value']['hour'] ) &&
!isset ( $this->completed['value']['min'] ) &&
* set calendar component property completed
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-02
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setCompleted( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
/*********************************************************************************/
* creates formatted output for calendar component property contact
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-24
foreach( $this->contact as $contact ) {
$attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
$content = $this->_strrep( $contact['value'] );
* set calendar component property contact
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
* @param array $params optional
$contact['value'] = $value;
$contact['params'] = $this->_setParams( $params );
/*********************************************************************************/
* creates formatted output for calendar component property created
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
if( !isset ( $this->created['value']['year'] ) &&
!isset ( $this->created['value']['month'] ) &&
!isset ( $this->created['value']['day'] ) &&
!isset ( $this->created['value']['hour'] ) &&
!isset ( $this->created['value']['min'] ) &&
!isset ( $this->created['value']['sec'] ))
* set calendar component property created
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-02
* @param mixed $year optional
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param mixed $params optional
function setCreated( $year= FALSE, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
$this->created = $this->_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
/*********************************************************************************/
* Property Name: DESCRIPTION
* creates formatted output for calendar component property description
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
$attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
$content = $this->_strrep( $description['value'] );
$descriptions .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
* set calendar component property description
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
$description['value'] = $value;
$description['params'] = $this->_setParams( $params );
/*********************************************************************************/
* creates formatted output for calendar component property dtend
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.19 - 2007-03-27
if( !isset ( $this->dtend['value']['year'] ) &&
!isset ( $this->dtend['value']['month'] ) &&
!isset ( $this->dtend['value']['day'] ) &&
!isset ( $this->dtend['value']['hour'] ) &&
!isset ( $this->dtend['value']['min'] ) &&
!isset ( $this->dtend['value']['sec'] ))
* set calendar component property dtend
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param string $tz optional
* @param array params optional
function setDtend( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $params= FALSE ) {
$this->dtend = $this->_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
/*********************************************************************************/
* creates formatted output for calendar component property dtstamp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.4 - 2007-08-01
if( !isset ( $htis->dtstamp['value']['year'] ) &&
!isset ( $this->dtstamp['value']['month'] ) &&
!isset ( $this->dtstamp['value']['day'] ) &&
!isset ( $this->dtstamp['value']['hour'] ) &&
!isset ( $this->dtstamp['value']['min'] ) &&
!isset ( $this->dtstamp['value']['sec'] ))
* computes datestamp for calendar component object instance dtstamp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-13
* set calendar component property dtstamp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-02
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setDtstamp( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
$this->dtstamp = $this->_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
/*********************************************************************************/
* creates formatted output for calendar component property dtstart
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* @param bool $localtime optional, default FALSE
if( !isset ( $this->dtstart['value']['year'] ) &&
!isset ( $this->dtstart['value']['month'] ) &&
!isset ( $this->dtstart['value']['day'] ) &&
!isset ( $this->dtstart['value']['hour'] ) &&
!isset ( $this->dtstart['value']['min'] ) &&
!isset ( $this->dtstart['value']['sec'] ))
unset ( $this->dtstart['value']['tz'] );
* set calendar component property dtstart
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param string $tz optional
* @param array $params optional
function setDtstart( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $params= FALSE ) {
$this->dtstart = $this->_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
/*********************************************************************************/
* creates formatted output for calendar component property due
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.19 - 2007-03-27
if( !isset ( $this->due['value']['year'] ) &&
!isset ( $this->due['value']['month'] ) &&
!isset ( $this->due['value']['day'] ) &&
!isset ( $this->due['value']['hour'] ) &&
!isset ( $this->due['value']['min'] ) &&
!isset ( $this->due['value']['sec'] ))
* set calendar component property due
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setDue( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $params= FALSE ) {
$this->due = $this->_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params );
/*********************************************************************************/
* Property Name: DURATION
* creates formatted output for calendar component property duration
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
if( !isset ( $this->duration['value']['week'] ) &&
!isset ( $this->duration['value']['day'] ) &&
!isset ( $this->duration['value']['hour'] ) &&
!isset ( $this->duration['value']['min'] ) &&
!isset ( $this->duration['value']['sec'] ))
* set calendar component property duration
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param mixed $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setDuration( $week= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
/*********************************************************************************/
* creates formatted output for calendar component property exdate
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
foreach( $this->exdate as $theExdate ) {
$content = $attributes = null;
foreach( $theExdate['value'] as $eix => $exdatePart ) {
if( isset ( $theExdate['params']['TZID'] ))
if( isset ( $theExdate['value'][0]['tz'] ) &&
( 'Z' == $theExdate['value'][0]['tz'] ))) {
if( 'Z' != substr( $formatted, - 1 ))
* set calendar component property exdate
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.5 - 2007-06-22
* @param array $params optional
function setExdate( $exdates, $params= FALSE ) {
$exdate['params'] = $this->_setParams( $params, array( 'VALUE' => 'DATE-TIME' ) );
$parno = ( isset ( $exdate['params']['TZID'] )) ? 6 : null;
$parno = $this->_existRem( $exdate['params'], 'VALUE', 'DATE-TIME', 7, $parno );
if( !isset ( $parno )) $parno = $this->_existRem( $exdate['params'], 'VALUE', 'DATE', 3 );
foreach( $exdates as $eix => $theExdate ) {
if( isset ( $exdate['params']['TZID'] ))
$theExdate['tz'] = $exdate['params']['TZID'];
if( 4 < count( $theExdate ))
elseif( is_array( $theExdate ) && isset ( $theExdate['timestamp'] )) {
if( isset ( $exdate['params']['TZID'] ))
$theExdate['tz'] = $exdate['params']['TZID'];
$tz = ( isset ( $theExdate['tz'] )) ? ' '. $theExdate['tz'] : null;
$parno = ( !empty( $tz )) ? 7 : 6;
elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
if( isset ( $exdate['params']['TZID'] ))
$exdatea['tz'] = $exdate['params']['TZID'];
$parno = count( $exdatea );
if( isset ( $exdatea['tz'] ))
$exdatea['tz'] = (string) $exdatea['tz'];
$exdate['value'][] = $exdatea;
if( 0 < count( $exdate['value'] )) {
$exdate['params']['VALUE'] = 'DATE';
foreach( $exdate['value'] as $eix => $exdatea )
unset ( $exdate['value'][$eix]['tz'] );
unset ( $exdate['params']['TZID'] );
if( !empty( $exdate['value'][0]['tz'] ) &&
( $exdate['value'][0]['tz'] != 'Z' ) &&
( !( in_array($exdate['value'][0]['tz']{0}, array( '+', '-' )) &&
$exdate['params']['TZID'] = $exdate['value'][0]['tz'];
foreach( $exdate['value'] as $exix => $exdatea ) {
if( !empty( $exdate['value'][0]['tz'] ) &&
( $exdate['value'][0]['tz'] != 'Z' ) &&
( !( in_array($exdate['value'][0]['tz']{0}, array( '+', '-' )) &&
unset ( $exdate['value'][$exix]['tz'] );
elseif( isset ( $exdate['params']['TZID'] ))
unset ( $exdate['params']['TZID'] );
/*********************************************************************************/
* creates formatted output for calendar component property exrule
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.7.28 - 2006-09-13
* set calendar component property exdate
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $exruleset
* @param array $params optional
function setExrule( $exruleset, $params= FALSE ) {
foreach( $exruleset as $exrulelabel => $exrulevalue ) {
if( 'UNTIL' != $exrulelabel )
$exrule['value'][$exrulelabel] = $exrulevalue;
(( 3 == count( $exrulevalue )) ||
( 6 == count( $exrulevalue )) ||
( 7 == count( $exrulevalue )) ||
$parno = ( 3 < count( $exrulevalue )) ? 7 : 3 ;
if(( 3 < count( $date )) && !isset ( $date['tz'] ))
$exrule['value'][$exrulelabel] = $date;
elseif( is_array( $exrulevalue ) && isset ( $exrulevalue['timestamp'] )) {
$exrule['value'][$exrulelabel] = $date;
elseif( 8 <= strlen( trim( $exrulevalue ))) { // ex. 2006-08-03 10:12:18
if(( 3 < count( $date )) && !isset ( $date['tz'] ))
$exrule['value'][$exrulelabel] = $date;
/*********************************************************************************/
* Property Name: FREEBUSY
* creates formatted output for calendar component property freebusy
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-16
foreach( $this->freebusy as $freebusyPart ) {
$attributes = $content = null;
if( isset ( $freebusyPart['value']['fbtype'] )) {
$attributes .= $this->intAttrDelimiter. 'FBTYPE='. $freebusyPart['value']['fbtype'];
unset ( $freebusyPart['value']['fbtype'] );
$freebusyPart['value'] = array_values( $freebusyPart['value'] );
$cnt = count( $freebusyPart['value']);
foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
$cnt2 = count( $freebusyPeriod[1]);
if(( 7 == $cnt2 ) && // period= -> date-time
isset ( $freebusyPeriod[1]['year'] ) &&
isset ( $freebusyPeriod[1]['month'] ) &&
isset ( $freebusyPeriod[1]['day'] )) {
else { // period= -> dur-time
$output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
* set calendar component property freebusy
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
function setFreebusy( $fbType, $fbValues, $params= FALSE ) {
if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
( 'X-' != substr( $fbType, 0, 2 )))
$freebusy['value'] = array( 'fbtype' => $fbType );
foreach( $fbValues as $fbPeriod ) { // periods => period
$freebusyPeriod = array();
foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
$freebusyPairMember = array();
$cnt = count( $fbMember );
if(( 6 == $cnt ) || ( 7 == $cnt ) || ( array_key_exists( 'year', $fbMember ))) { // date-time value
$date['tz'] = ( !isset ( $date['tz'] )) ? 'Z' : $date['tz'];
$freebusyPairMember = $date;
$tz = ( isset ( $fbMember['tz'] )) ? ' '. $fbMember['tz'] : null;
$parno = ( !empty( $tz )) ? 7 : 6;
$date['tz'] = ( !isset ( $date['tz'] )) ? 'Z' : $date['tz'];
$freebusyPairMember = $date;
else { // array format duration
elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration
( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
if( 'P' != $fbMember{0} )
$fbmember = substr( $fbMember, 1 );
elseif( 8 <= strlen( trim( $fbMember ))) { // ex. 2006-08-03 10:12:18
$date['tz'] = ( !isset ( $date['tz'] )) ? 'Z' : $date['tz'];
$freebusyPairMember = $date;
$freebusyPeriod[] = $freebusyPairMember;
$freebusy['value'][] = $freebusyPeriod;
$freebusy['params'] = $this->_setParams( $params );
/*********************************************************************************/
* creates formatted output for calendar component property geo
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
$content .= number_format( (float) $this->geo['value']['latitude'], 6, '.', '');
$content .= number_format( (float) $this->geo['value']['longitude'], 6, '.', '');
* set calendar component property geo
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param float $longitude
* @param array $params optional
function setGeo( $latitude, $longitude, $params= FALSE ) {
$this->geo['value']['latitude'] = $latitude;
$this->geo['value']['longitude'] = $longitude;
/*********************************************************************************/
* Property Name: LAST-MODIFIED
* creates formatted output for calendar component property last-modified
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
* set calendar component property completed
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-02
* @param mixed $year optional
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setLastModified( $year= FALSE, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
/*********************************************************************************/
* Property Name: LOCATION
* creates formatted output for calendar component property location
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property location
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array params optional
/*********************************************************************************/
* Property Name: ORGANIZER
* creates formatted output for calendar component property organizer
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
, array( 'CN', 'DIR', 'LANGUAGE', 'SENT-BY' ));
$content = 'MAILTO:'. $this->organizer['value'];
* set calendar component property organizer
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.9 - 2007-06-30
* @param array params optional
if( isset ($this->organizer['params']['SENT-BY'] )) {
/*********************************************************************************/
* Property Name: PERCENT-COMPLETE
* creates formatted output for calendar component property percent-complete
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property percent-complete
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
/*********************************************************************************/
* Property Name: PRIORITY
* creates formatted output for calendar component property priority
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property priority
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
/*********************************************************************************/
* creates formatted output for calendar component property rdate
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-13
* @param bool $localtime optional, default FALSE
foreach( $this->rdate as $theRdate ) {
$cnt = count( $theRdate['value'] );
foreach( $theRdate['value'] as $rpix => $rdatePart ) {
( 2 == count( $rdatePart )) &&
unset ( $rdatePart[0]['tz'] );
if( isset ( $theRdate['params']['TZID'] ))
if( isset ( $rdatePart[0]['tz'] ) &&
( 'Z' == $rdatePart[0]['tz'] ))) {
if( 'Z' != substr( $formatted, - 1 ))
$contentPart .= $formatted;
$cnt2 = count( $rdatePart[1]);
if(( 7 == $cnt2 ) && // period= -> date-time
isset ( $rdatePart[1]['year'] ) &&
isset ( $rdatePart[1]['month'] ) &&
isset ( $rdatePart[1]['day'] )) {
unset ( $rdatePart[1]['tz'] );
if( isset ( $theRdate['params']['TZID'] ))
if( isset ( $rdatePart[0]['tz'] ) &&
( 'Z' == $rdatePart[0]['tz'] ))) {
if( 'Z' != substr( $formatted, - 1 ))
$contentPart .= $formatted;
else { // period= -> dur-time
else { // single date start
unset ( $rdatePart['tz'] );
if( isset ( $theRdate['params']['TZID'] ))
$firstPart = ( 2 == count( $theRdate['value'][0] ))
? $theRdate['value'][0][0] : $theRdate['value'][0];
if( isset ( $firstPart['tz'] ) &&
( 'Z' == $firstPart['tz'] ))) {
if( 'Z' != substr( $formatted, - 1 ))
$contentPart .= $formatted;
$content .= $contentPart;
* set calendar component property rdate
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.14 - 2007-10-30
* @param array $params optional
function setRdate( $rdates, $params= FALSE ) {
$input['params'] = $this->_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
$this->_existRem( $input['params'], 'VALUE', 'PERIOD' );
$parno = ( isset ( $input['params']['TZID'] )) ? 6 : null;
$parno = ( !$parno ) ? $this->_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ) : null;
$parno = ( !$parno ) ? $this->_existRem( $input['params'], 'VALUE', 'DATE', 3 ) : 6;
$this->_existRem( $input['params'], 'VALUE', 'DATE-TIME' );
foreach( $rdates as $theRdate ) {
// echo 'setRdate in '; print_r ( $theRdate ); echo "<br />\n"; // test ##
if(( 2 == count( $theRdate )) &&
foreach( $theRdate as $rix => $rPeriod ) {
// echo 'setRdate i2 '; print_r ( $rPeriod ); echo "<br />\n"; // test ##
// echo 'setRdate i3 '; print_r ( $rPeriod ); echo "<br />\n"; // test ##
if (( 1 == count( $rPeriod )) &&
// echo 'setRdate i4 '; print_r ( $rPeriod ); echo "<br />\n"; // test ##
if ( isset ( $input['params']['TZID'] ))
$inputab['tz'] = (string) $input['params']['TZID'];
elseif( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
$parno = ( !isset ( $parno )) ? count( $inputab ) : $parno;
if(( 7 == $parno ) && !isset ( $inputab['tz'] ))
if( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
elseif (((3 == count( $rPeriod )) && ( $rix < 1 )) ||
( 6 == count( $rPeriod )) ||
( 7 == count( $rPeriod )) ||
if( !isset ( $parno ) && 3 < count( $rPeriod ))
if ( isset ( $input['params']['TZID'] ))
$inputab['tz'] = (string) $input['params']['TZID'];
elseif( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
$parno = ( !isset ( $parno )) ? count( $inputab ) : $parno;
if(( 7 == $parno ) && !isset ( $inputab['tz'] ))
if( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
elseif( isset ( $rPeriod['timestamp'] )) { // timestamp
$tz = ( isset ( $rPeriod['tz'] )) ? ' '. $rPeriod['tz'] : null;
$tz = ( isset ( $input['params']['TZID'] )) ? ' '. $input['params']['TZID'] : $tz;
if( !isset ( $parno )) $parno = ( !empty( $tz )) ? 7 : 6;
if(( 7 == $parno ) && !isset ( $inputab['tz'] ))
if( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
else { // array format duration
elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
( in_array( $rPeriod{0}, array( 'P', '+', '-' )))) {
$rPeriod = substr( $rPeriod, 1 );
elseif( 8 <= strlen( trim( $rPeriod ))) { // ex. 2006-08-03 10:12:18
$tz = ( isset ( $input['params']['TZID'] )) ? ' '. $input['params']['TZID'] : '';
if ( isset ( $input['params']['TZID'] ))
$inputab['tz'] = (string) $input['params']['TZID'];
elseif( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
$parno = ( !isset ( $parno )) ? count( $inputab ) : $parno;
$tz = ( isset ( $theRdate['tz'] )) ? ' '. $theRdate['tz'] : null;
$tz = ( isset ( $input['params']['TZID'] )) ? ' '. $input['params']['TZID'] : $tz;
if( !isset ( $parno )) $parno = ( !empty( $tz )) ? 7 : 6;
if(( 7 == $parno ) && !isset ( $inputab['tz'] ))
if( isset ( $inputab['tz'] ))
$inputab['tz'] = (string) $inputab['tz'];
if( isset ( $input['params']['TZID'] ))
$theRdate['tz'] = $input['params']['TZID'];
elseif( !isset ( $theRdate['tz'] )) {
if(( 7 == count( $theRdate )) && isset ( $theRdate[6] )) {
$theRdate['tz'] = $theRdate[6];
elseif(( 4 == count( $theRdate )) && isset ( $theRdate[3] )) {
$theRdate['tz'] = $theRdate[3];
if( !isset ( $parno ) && 3 < count( $theRdate ))
$parno = ( isset ( $theRdate['tz'] )) ? 7 : count( $theRdate );
elseif( !isset ( $parno ))
if(( 7 == $parno ) && !isset ( $inputa['tz'] ))
if( isset ( $inputa['tz'] ))
$inputa['tz'] = (string) $inputa['tz'];
elseif( 8 <= strlen( trim( $theRdate ))) { // ex. 2006-08-03 10:12:18
if( isset ( $input['params']['TZID'] ))
$inputa['tz'] = (string) $input['params']['TZID'];
elseif( isset ( $inputa['tz'] ))
$inputa['tz'] = (string) $inputa['tz'];
$parno = ( !isset ( $parno )) ? count( $inputa ) : $parno;
if(( 7 == $parno ) && !isset ( $inputa['tz'] ))
if( isset ( $inputa['tz'] ))
$inputa['tz'] = (string) $inputa['tz'];
// echo 'setRdate inputa single string '; print_r ( $inputa ); echo "<br />\n"; // test ##
$input['value'][] = $inputa;
if( 0 < count( $input['value'] )) {
// echo 'setRdate ut 1 '; print_r ( $input ); echo "<br />\n"; // test ##
$input['params']['VALUE'] = 'DATE';
foreach( $input['value'] as $eix => $inputa )
unset ( $input['value'][$eix]['tz'] );
unset ( $input['params']['TZID'] );
if( empty( $input['value'][0]['tz'] ) ||
( $input['value'][0]['tz'] == 'Z' ))
elseif( in_array($input['value'][0]['tz']{0}, array( '+', '-' )) &&
elseif( isset ( $input['value'][0]['tz'] )) {
$input['params']['TZID'] = $input['value'][0]['tz'];
foreach( $input['value'] as $eix => $inputa ) {
if( !empty( $input['value'][0]['tz'] ) &&
( $input['value'][0]['tz'] != 'Z' ) &&
( !( in_array( $input['value'][0]['tz']{0}, array( '+', '-' )) &&
unset ( $input['value'][$eix]['tz'] );
elseif( isset ( $input['params']['TZID'] ))
unset ( $input['params']['TZID'] );
// echo 'setRdate ut 2 '; print_r ( $input ); echo "<br />\n"; // test ##
/*********************************************************************************/
* Property Name: RECURRENCE-ID
* creates formatted output for calendar component property recurrence-id
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-26
return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
* set calendar component property recurrence-id
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function setRecurrenceid( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $params= FALSE ) {
/*********************************************************************************/
* Property Name: RELATED-TO
* creates formatted output for calendar component property related-to
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
$content = '<'. $this->_strrep( $relation['value'] ). '>';
$output .= $this->_createElement( 'RELATED-TO', $attributes, $content );
* set calendar component property related-to
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-24
* @param array $params optional
if(( '<' == substr( $relid, 0, 1 )) &&
( '>' == substr( $relid, - 1 )))
$relation['value'] = $relid;
$relation['params'] = $this->_setParams( $params );
if( isset ( $relation['params']['RELTYPE'] ) &&
( strtoupper( $relation['params']['RELTYPE'] ) == 'PARENT' ))
unset ( $relation['params']['RELTYPE'] );
/*********************************************************************************/
* creates formatted output for calendar component property repeat
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property transp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
function setRepeat( $value, $params= FALSE ) {
$this->repeat['value'] = $value;
/*********************************************************************************/
* Property Name: REQUEST-STATUS
* creates formatted output for calendar component property request-status
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-16
$attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
$content = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
$content .= ';'. $this->_strrep( $rstat['value']['text'] );
if( isset ( $rstat['value']['extdata'] ))
$content .= ';'. $this->_strrep( $rstat['value']['extdata'] );
$output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
* set calendar component property request-status
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-16
* @param string $extdata optional
* @param array params optional
$input['value']['statcode'] = $statcode;
$input['value']['text'] = $text;
$input['value']['extdata'] = $extdata;
/*********************************************************************************/
* Property Name: RESOURCES
* creates formatted output for calendar component property resources
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-24
$attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
foreach( $resource['value'] as $rix => $resourcePart )
$resource['value'][$rix] = $this->_strrep( $resourcePart );
$content = implode( ',', $resource['value'] );
$content = $this->_strrep( $resource['value'] );
$output .= $this->_createElement( 'RESOURCES', $attributes, $content );
* set calendar component property recources
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-24
* @param array params optional
$input['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property rrule
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.7.43 - 2006-09-15
* set calendar component property rrule
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
function setRrule( $rruleset, $params= FALSE ) {
foreach( $rruleset as $rrulelabel => $rrulevalue ) {
if( 'UNTIL' != $rrulelabel )
$rrule['value'][$rrulelabel] = $rrulevalue;
(( 3 == count( $rrulevalue )) ||
( 6 == count( $rrulevalue )) ||
( 7 == count( $rrulevalue )) ||
$parno = ( 3 < count( $rrulevalue )) ? 7 : 3 ; // datetime / date
if(( 3 < count( $date )) && !isset ( $date['tz'] ))
$rrule['value'][$rrulelabel] = $date;
elseif( is_array( $rrulevalue ) && isset ( $rrulevalue['timestamp'] )) {
$rrule['value'][$rrulelabel] = $date;
elseif( 8 <= strlen( trim( $rrulevalue ))) { // ex. 2006-08-03 10:12:18
if(( 3 < count( $date )) && !isset ( $date['tz'] ))
$rrule['value'][$rrulelabel] = $date;
/*********************************************************************************/
* Property Name: SEQUENCE
* creates formatted output for calendar component property sequence
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property sequence
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-04-25
* @param int $value optional
* @param array $params optional
/*********************************************************************************/
* creates formatted output for calendar component property status
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property status
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param array $params optional
function setStatus( $value, $params= FALSE ) {
$this->status['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property summary
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property summary
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
/*********************************************************************************/
* creates formatted output for calendar component property transp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property transp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
function setTransp( $value, $params= FALSE ) {
$this->transp['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property trigger
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.19 - 2007-03-27
$content = $attributes = null;
if( isset ( $this->trigger['value']['year'] ) &&
isset ( $this->trigger['value']['month'] ) &&
isset ( $this->trigger['value']['day'] ))
if( TRUE !== $this->trigger['value']['relatedstart'] )
if( $this->trigger['value']['before'] )
* set calendar component property trigger
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.1.1 - 2007-07-07
* @param mixed $month optional
* @param int $day optional
* @param int $week optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param bool $relatedEnd optional
* @param bool $after optional
* @param string $tz optional
* @param array $params optional
function setTrigger( $year= FALSE, $month= FALSE, $day= FALSE, $week= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $relatedEnd= FALSE, $after= FALSE, $tz= FALSE, $params= FALSE ) {
$this->_existRem( $params, 'VALUE', 'DATE-TIME' );
if( isset ( $params['TZID'] )) {
$year['tz'] = $params['TZID'];
unset ( $params['TZID'] );
$tz = ( isset ( $year['tz'] )) ? ' '. $year['tz'] : null;
$parno = ( isset ( $tz )) ? 7 : 6;
foreach( $date as $k => $v )
if( isset ( $params['TZID'] )) {
$year['tz'] = $params['TZID'];
unset ( $params['TZID'] );
$this->_existRem( $params, 'VALUE', 'DATE-TIME' );
$this->_existRem( $params, 'VALUE', 'DURATION' );
$this->_existRem( $params, 'VALUE', 'DATE-TIME' ); // ??
$this->_existRem( $params, 'VALUE', 'DURATION' );
if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration
elseif( '+' == $year{0} )
elseif( 'P' == $year{0} )
if( isset ( $params['TZID'] )) {
$date['tz'] = $params['TZID'];
unset ( $params['TZID'] );
foreach( $date as $k => $v )
if( !empty( $year ) && !empty( $month ) && !empty( $day ) ) { // date
$params['VALUE'] = 'DATE-TIME';
$this->trigger['value'] = array( 'year' => $year
$this->trigger['value']['hour'] = $hour;
$this->trigger['value']['min'] = $min;
$this->trigger['value']['sec'] = $sec;
$this->trigger['value']['tz'] = $tz;
$this->trigger['value']['tz'] = 'Z';
$this->_existRem( $params, 'VALUE', 'DURATION' );
if( $this->_existRem( $params, 'RELATED', 'END', TRUE ))
elseif( $this->_existRem( $params, 'RELATED', 'START', TRUE ))
$this->trigger['value'] = array( 'week' => $week
, 'relatedstart' => !$relatedEnd
$this->trigger['value'] = array( 'day' => $day
, 'relatedstart' => !$relatedEnd
if( !isset ( $this->trigger['value'] ))
$this->trigger['params'] = $params;
/*********************************************************************************/
* creates formatted output for calendar component property tzid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
* set calendar component property tzid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
* @param array $params optional
function setTzid( $value, $params= FALSE ) {
$this->tzid['value'] = $value;
/*********************************************************************************/
* creates formatted output for calendar component property tzname
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
foreach( $this->tzname as $theName ) {
$attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
* set calendar component property tzname
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
* @param string $params optional
function setTzname( $value, $params= FALSE ) {
$input['value'] = $value;
/*********************************************************************************/
* Property Name: TZOFFSETFROM
* creates formatted output for calendar component property tzoffsetfrom
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property tzoffsetfrom
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-25
* @param string $params optional
/*********************************************************************************/
* Property Name: TZOFFSETTO
* creates formatted output for calendar component property tzoffsetto
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property tzoffsetto
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
/*********************************************************************************/
* creates formatted output for calendar component property tzurl
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property tzurl
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
function setTzurl( $value, $params= FALSE ) {
$this->tzurl['value'] = $value;
if( empty( $this->tzurl['value'] ))
/*********************************************************************************/
* creates formatted output for calendar component property uid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* create an unique id for this calendar component object instance
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.7 - 2007-09-04
$date = date('Ymd\THisT');
$base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
for( $p = 0; $p < $length; $p++ )
$unique .= $base{mt_rand( $start, $end )};
$this->uid['value'] = $date. '-'. $unique. '@'. $this->getConfig( 'unique_id' );
$this->uid['params'] = null;
* set calendar component property uid
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
function setUid( $value, $params= FALSE ) {
$this->uid['value'] = $value;
if( empty( $this->uid['value'] ))
/*********************************************************************************/
* creates formatted output for calendar component property url
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* set calendar component property url
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.18 - 2007-03-18
* @param string $params optional
function setUrl( $value, $params= FALSE ) {
$this->url['value'] = $value;
if( empty( $this->url['value'] ))
/*********************************************************************************/
* creates formatted output for calendar component property x-prop
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.3.2 - 2007-11-25
foreach( $this->xprop as $label => $xpropPart ) {
$attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
foreach( $xpropPart['value'] as $pix => $theXpart )
$xpropPart['value'][$pix] = $this->_strrep( $theXpart );
$xpropPart['value'] = implode( ',', $xpropPart['value'] );
$xpropPart['value'] = $this->_strrep( $xpropPart['value'] );
* set calendar component property x-prop
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.7 - 2007-06-21
* @param array $params optional
function setXprop( $label, $value, $params= FALSE ) {
if( empty( $label ) || empty( $value ))
$xprop = array( 'value' => $value );
$xprop['params'] = $toolbox->_setParams( $params );
$this->xprop[$label] = $xprop;
/*********************************************************************************/
/*********************************************************************************/
* create element format parts
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.6 - 2006-06-20
$objectname = ( isset ( $this->timezonetype )) ?
$objectname = ( isset ( $this->timezonetype )) ?
* creates formatted output for calendar component property
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.15 - 2007-01-08
* @param string $label property name
* @param string $attributes property attributes
* @param string $content property content (optional)
$categoriesAttrLang = null;
$attachInlineBinary = FALSE;
if( !empty( $attributes )) {
$attributes = trim( $attributes );
if ( 'xcal' == $this->format) {
foreach( $attributes2 as $attribute ) {
$attrKVarr = explode( '=', $attribute );
if( empty( $attrKVarr[0] ))
if( !isset ( $attrKVarr[1] )) {
$attrValue = $attrKVarr[0];
elseif( 2 == count( $attrKVarr)) {
$attrValue = $attrKVarr[1];
$attrValue = implode( '=', $attrKVarr );
if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
$attachInlineBinary = TRUE;
if( 'fmttype' == $attrKey )
$attachfmttype = $attrKey. '='. $attrValue;
elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
$categoriesAttrLang = $attrKey. '='. $attrValue;
$attributes .= ( !empty( $attrKey )) ? $attrKey. '=' : null;
if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, - 1 ))) {
$attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
if(((( 'attach' == $label ) && !$attachInlineBinary ) ||
( in_array( $label, array( 'tzurl', 'url' )))) && ( 'xcal' == $this->format)) {
$docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
$this->xcaldecl[] = array( 'xmldecl' => 'ENTITY'
$attributes .= 'uri="'. $docname. '"';
if( 'attach' == $label ) {
elseif(( 'attach' == $label ) && $attachInlineBinary && ( 'xcal' == $this->format)) {
$content = $this->nl. $this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
foreach( $items as $item )
$content .= $this->_createElement( 'item', $categoriesAttrLang, $item ); // max one attribute
list ($lat, $lon) = explode(';', $content);
* creates formatted output for calendar component property parameters
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.22 - 2007-04-10
* @param array $params optional
* @param array $ctrKeys optional
$attrLANG = $attr1 = $attr2 = null;
$CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ;
$LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
$CNattrExist = $LANGattrExist = FALSE;
foreach( $params as $paramKey => $paramValue ) {
elseif(( 'LANGUAGE' == $paramKey ) && $LANGattrKey ) {
elseif(( 'CN' == $paramKey ) && $CNattrKey ) {
elseif(( 'ALTREP' == $paramKey ) && in_array( $paramKey, $ctrKeys ))
elseif(( 'DIR' == $paramKey ) && in_array( $paramKey, $ctrKeys ))
elseif(( 'SENT-BY' == $paramKey ) && in_array( $paramKey, $ctrKeys ))
if(( $CNattrExist || $LANGattrKey ) && $lang )
return $attrLANG. $attr1. $attr2;
* convert local startdate/enddate (Ymd[His]) to duration
* uses this component dates if missing input dates
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.11 - 2007-11-03
* @param array $startdate, optional
* @param array $duration, optional
if( !$startdate || !$enddate ) {
if( FALSE === ( $startdate = $this->getProperty( 'dtstart' )))
if( FALSE === ( $enddate = $this->getProperty( 'dtend' ))) // vevent/vfreebusy
if( FALSE === ( $enddate = $this->getProperty( 'due' ))) // vtodo
if( !$startdate || !$enddate )
$startWdate = mktime( 0, 0, 0, $startdate['month'], $startdate['day'], $startdate['year'] );
$endWdate = mktime( 0, 0, 0, $enddate['month'], $enddate['day'], $enddate['year'] );
$wduration = $endWdate - $startWdate;
$dur['week'] = (int) floor( $wduration / ( 7 * 24 * 60 * 60 ));
$wduration = $wduration % ( 7 * 24 * 60 * 60 );
$dur['day'] = (int) floor( $wduration / ( 24 * 60 * 60 ));
$wduration = $wduration % ( 24 * 60 * 60 );
$dur['hour'] = (int) floor( $wduration / ( 60 * 60 ));
$wduration = $wduration % ( 60 * 60 );
$dur['min'] = (int) floor( $wduration / ( 60 ));
$dur['sec'] = (int) $wduration % ( 60 );
* convert date/datetime to timestamp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param array $datetime datetime/(date)
* @param string $tz timezone
if( !isset ( $datetime['hour'] )) $datetime['hour'] = '0';
if( !isset ( $datetime['min'] )) $datetime['min'] = '0';
if( !isset ( $datetime['sec'] )) $datetime['sec'] = '0';
foreach( $datetime as $dkey => $dvalue ) {
$datetime[$dkey] = (integer) $dvalue;
if( isset ( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] )))
$output = mktime( $datetime['hour']
, $datetime['sec'] + $offset
* ensures internal date-time/date format for input date-time/date in array format
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.3.0 - 2006-08-15
* @param int $parno optional, default FALSE
foreach( $datetime as $dateKey => $datePart ) {
case '0': case 'year': $output['year'] = $datePart; break;
case '1': case 'month': $output['month'] = $datePart; break;
case '2': case 'day': $output['day'] = $datePart; break;
case '3': case 'hour': $output['hour'] = $datePart; break;
case '4': case 'min' : $output['min'] = $datePart; break;
case '5': case 'sec' : $output['sec'] = $datePart; break;
case '6': case 'tz' : $output['tz'] = $datePart; break;
if( !isset ( $output['hour'] ))
if( !isset ( $output['min'] ))
if( !isset ( $output['sec'] ))
* ensures internal date-time/date format for input date-time/date in string fromat
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.10 - 2007-10-19
* @param int $parno optional, default FALSE
$datetime = (string) trim( $datetime );
$len = strlen( $datetime ) - 1;
if( 'Z' == substr( $datetime, - 1 )) {
( '-' == substr( $datetime, - 3, 1 )) ||
( ':' == substr( $datetime, - 3, 1 )) ||
( '.' == substr( $datetime, - 3, 1 ))) {
( ' +' == substr( $datetime, - 6, 2 )) ||
( ' -' == substr( $datetime, - 6, 2 ))) {
$tz = substr( $datetime, - 5, 5 );
$datetime = substr( $datetime, 0, ($len - 5));
( ' +' == substr( $datetime, - 8, 2 )) ||
( ' -' == substr( $datetime, - 8, 2 ))) {
$tz = substr( $datetime, - 7, 7 );
$datetime = substr( $datetime, 0, ($len - 7));
elseif( 'T' == substr( $datetime, - 7, 1 )) {
//echo "_date_time_string 1: $datetime tz=$tz.<br />\n"; // test ###
$cx = $tx = 0; // 19970415T133000 US-Eastern
for( $cx = - 1; $cx > ( 9 - $len ); $cx-- ) {
break; // if exists, tz ends here.. . ?
$tx-- ; // tz length counter
$tz = substr( $datetime, $tx );
$datetime = trim( substr( $datetime, 0, $len + $tx + 1 ));
//echo "_date_time_string 1a: $datetime tz=$tz.<br />\n"; // test ###
//echo "_date_time_string 1b: $datetime tz=$tz.<br />\n"; // test ###
( 'T' == substr( $datetime, 8, 1 )) &&
$datetime = substr( $datetime, 4, 2 )
. '/'. substr( $datetime, 6, 2 )
. '/'. substr( $datetime, 0, 4 )
. ' '. substr( $datetime, 9, 2 )
. ':'. substr( $datetime, 11, 2 )
// $datetime = substr( $datetime, 0, 8 ).substr( $datetime, 9 ); // not PHP 4.x compatible
//echo "_date_time_string 1c: $datetime tz=$tz.<br />\n"; // test ###
// echo "_date_time_string 2: $datestring tz=$tz. datetime=$datetime<br />\n"; // test ###
$output['year'] = substr( $datestring, 0, 4 );
$output['month'] = substr( $datestring, 5, 2 );
$output['day'] = substr( $datestring, 8, 2 );
if(( 6 == $parno ) || ( 7 == $parno )) {
$output['hour'] = substr( $datestring, 11, 2 );
$output['min'] = substr( $datestring, 14, 2 );
$output['sec'] = substr( $datestring, 17, 2 );
if(( '00' < substr( $datestring, 11, 2 )) ||
( '00' < substr( $datestring, 14, 2 )) ||
( '00' < substr( $datestring, 17, 2 ))) {
$output['hour'] = substr( $datestring, 11, 2 );
$output['min'] = substr( $datestring, 14, 2 );
$output['sec'] = substr( $datestring, 17, 2 );
* ensures internal duration format for input in array format
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.1.1 - 2007-06-24
( 1 == count( $duration )) &&
isset ( $duration['sec'] ) &&
( 60 < $duration['sec'] )) {
$durseconds = $duration['sec'];
$output['week'] = floor( $durseconds / ( 60 * 60 * 24 * 7 ));
$durseconds = $durseconds % ( 60 * 60 * 24 * 7 );
$output['day'] = floor( $durseconds / ( 60 * 60 * 24 ));
$durseconds = $durseconds % ( 60 * 60 * 24 );
$output['hour'] = floor( $durseconds / ( 60 * 60 ));
$durseconds = $durseconds % ( 60 * 60 );
$output['min'] = floor( $durseconds / ( 60 ));
$output['sec'] = ( $durseconds % ( 60 ));
foreach( $duration as $durKey => $durValue ) {
case '0': case 'week': $output['week'] = $durValue; break;
case '1': case 'day': $output['day'] = $durValue; break;
case '2': case 'hour': $output['hour'] = $durValue; break;
case '3': case 'min': $output['min'] = $durValue; break;
case '4': case 'sec': $output['sec'] = $durValue; break;
if( isset ( $output['week'] ) && ( 0 < $output['week'] ))
elseif (( isset ( $output['hour'] ) && ( 0 < $output['hour'] )) ||
( isset ( $output['min'] ) && ( 0 < $output['min'] )) ||
(isset ( $output['sec'] ) && ( 0 < $output['sec'] ))) {
if( !isset ( $output['hour'] ))
if( !isset ( $output['min'] ))
if( !isset ( $output['sec'] ))
* convert duration to date in array format based on dtstart value
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.11 - 2007-12-02
* @param array $startdate, optional
* @param array $duration, optional
* @return array, date format
if( $startdate && $duration ) {
elseif( isset ( $this->dtstart['value'] ) && isset ( $this->duration['value'] )) {
$dateOnly = ( isset ( $d1['hour'] ) || isset ( $d1['min'] ) || isset ( $d1['sec'] )) ? FALSE : TRUE;
$d1['hour'] = ( isset ( $d1['hour'] )) ? $d1['hour'] : 0;
$d1['min'] = ( isset ( $d1['min'] )) ? $d1['min'] : 0;
$d1['sec'] = ( isset ( $d1['sec'] )) ? $d1['sec'] : 0;
if( isset ( $dur['week'] ))
$dtend += ( $dur['week'] * 7 * 24 * 60 * 60 );
if( isset ( $dur['day'] ))
$dtend += ( $dur['day'] * 24 * 60 * 60 );
if( isset ( $dur['hour'] ))
$dtend += ( $dur['hour'] * 60 * 60 );
if( isset ( $dur['min'] ))
$dtend += ( $dur['min'] * 60 );
if( isset ( $dur['sec'] ))
$dtend2['year'] = date('Y', $dtend );
$dtend2['month'] = date('m', $dtend );
$dtend2['day'] = date('d', $dtend );
$dtend2['hour'] = date('H', $dtend );
$dtend2['min'] = date('i', $dtend );
$dtend2['sec'] = date('s', $dtend );
$dtend2['tz'] = $d1['tz'];
if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
unset ( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
* ensures internal duration format for input in string format
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.0.5 - 2007-03-14
* @param string $duration
$duration = (string) trim( $duration );
$duration = substr( $duration, 1 );
return false; // no leading P !?!?
$duration = substr( $duration, 1 ); // skip P
for( $ix= 0; $ix < strlen( $duration ); $ix++ ) {
return false; // unknown duration controll character !?!?
* if exist, remove key with expected value from array and return spec. value
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.19 - 2007-03-18
* @param int $hitval optional
function _existRem( &$array, $expkey, $expval= FALSE, $hitval= null ) {
if( !isset ( $array[$expkey] ))
if( $expval != $array[$expkey] )
unset ( $array[$expkey] );
* creates formatted output for calendar component property data value type date/date-time
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
* @param int $parno, optional, default 6
if( !isset ( $datetime['year'] ) &&
!isset ( $datetime['month'] ) &&
!isset ( $datetime['day'] ) &&
!isset ( $datetime['hour'] ) &&
!isset ( $datetime['min'] ) &&
!isset ( $datetime['sec'] ))
, (integer) $datetime['month']
, (integer) $datetime['day']
, (integer) $datetime['year']));
if( isset ( $datetime['hour'] ) ||
isset ( $datetime['min'] ) ||
isset ( $datetime['sec'] ) ||
isset ( $datetime['tz'] )) {
if( isset ( $datetime['tz'] ) &&
!isset ( $datetime['hour'] ))
if( isset ( $datetime['hour'] ) &&
!isset ( $datetime['min'] ))
if( isset ( $datetime['hour'] ) &&
isset ( $datetime['min'] ) &&
!isset ( $datetime['sec'] ))
foreach( $datetime as $dkey => $dvalue ) {
$datetime[$dkey] = (integer) $dvalue;
$output .= date('\THis', mktime( $datetime['hour']
if( isset ( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) {
$datetime['tz'] = trim( $datetime['tz'] );
if( 'Z' == $datetime['tz'] )
$output = date('Ymd\THis\Z', mktime( $datetime['hour']
, $datetime['sec'] + $offset
* creates formatted output for calendar component property data value type duration
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-13
* @param array $duration ( week, day, hour, min, sec )
if( !isset ( $duration['week'] ) &&
!isset ( $duration['day'] ) &&
!isset ( $duration['hour'] ) &&
!isset ( $duration['min'] ) &&
!isset ( $duration['sec'] ))
if( isset ( $duration['week'] ) && ( 0 < $duration['week'] ))
$output .= $duration['week']. 'W';
if( isset ($duration['day'] ) && ( 0 < $duration['day'] ))
$output .= $duration['day']. 'D';
if(( isset ( $duration['hour']) && ( 0 < $duration['hour'] )) ||
( isset ( $duration['min']) && ( 0 < $duration['min'] )) ||
( isset ( $duration['sec']) && ( 0 < $duration['sec'] ))) {
if( 0 < $duration['hour'] ) {
$output .= $duration['hour']. 'H';
if( 0 < $duration['min'] ) {
$output .= $duration['min']. 'M';
if( 0 < $duration['sec'] )
$output .= $duration['sec']. 'S';
elseif( 0 < $duration['sec'] )
$output .= '0M'. $duration['sec']. 'S';
elseif( 0 < $duration['min'] ) {
$output .= '0H'. $duration['min']. 'M';
if( 0 < $duration['sec'] )
$output .= $duration['sec']. 'S';
elseif( 0 < $duration['sec'] )
$output .= '0H0M'. $duration['sec']. 'S';
* creates formatted output for calendar component property data value type recur
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* @param array $recurlabel
* @param array $recurdata
foreach( $recurdata as $therule ) {
$attributes = ( isset ( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
$content1 = $content2 = null;
foreach( $therule['value'] as $rulelabel => $rulevalue ) {
$content1 .= "FREQ=$rulevalue";
$content2 .= ";$rulelabel=$rulevalue";
$content2 .= ";$rulelabel=";
foreach( $rulevalue as $vix => $valuePart ) {
$content2 .= ( $vix ) ? ',' : null;
$content2 .= ";$rulelabel=";
foreach( $rulevalue as $vix => $valuePart ) {
$content21 = $content22 = null;
$content2 .= ( $bydaycnt ) ? ',' : null;
foreach( $valuePart as $vix2 => $valuePart2 ) {
$content21 .= $valuePart2;
$content22 .= $valuePart2;
$content2 .= $content21. $content22;
$content2 .= ( $bydaycnt ) ? ',' : null;
$content21 .= $valuePart;
$content22 .= $valuePart;
$content2 .= $content21. $content22;
$content2 .= ";$rulelabel=$rulevalue";
$recur .= $this->_createElement( $recurlabel, $attributes, $content1. $content2 );
* create property name case - lower/upper
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 0.9.7 - 2006-11-20
* @param string $propertyName
* remakes a recur pattern to an array of dates
* if missing, UNTIL is set 1 year from startdate (emergency break)
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.11 - 2007-11-26
* @param array $result, array to update, array([timestamp] => timestamp)
* @param array $recur, pattern for recurrency (only value part, params ignored)
* @param array $wdate, component start date
* @param array $startdate, start date
* @param array $enddate, optional
* @return array of recurrence (start-)dates as index
* @todo BYHOUR, BYMINUTE, BYSECOND, ev. BYSETPOS due to ambiguity, WEEKLY at year end/start
function _recur2date( & $result, $recur, $wdate, $startdate, $enddate= FALSE ) {
foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
// echo "recur __in_ ".implode('-',$startdate)." period start ".implode('-',$wdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
if( !isset ( $recur['COUNT'] ) && !isset ( $recur['UNTIL'] ))
$recur['UNTIL'] = $enddate; // create break
if( isset ( $recur['UNTIL'] )) {
if( $endDatets > $tdatets ) {
$endDatets = $tdatets; // emergency break
if( $wdatets > $endDatets ) {
//echo "recur out of date ".implode('-',$this->_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
return array(); // nothing to do.. .
if( !isset ( $recur['FREQ'] )) // "MUST be specified.. ."
$recur['FREQ'] = 'DAILY'; // ??
$wkst = ( isset ( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24* 60* 60 : 0; // ??
if( !isset ( $recur['INTERVAL'] ))
$countcnt = ( !isset ( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
/* find out how to step up dates and set index for interval count */
if( 'YEARLY' == $recur['FREQ'] )
elseif( 'MONTHLY' == $recur['FREQ'] )
elseif( 'WEEKLY' == $recur['FREQ'] )
if( isset ( $step['year'] ) && isset ( $recur['BYMONTH'] ))
$step = array( 'month' => 1 );
if( empty( $step ) && isset ( $recur['BYWEEKNO'] )) // ??
$step = array( 'day' => 7 );
if( isset ( $recur['BYYEARDAY'] ) || isset ( $recur['BYMONTHDAY'] ) || isset ( $recur['BYDAY'] ))
$step = array( 'day' => 1 );
if( 1 < $recur['INTERVAL'] ) {
$intervalarr = array( $intervalix => 0 );
if( isset ( $recur['BYSETPOS'] )) { // save start date + weekno
$bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
$bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
$bysetposYold = $wdate['year'];
$bysetposMold = $wdate['month'];
$bysetposDold = $wdate['day'];
foreach( $recur['BYSETPOS'] as $bix => $bval )
$recur['BYSETPOS'][$bix] = (int) $bval;
$recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
$this->_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
$daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
// echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
if( isset ( $endDatets ) && ( $wdatets > $endDatets ))
if( isset ( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
if( $year_old != $wdate['year'] ) {
$year_old = $wdate['year'];
for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
foreach( $daynames as $dn )
$yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
$mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
for( $d = 1; $d <= $mcnt; $d++ ) {
$daycnts[$m][$d] = array();
if( isset ( $recur['BYYEARDAY'] )) {
$daycnts[$m][$d]['yearcnt_up'] = $yeardays;
if( isset ( $recur['BYDAY'] )) {
$day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
$daycnts[$m][$d]['DAY'] = $day;
$daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
$daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
if( isset ( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
$daycnts[$m][$d]['weekno_up'] =(int) date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
if( isset ( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
for( $d= 31; $d > 25; $d-- ) { // get last weekno for year
$weekno = $daycnts[12][$d]['weekno_up'];
elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
$weekno = $daycnts[12][$d]['weekno_up'];
for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
foreach( $daynames as $dn )
$yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
$mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
for( $d = $mcnt; $d > 0; $d-- ) {
if( isset ( $recur['BYYEARDAY'] )) {
$daycnts[$m][$d]['yearcnt_down'] = $daycnt;
if( isset ( $recur['BYMONTHDAY'] )) {
$daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
if( isset ( $recur['BYDAY'] )) {
$day = $daycnts[$m][$d]['DAY'];
$daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
$daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
if( isset ( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
$daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
if( 1 < $recur['INTERVAL'] ) {
/* create interval index */
$currentKey = end( $currentKey ); // get last index
if( $currentKey != $intervalix )
$intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
( 0 != $intervalarr[$intervalix] )) {
//echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
else // continue within the selected interval
$intervalarr[$intervalix] = 0;
//echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
if( $updateOK && isset ( $recur['BYMONTH'] ))
,($wdate['month'] - 13));
if( $updateOK && isset ( $recur['BYWEEKNO'] ))
, $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
, $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
if( $updateOK && isset ( $recur['BYYEARDAY'] ))
, $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
, $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
if( $updateOK && isset ( $recur['BYMONTHDAY'] ))
, $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
//echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
if( $updateOK && isset ( $recur['BYDAY'] )) {
if( isset ( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
$daynoexists = $daynosw = $daynamesw = FALSE;
if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
if( isset ( $recur['BYDAY'][0] )) {
if(( isset ( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset ( $recur['BYMONTH'] ))
, $daycnts[$m][$d]['monthdayno_up']
, $daycnts[$m][$d]['monthdayno_down'] );
elseif( isset ( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
, $daycnts[$m][$d]['yeardayno_up']
, $daycnts[$m][$d]['yeardayno_down'] );
if(( $daynoexists && $daynosw && $daynamesw ) ||
( !$daynoexists && !$daynosw && $daynamesw )) {
//echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
foreach( $recur['BYDAY'] as $bydayvalue ) {
$daynoexists = $daynosw = $daynamesw = FALSE;
if( isset ( $bydayvalue['DAY'] ) &&
( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
if( isset ( $bydayvalue[0] )) {
if(( isset ( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
isset ( $recur['BYMONTH'] ))
, $daycnts[$m][$d]['monthdayno_up']
, $daycnts[$m][$d]['monthdayno_down'] );
elseif( isset ( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
, $daycnts[$m][$d]['yeardayno_up']
, $daycnts[$m][$d]['yeardayno_down'] );
//echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
if(( $daynoexists && $daynosw && $daynamesw ) ||
( !$daynoexists && !$daynosw && $daynamesw )) {
//echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
if( isset ( $recur['BYSETPOS'] ) &&
( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
if( isset ( $recur['WEEKLY'] )) {
if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
$bysetposw1[] = $wdatets;
$bysetposw2[] = $wdatets;
if(( isset ( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
( $bysetposYold == $wdate['year'] )) ||
( isset ( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
(( $bysetposYold == $wdate['year'] ) &&
( $bysetposMold == $wdate['month'] ))) ||
( isset ( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
(( $bysetposYold == $wdate['year'] ) &&
( $bysetposMold == $wdate['month']) &&
( $bysetposDold == $wdate['sday'] ))))
$bysetposymd1[] = $wdatets;
$bysetposymd2[] = $wdatets;
/* update result array if BYSETPOS is set */
if( $startdatets <= $wdatets ) { // only output within period
$result[$wdatets] = TRUE;
//echo "recur ".implode('-',$this->_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
//else echo "recur undate ".implode('-',$this->_date_time_string(date('Y-m-d H:i:s',$wdatets),6))." okdatstart ".implode('-',$this->_date_time_string(date('Y-m-d H:i:s',$startdatets),6))."<br />\n";//test
/* check if BYSETPOS is set for updating result array */
if( $updateOK && isset ( $recur['BYSETPOS'] )) {
if( isset ( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
( $bysetposYold != $wdate['year'] )) {
$bysetposYold = $wdate['year'];
elseif( isset ( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
(( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
$bysetposYold = $wdate['year'];
$bysetposMold = $wdate['month'];
elseif( isset ( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) {
$weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
if( $bysetposWold != $weekno ) {
elseif( isset ( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
(( $bysetposYold != $wdate['year'] ) ||
( $bysetposMold != $wdate['month'] ) ||
( $bysetposDold != $wdate['sday'] ))) {
$bysetposYold = $wdate['year'];
$bysetposMold = $wdate['month'];
$bysetposDold = $wdate['day'];
if( isset ( $recur['BYWEEKNO'] )) {
$bysetposarr1 = & $bysetposw1;
$bysetposarr2 = & $bysetposw2;
$bysetposarr1 = & $bysetposymd1;
$bysetposarr2 = & $bysetposymd2;
foreach( $recur['BYSETPOS'] as $ix ) {
if( 0 > $ix ) // both positive and negative BYSETPOS allowed
$ix = ( count( $bysetposarr1 ) + $ix + 1);
if( isset ( $bysetposarr1[$ix] )) {
if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
$result[$bysetposarr1[$ix]] = TRUE;
//echo "recur ".implode('-',$this->_date_time_string(date('Y-m-d H:i:s',$bysetposarr1[$ix]),6))."<br />\n";//test
if( isset ( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
$bysetposarr1 = $bysetposarr2;
elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
/* create interval index */
$intervalix = $date['year'];
$intervalix = $date['year']. '-'. $date['month'];
$intervalix = (int) date( 'W', ( $wdatets + $wkst ));
$intervalix = $date['year']. '-'. $date['month']. '-'. $date['day'];
* convert format for input date to internal date with parameters
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-01
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function _setDate( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $tz= FALSE, $params= FALSE ) {
$input['params'] = $this->_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
if( isset ( $input['params']['TZID'] ))
$year['tz'] = $input['params']['TZID'];
$hitval = ( !empty( $year['tz'] ) || !empty( $year[6] ) || ( 4 == count( $year ))) ? 7 : 6;
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE', 3 );
elseif( is_array( $year ) && isset ( $year['timestamp'] )) {
$input['params'] = $this->_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
if( isset ( $input['params']['TZID'] ))
$year['tz'] = $input['params']['TZID'];
$tz = ( isset ( $year['tz'] )) ? ' '. $year['tz'] : null;
$hitval = ( !empty( $tz )) ? 7 : 6;
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE', 3 );
elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
$input['params'] = $this->_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 );
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE', 3 );
if( isset ( $input['params']['TZID'] ))
$input['value']['tz'] = $input['params']['TZID'];
$input['params'] = $this->_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
if( isset ( $input['params']['TZID'] ))
$tz = $input['params']['TZID'];
$hitval = ( !empty( $tz )) ? 7 : null;
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
$parno = $this->_existRem( $input['params'], 'VALUE', 'DATE', 3 );
$input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day );
$input['value']['hour'] = ( $hour ) ? $hour : '0';
$input['value']['min'] = ( $min ) ? $min : '0';
$input['value']['sec'] = ( $sec ) ? $sec : '0';
$input['value']['tz'] = $tz;
$input['params']['VALUE'] = 'DATE';
unset ( $input['value']['tz'] );
unset ( $input['params']['TZID'] );
if( isset ( $input['value']['tz'] ))
$input['value']['tz'] = (string) $input['value']['tz'];
if( !empty( $input['value']['tz'] ) &&
( $input['value']['tz'] != 'Z' ) &&
( !( in_array($input['value']['tz']{0}, array( '+', '-' )) &&
$input['params']['TZID'] = $input['value']['tz'];
unset ( $input['value']['tz'] );
elseif( isset ( $input['params']['TZID'] ))
unset ( $input['params']['TZID'] );
* convert format for input date (UTC) to internal date with parameters
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-08-02
* @param mixed $month optional
* @param int $day optional
* @param int $hour optional
* @param int $min optional
* @param int $sec optional
* @param array $params optional
function _setDate2( $year, $month= FALSE, $day= FALSE, $hour= FALSE, $min= FALSE, $sec= FALSE, $params= FALSE ) {
(( 6 == count( $year )) ||
elseif( is_array( $year ) && isset ( $year['timestamp'] )) {
elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
$input['value'] = array( 'year' => $year
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;
$input['value']['tz'] = 'Z';
* set input (formatted) parameters- component property attributes
* default parameters can be set, if missing
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 1.x.x - 2007-05-01
foreach( $params as $paramKey => $paramValue ) {
foreach( $paramValue as $pkey => $pValue ) {
if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, - 1 )))
$paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, - 1 )))
$paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
foreach( $defaults as $paramKey => $paramValue ) {
if( !isset ( $input[$paramKey] ))
$input[$paramKey] = $paramValue;
return (0 < count( $input )) ? $input : null;
* step date, return updated date, array and timpstamp
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.15 - 2007-11-12
* @param array $date, date to step
* @param array $step, default array( 'day' => 1 )
function _stepdate( &$date, &$timestamp, $step= array( 'day' => 1 )) {
foreach( $step as $stepix => $stepvalue )
$date[$stepix] += $stepvalue;
foreach( $date as $k => $v ) {
* convert (numeric) tz to offset seconds
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.2 - 2007-07-29
$tz = trim( (string) $tz );
( '0000' <= substr( $tz, - 4 )) &&
( '9999' >= substr( $tz, - 4 )) &&
(( '+' == substr( $tz, 0, 1 )) ||
( '-' == substr( $tz, 0, 1 )))) {
$hours2sec = substr( $tz, 1, 2 ) * 3600;
$min2sec = substr( $tz, - 2 ) * 60;
$offset = (int) ( $sign. '1' * ($hours2sec + $min2sec ));
elseif(( 7 == strlen( $tz )) &&
( '000000' <= substr( $tz, - 6 )) &&
( '999999' >= substr( $tz, - 6 )) &&
(( '+' == substr( $tz, 0, 1 )) ||
( '-' == substr( $tz, 0, 1 )))) {
$hours2sec = substr( $tz, 1, 2 ) * 3600;
$min2sec = substr( $tz, 3, 2 ) * 60;
$offset = (int) ( $sign. '1' * ( $hours2sec + $min2sec + $sec ));
/*********************************************************************************/
/*********************************************************************************/
* get general component config variables or info about subcomponents
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-10-23
foreach( $this->components as $cix => $component ) {
unset ( $component->propix );
$info[$cix]['ordno'] = $cix + 1;
$info[$cix]['type'] = $component->objName;
$info[$cix]['uid'] = $component->getProperty( 'uid' );
$info[$cix]['props'] = $component->getConfig( 'propinfo' );
$info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
unset ( $component->propix );
// get language for calendar component as defined in [RFC 1766]
$output['DUE'] = count( $this->due ) / 2;
$output['GEO'] = count( $this->geo ) / 2;
if( empty( $this->uid['value'] ))
$output['URL'] = count( $this->url ) / 2;
* general component config setting
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-10-25
// set language for calendar component as defined in [RFC 1766]
/*********************************************************************************/
* delete component property value
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.15 - 2007-10-29
* @param string $propName
* @param int @propix, optional, if specific property is wanted in case of multiply occurences
* @return bool, if successfull delete TRUE
$propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
$propix = ( isset ( $this->propdelix[$propName] )) ? $this->propdelix[$propName] + 2 : 1;
$this->propdelix[$propName] = -- $propix;
if(( 0 < $propix ) || !isset ( $this->action['value'] )) return FALSE;
$this->attach[$propix] = null;
if( !isset ( $this->categories[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->class['value'] )) return FALSE;
if( !isset ( $this->comment[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->completed['value'] )) return FALSE;
if( !isset ( $this->contact[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->created['value'] )) return FALSE;
if( !isset ( $this->description[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->dtend['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->dtstamp['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->dtstart['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->due['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->duration['value'] )) return FALSE;
if( !isset ( $this->exdate[$propix])) return FALSE;
$this->exdate[$propix] = null;
if( !isset ( $this->exrule[$propix])) return FALSE;
$this->exrule[$propix]['value'];
if( !isset ( $this->freebusy[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->geo['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->lastmodified['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->location['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->organizer['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->percentcomplete['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->priority['value'] )) return FALSE;
if( !isset ( $this->rdate[$propix])) return FALSE;
$this->rdate[$propix] = null;
if(( 0 < $propix ) || !isset ( $this->recurrenceid['value'] )) return FALSE;
if( !isset ( $this->relatedto[$propix])) return FALSE;
if(( 0 < $propix ) || !isset ( $this->repeat['value'] )) return FALSE;
if( !isset ( $this->resources[$propix])) return FALSE;
if( !isset ( $this->rrule[$propix])) return FALSE;
$this->rrule[$propix] = null;
if(( 0 < $propix ) || !isset ( $this->sequence['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->status['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->summary['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->transp['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->trigger['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzid['value'] )) return FALSE;
if( !isset ( $this->tzname[$propix])) return FALSE;
$this->tzname[$propix] = null;
if(( 0 < $propix ) || !isset ( $this->tzoffsetfrom['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzoffsetto['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzurl['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->uid['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->url['value'] )) return FALSE;
if( $propName != 'X-PROP' ) {
if( !isset ( $this->xprop[$propName] )) return FALSE;
unset ( $this->xprop[$propName] );
if( count( $this->xprop ) <= $propix ) return FALSE;
foreach( $this->xprop as $xpropkey => $xpropvalue ) {
if( $propix == $xpropno ) {
unset ( $this->xprop[$xpropkey] );
* get component property value/params
* if property has multiply values, consequtive function calls are needed
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.1.1 - 2007-07-07
* @param string $propName, optional
* @param int @propix, optional, if specific property is wanted in case of multiply occurences
* @param bool $inclParam=FALSE
* @param bool $specform=FALSE
function getProperty( $propName= FALSE, $propix= FALSE, $inclParam= FALSE, $specform= FALSE ) {
$propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
$propix = ( isset ( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
$this->propix[$propName] = -- $propix;
if(( 0 < $propix ) || !isset ( $this->action['value'] )) return FALSE;
return ( $inclParam ) ? $this->action : $this->action['value'];
return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->class['value'] )) return FALSE;
return ( $inclParam ) ? $this->class : $this->class['value'];
return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->completed['value'] )) return FALSE;
return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->created['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->dtend['value'] )) return FALSE;
return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
if( !isset ( $this->dtstamp['value'] ))
if(( 0 < $propix ) || !isset ( $this->dtstart['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->due['value'] )) return FALSE;
return ( $inclParam ) ? $this->due : $this->due['value'];
if(( 0 < $propix ) || !isset ( $this->duration['value'] )) return FALSE;
return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value;
return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->geo['value'] )) return FALSE;
return ( $inclParam ) ? $this->geo : $this->geo['value'];
if(( 0 < $propix ) || !isset ( $this->lastmodified['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->location['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->organizer['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->percentcomplete['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->priority['value'] )) return FALSE;
if( count( $this->rdate ) <= $propix ) return FALSE;
return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->recurrenceid['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->repeat['value'] )) return FALSE;
return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
if( count( $this->rrule ) <= $propix ) return FALSE;
return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->sequence['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->status['value'] )) return FALSE;
return ( $inclParam ) ? $this->status : $this->status['value'];
if(( 0 < $propix ) || !isset ( $this->summary['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->transp['value'] )) return FALSE;
return ( $inclParam ) ? $this->transp : $this->transp['value'];
if(( 0 < $propix ) || !isset ( $this->trigger['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzid['value'] )) return FALSE;
return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
if(( 0 < $propix ) || !isset ( $this->tzoffsetfrom['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzoffsetto['value'] )) return FALSE;
if(( 0 < $propix ) || !isset ( $this->tzurl['value'] )) return FALSE;
return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
if( empty( $this->uid['value'] ))
return ( $inclParam ) ? $this->uid : $this->uid['value'];
if(( 0 < $propix ) || !isset ( $this->url['value'] )) return FALSE;
return ( $inclParam ) ? $this->url : $this->url['value'];
if( $propName != 'X-PROP' ) {
if( !isset ( $this->xprop[$propName] )) return FALSE;
return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
: array( $propName, $this->xprop[$propName]['value'] );
if( count( $this->xprop ) <= $propix ) return FALSE;
foreach( $this->xprop as $xpropkey => $xpropvalue ) {
if( $propix == $xpropno )
return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
: array( $xpropkey, $this->xprop[$xpropkey]['value'] );
return FALSE; // not found ??
* general component property setting
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.2.13 - 2007-10-25
* @param mixed $args variable number of function arguments,
* first argument is ALWAYS component name,
* second ALWAYS component value!
if( !$this->getConfig( 'allowEmpty' ) && ( !isset ( $arglist[1] ) || empty( $arglist[1] )))
for( $argix= $numargs; $argix < 12; $argix++ ) {
if( !isset ( $arglist[$argix] ))
$this->setAction( $arglist[1], $arglist[2] );
$this->setAttach( $arglist[1], $arglist[2] );
$this->setClass( $arglist[1], $arglist[2] );
$this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
$this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
$this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
$this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
$this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
$this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
$this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
$this->setExdate( $arglist[1], $arglist[2] );
$this->setExrule( $arglist[1], $arglist[2] );
$this->setFreebusy( $arglist[1], $arglist[2], $arglist[3] );
$this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
$this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
$this->setRdate( $arglist[1], $arglist[2] );
$this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
$this->setRepeat( $arglist[1], $arglist[2] );
$this->setRrule( $arglist[1], $arglist[2] );
$this->setStatus( $arglist[1], $arglist[2] );
$this->setTransp( $arglist[1], $arglist[2] );
$this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] );
$this->setTzid( $arglist[1], $arglist[2] );
$this->setTzname( $arglist[1], $arglist[2] );
$this->setTzurl( $arglist[1], $arglist[2] );
$this->setUid( $arglist[1], $arglist[2] );
$this->setUrl( $arglist[1], $arglist[2] );
$this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
/*********************************************************************************/
* parse component unparsed data into properties
* @author Kjell-Inge Gustafsson <ical@kigkonsult.se>
* @since 2.4.2 - 2008-02-07
* @param mixed $unparsedtext, strict rfc2445 formatted, single property string or array of property strings
* @return bool FALSE if error occurs during parsing
function parse( $unparsedtext= null ) {
$this->unparsed = array();
foreach ( $unparsedtext as $line ) {
|