Files
2025-08-14 22:41:49 +02:00

2231 lines
89 KiB
PHP

<?php
/**
* iCalcreator, a PHP rfc2445/rfc5545 solution.
*
* This file is a part of iCalcreator.
*
* Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* Link http://kigkonsult.se/iCalcreator/index.php
* Package iCalcreator
* Version 2.24
* License Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the [rfc5545] PRODID as implemented and
* invoked in iCalcreator shall be included in all copies or
* substantial portions of the iCalcreator.
* iCalcreator can be used either under the terms of
* a proprietary license, available at <https://kigkonsult.se/>
* or the GNU Affero General Public License, version 3:
* iCalcreator is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* iCalcreator 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 Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
* License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
namespace kigkonsult\iCalcreator\util;
/**
* iCalcreator utility/support class
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.21.11 - 2015-04-03
*/
class util {
/**
* @var string iCal component (lowercase) names
* @static
*/
public static $LCVTIMEZONE = 'vtimezone';
public static $LCSTANDARD = 'standard';
public static $LCDAYLIGHT = 'daylight';
public static $LCVEVENT = 'vevent';
public static $LCVTODO = 'vtodo';
public static $LCVJOURNAL = 'vjournal';
public static $LCVFREEBUSY = 'vfreebusy';
public static $LCVALARM = 'valarm';
/**
* @var array iCal component (lowercase) collections
* @static
*/
public static $VCOMPS = ['vevent', 'vtodo', 'vjournal', 'vfreebusy'];
public static $MCOMPS = ['vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone'];
public static $LCSUBCOMPS = ['valarm', 'vtimezone', 'standard', 'daylight'];
public static $TZCOMPS = ['vtimezone', 'standard', 'daylight'];
public static $ALLCOMPS = ['vtimezone', 'standard', 'daylight', 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm'];
/**
* @var string iCal property names
* @static
*/
public static $ACTION = 'ACTION';
public static $ATTACH = 'ATTACH';
public static $ATTENDEE = 'ATTENDEE';
public static $CALSCALE = 'CALSCALE';
public static $CATEGORIES = 'CATEGORIES';
public static $CLASS = 'CLASS';
public static $COMMENT = 'COMMENT';
public static $COMPLETED = 'COMPLETED';
public static $CONTACT = 'CONTACT';
public static $CREATED = 'CREATED';
public static $DESCRIPTION = 'DESCRIPTION';
public static $DTEND = 'DTEND';
public static $DTSTAMP = 'DTSTAMP';
public static $DTSTART = 'DTSTART';
public static $DUE = 'DUE';
public static $DURATION = 'DURATION';
public static $EXDATE = 'EXDATE';
public static $EXRULE = 'EXRULE';
public static $FREEBUSY = 'FREEBUSY';
public static $GEO = 'GEO';
public static $GEOLOCATION = 'GEOLOCATION';
public static $LAST_MODIFIED = 'LAST-MODIFIED';
public static $LOCATION = 'LOCATION';
public static $METHOD = 'METHOD';
public static $ORGANIZER = 'ORGANIZER';
public static $PERCENT_COMPLETE = 'PERCENT-COMPLETE';
public static $PRIORITY = 'PRIORITY';
public static $PRODID = 'PRODID';
public static $RECURRENCE_ID = 'RECURRENCE-ID';
public static $RELATED_TO = 'RELATED-TO';
public static $REPEAT = 'REPEAT';
public static $REQUEST_STATUS = 'REQUEST-STATUS';
public static $RESOURCES = 'RESOURCES';
public static $RDATE = 'RDATE';
public static $RRULE = 'RRULE';
public static $SEQUENCE = 'SEQUENCE';
public static $STATUS = 'STATUS';
public static $SUMMARY = 'SUMMARY';
public static $TRANSP = 'TRANSP';
public static $TRIGGER = 'TRIGGER';
public static $TZID = 'TZID';
public static $TZNAME = 'TZNAME';
public static $TZOFFSETFROM = 'TZOFFSETFROM';
public static $TZOFFSETTO = 'TZOFFSETTO';
public static $TZURL = 'TZURL';
public static $UID = 'UID';
public static $URL = 'URL';
public static $VERSION = 'VERSION';
public static $X_PROP = 'X-PROP';
/**
* @var string vcalendar::selectComponents added x-property names
* @static
*/
public static $X_CURRENT_DTSTART = 'X-CURRENT-DTSTART';
public static $X_CURRENT_DTEND = 'X-CURRENT-DTEND';
public static $X_CURRENT_DUE = 'X-CURRENT-DUE';
public static $X_RECURRENCE = 'X-RECURRENCE';
public static $X_OCCURENCE = 'X-OCCURENCE';
/**
* @var array iCal component property collections
* @static
*/
public static $PROPNAMES = ['ACTION', 'ATTACH', 'ATTENDEE', 'CATEGORIES',
'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT',
'CREATED', 'DESCRIPTION', 'DTEND', 'DTSTAMP',
'DTSTART', 'DUE', 'DURATION', 'EXDATE', 'EXRULE',
'FREEBUSY', 'GEO', 'LAST-MODIFIED', 'LOCATION',
'ORGANIZER', 'PERCENT-COMPLETE', 'PRIORITY',
'RECURRENCE-ID', 'RELATED-TO', 'REPEAT',
'REQUEST-STATUS', 'RESOURCES', 'RRULE', 'RDATE',
'SEQUENCE', 'STATUS', 'SUMMARY', 'TRANSP',
'TRIGGER', 'TZNAME', 'TZID', 'TZOFFSETFROM',
'TZOFFSETTO', 'TZURL', 'UID', 'URL', 'X-'];
public static $DATEPROPS = ['DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED',
'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID'];
public static $OTHERPROPS = ['ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION',
'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES',
'STATUS', 'SUMMARY', 'UID', 'URL'];
public static $MPROPS1 = ['ATTENDEE', 'CATEGORIES', 'CONTACT',
'RELATED-TO', 'RESOURCES'];
public static $MPROPS2 = ['ATTACH', 'ATTENDEE', 'CATEGORIES',
'COMMENT', 'CONTACT', 'DESCRIPTION',
'EXDATE', 'EXRULE', 'FREEBUSY', 'RDATE',
'RELATED-TO', 'RESOURCES', 'RRULE',
'REQUEST-STATUS', 'TZNAME', 'X-PROP'];
/**
* @var string iCalcreator config keys
* @static
*/
public static $ALLOWEMPTY = 'ALLOWEMPTY';
public static $COMPSINFO = 'COMPSINFO';
public static $DELIMITER = 'DELIMITER';
public static $DIRECTORY = 'DIRECTORY';
public static $FILENAME = 'FILENAME';
public static $DIRFILE = 'DIRFILE';
public static $FILESIZE = 'FILESIZE';
public static $FILEINFO = 'FILEINFO';
public static $LANGUAGE = 'LANGUAGE';
public static $PROPINFO = 'PROPINFO';
public static $SETPROPERTYNAMES = 'SETPROPERTYNAMES';
public static $UNIQUE_ID = 'UNIQUE_ID';
/**
* @var string iCal date/time parameter key values
* @static
*/
public static $DATE = 'DATE';
public static $PERIOD = 'PERIOD';
public static $DATE_TIME = 'DATE-TIME';
public static $DEFAULTVALUEDATETIME = ['VALUE' => 'DATE-TIME'];
public static $T = 'T';
public static $Z = 'Z';
public static $UTC = 'UTC';
public static $GMT = 'GMT';
public static $LCYEAR = 'year';
public static $LCMONTH = 'month';
public static $LCDAY = 'day';
public static $LCHOUR = 'hour';
public static $LCMIN = 'min';
public static $LCSEC = 'sec';
public static $LCtz = 'tz';
public static $LCWEEK = 'week';
public static $LCTIMESTAMP = 'timestamp';
/**
* @var string iCal ATTENDEE, ORGANIZER etc param keywords
* @static
*/
public static $CUTYPE = 'CUTYPE';
public static $MEMBER = 'MEMBER';
public static $ROLE = 'ROLE';
public static $PARTSTAT = 'PARTSTAT';
public static $RSVP = 'RSVP';
public static $DELEGATED_TO = 'DELEGATED-TO';
public static $DELEGATED_FROM = 'DELEGATED-FROM';
public static $SENT_BY = 'SENT-BY';
public static $CN = 'CN';
public static $DIR = 'DIR';
public static $INDIVIDUAL = 'INDIVIDUAL';
public static $NEEDS_ACTION = 'NEEDS-ACTION';
public static $REQ_PARTICIPANT = 'REQ-PARTICIPANT';
public static $false = 'false';
/**
* @var array iCal ATTENDEE, ORGANIZER etc param collections
* @static
*/
public static $ATTENDEEPARKEYS = ['DELEGATED-FROM', 'DELEGATED-TO', 'MEMBER'];
public static $ATTENDEEPARALLKEYS = ['CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT',
'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM',
'SENT-BY', 'CN', 'DIR', 'LANGUAGE'];
/**
* @var string iCal RRULE, EXRULE etc param keywords
* @static
*/
public static $FREQ = 'FREQ';
public static $UNTIL = 'UNTIL';
public static $COUNT = 'COUNT';
public static $INTERVAL = 'INTERVAL';
public static $WKST = 'WKST';
public static $BYMONTHDAY = 'BYMONTHDAY';
public static $BYYEARDAY = 'BYYEARDAY';
public static $BYWEEKNO = 'BYWEEKNO';
public static $BYMONTH = 'BYMONTH';
public static $BYSETPOS = 'BYSETPOS';
public static $BYDAY = 'BYDAY';
public static $DAY = 'DAY';
/**
* @var string misc. values
* @static
*/
public static $ALTREP = 'ALTREP';
public static $ALTRPLANGARR = ['ALTREP', 'LANGUAGE'];
public static $VALUE = 'VALUE';
public static $BINARY = 'BINARY';
public static $LCvalue = 'value';
public static $LCparams = 'params';
public static $UNPARSEDTEXT = 'unparsedtext';
public static $SERVER_NAME = 'SERVER_NAME';
public static $LOCALHOST = 'localhost';
public static $EMPTYPROPERTY = '';
public static $FMTBEGIN = "BEGIN:%s\r\n";
public static $FMTEND = "END:%s\r\n";
public static $CRLF = "\r\n";
public static $COMMA = ',';
public static $COLON = ':';
public static $QQ = '"';
public static $SEMIC = ';';
public static $MINUS = '-';
public static $PLUS = '+';
public static $SP1 = ' ';
public static $ZERO = '0';
public static $DOT = '.';
public static $L = '/';
public static $YMDHISE = '%04d-%02d-%02d %02d:%02d:%02d %s';
public static $YMD = '%04d%02d%02d';
public static $HIS = '%02d%02d%02d';
/**
* @var string util date/datetime formats
* @access private
* @static
*/
private static $YMDHIS3 = 'Y-m-d-H-i-s';
/**
* Initiates configuration, set defaults
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-03-11
* @param array $config
* @return array
* @static
*/
public static function initConfig( $config ) {
$config = array_change_key_case( $config, CASE_UPPER );
if( ! isset( $config[self::$ALLOWEMPTY] ))
$config[self::$ALLOWEMPTY] = true;
if( ! isset( $config[self::$DELIMITER] ))
$config[self::$DELIMITER] = DIRECTORY_SEPARATOR;
if( ! isset( $config[self::$DIRECTORY] ))
$config[self::$DIRECTORY] = self::$DOT;
return $config;
}
/**
* Return formatted output for calendar component property
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.20 - 2017-01-30
* @param string $label property name
* @param string $attributes property attributes
* @param string $content property content
* @return string
* @static
*/
public static function createElement( $label, $attributes=null, $content=null ) {
$output = strtoupper( $label );
if( ! empty( $attributes ))
$output .= trim( $attributes );
$output .= util::$COLON . $content;
return self::size75( $output );
}
/**
* Return formatted output for calendar component property parameters
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-01-29
* @param array $params
* @param array $ctrKeys
* @param string $lang
* @return string
* @static
*/
public static function createParams( $params=null, $ctrKeys=null, $lang=null ) {
static $FMTFMTTYPE = ';FMTTYPE=%s%s';
static $FMTKEQV = '%s=%s';
static $ENCODING = 'ENCODING';
static $FMTTYPE = 'FMTTYPE';
static $RANGE = 'RANGE';
static $RELTYPE = 'RELTYPE';
static $PARAMSARRAY = null;
if( is_null( $PARAMSARRAY ))
$PARAMSARRAY = [self::$ALTREP,
self::$CN,
self::$DIR,
$ENCODING,
$FMTTYPE,
self::$LANGUAGE,
$RANGE,
$RELTYPE,
self::$SENT_BY,
self::$TZID,
self::$VALUE];
static $FMTQ = '"%s"';
static $FMTQTD = ';%s=%s%s%s';
static $FMTCMN = ';%s=%s';
if( ! is_array( $params ))
$params = [];
if( ! is_array( $ctrKeys ) || empty( $ctrKeys ))
$ctrKeys = [];
if( empty( $params ) && empty( $ctrKeys ))
return null;
$attrLANG = $attr1 = $attr2 = null;
$hasCNattrKey = ( in_array( self::$CN, $ctrKeys ));
$hasLANGattrKey = ( in_array( self::$LANGUAGE, $ctrKeys ));
$CNattrExist = false;
$xparams = [];
$params = array_change_key_case( $params, CASE_UPPER );
foreach( $params as $paramKey => $paramValue ) {
if(( false !== strpos( $paramValue, self::$COLON )) ||
( false !== strpos( $paramValue, self::$SEMIC )) ||
( false !== strpos( $paramValue, self::$COMMA )))
$paramValue = sprintf( $FMTQ, $paramValue );
if( ctype_digit( (string) $paramKey )) {
$xparams[] = $paramValue;
continue;
}
if( ! in_array( $paramKey, $PARAMSARRAY ))
$xparams[$paramKey] = $paramValue;
else
$params[$paramKey] = $paramValue;
}
ksort( $xparams, SORT_STRING );
foreach( $xparams as $paramKey => $paramValue ) {
$attr2 .= util::$SEMIC;
$attr2 .= ( ctype_digit( (string) $paramKey ))
? $paramValue
: sprintf( $FMTKEQV, $paramKey, $paramValue );
}
if( isset( $params[$FMTTYPE] ) &&
! in_array( $FMTTYPE, $ctrKeys )) {
$attr1 .= sprintf( $FMTFMTTYPE, $params[$FMTTYPE],
$attr2 );
$attr2 = null;
}
if( isset( $params[$ENCODING] ) &&
! in_array( $ENCODING, $ctrKeys )) {
if( !empty( $attr2 )) {
$attr1 .= $attr2;
$attr2 = null;
}
$attr1 .= sprintf( $FMTCMN, $ENCODING,
$params[$ENCODING] );
}
if( isset( $params[self::$VALUE] ) &&
! in_array( self::$VALUE, $ctrKeys ))
$attr1 .= sprintf( $FMTCMN, self::$VALUE,
$params[self::$VALUE] );
if( isset( $params[self::$TZID] ) &&
! in_array( self::$TZID, $ctrKeys )) {
$attr1 .= sprintf( $FMTCMN, self::$TZID,
$params[self::$TZID] );
}
if( isset( $params[$RANGE] ) &&
! in_array( $RANGE, $ctrKeys ))
$attr1 .= sprintf( $FMTCMN, $RANGE,
$params[$RANGE] );
if( isset( $params[$RELTYPE] ) &&
! in_array( $RELTYPE, $ctrKeys ))
$attr1 .= sprintf( $FMTCMN, $RELTYPE,
$params[$RELTYPE] );
if( isset( $params[self::$CN] ) &&
$hasCNattrKey ) {
$attr1 = sprintf( $FMTCMN, self::$CN,
$params[self::$CN] );
$CNattrExist = true;
}
if( isset( $params[self::$DIR] ) &&
in_array( self::$DIR, $ctrKeys )) {
$delim = ( false !== strpos( $params[self::$DIR], self::$QQ ))
? null : self::$QQ;
$attr1 .= sprintf( $FMTQTD, self::$DIR,
$delim,
$params[self::$DIR],
$delim );
}
if( isset( $params[self::$SENT_BY] ) &&
in_array( self::$SENT_BY, $ctrKeys ))
$attr1 .= sprintf( $FMTCMN, self::$SENT_BY,
$params[self::$SENT_BY] );
if( isset( $params[self::$ALTREP] ) &&
in_array( self::$ALTREP, $ctrKeys )) {
$delim = ( false !== strpos( $params[self::$ALTREP], self::$QQ ))
? null : self::$QQ;
$attr1 .= sprintf( $FMTQTD, self::$ALTREP,
$delim,
$params[self::$ALTREP],
$delim );
}
if( isset( $params[self::$LANGUAGE] ) && $hasLANGattrKey )
$attrLANG .= sprintf( $FMTCMN, self::$LANGUAGE,
$params[self::$LANGUAGE] );
elseif(( $CNattrExist || $hasLANGattrKey ) && ! empty( $lang ))
$attrLANG .= sprintf( $FMTCMN, self::$LANGUAGE,
$lang );
return $attr1 . $attrLANG . $attr2;
}
/**
* Return (conformed) iCal component property parameters
*
* Trim quoted values, default parameters may be set, if missing
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-04-08
* @param array $params
* @param array $defaults
* @return array
* @static
*/
public static function setParams( $params, $defaults=null ) {
if( ! is_array( $params ))
$params = [];
$output = [];
$params = array_change_key_case( $params, CASE_UPPER );
foreach( $params as $paramKey => $paramValue ) {
if( is_array( $paramValue )) {
foreach( $paramValue as $pkey => $pValue )
$paramValue[$pkey] = trim( $pValue, util::$QQ );
}
else
$paramValue = trim( $paramValue, util::$QQ );
if( self::$VALUE == $paramKey )
$output[self::$VALUE] = strtoupper( $paramValue );
else
$output[$paramKey] = $paramValue;
} // end foreach
if( is_array( $defaults ))
$output = array_merge( array_change_key_case( $defaults, CASE_UPPER ),
$output );
return ( 0 < count( $output )) ? $output : null;
}
/**
* Remove expected key/value from array and returns hitval (if found) else returns elseval
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.4.16 - 2008-11-08
* @param array $array iCal property parameters
* @param string $expkey expected key
* @param string $expval expected value
* @param int $hitVal return value if found
* @param int $elseVal return value if not found
* @param int $preSet return value if already preset
* @return int
* @static
*/
public static function existRem( & $array,
$expkey,
$expval=false,
$hitVal=null,
$elseVal=null,
$preSet=null ) {
if( $preSet )
return $preSet;
if( ( 0 == count( $array )) || ! is_array( $array ))
return $elseVal;
foreach( $array as $key => $value ) {
if( 0 == strcasecmp( $expkey, $key )) {
if( ! $expval ||
( 0 == strcasecmp( $expval, $value ))) {
unset( $array[$key] );
return $hitVal;
}
}
}
return $elseVal;
}
/**
* Delete component property value, managing components with multiple occurencies
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.8.8 - 2011-03-15
* @param array $multiprop component (multi-)property
* @param int $propix removal counter
* @return bool true
* @static
*/
public static function deletePropertyM( & $multiprop, & $propix ) {
if( isset( $multiprop[$propix] ))
unset( $multiprop[$propix] );
if( empty( $multiprop )) {
$multiprop = null;
unset( $propix );
return false;
}
return true;
}
/**
* Recount property propix, used at consecutive getProperty calls
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-18
* @param array $prop component (multi-)property
* @param int $propix getter counter
* @return bool true
* @static
*/
public static function recountMvalPropix( & $prop, & $propix ) {
if( ! is_array( $prop ) || empty( $prop ))
return false;
$last = key( array_slice( $prop, -1, 1, TRUE ));
while( ! isset( $prop[$propix] ) &&
( $last > $propix ))
$propix++;
return true;
}
/**
* Check index and set (an indexed) content in a multiple value array
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-04-08
* @param array $valArr
* @param mixed $value
* @param array $params
* @param array $defaults
* @param int $index
* @static
*/
public static function setMval( & $valArr,
$value,
$params=null,
$defaults=null,
$index=null ) {
if( ! is_array( $valArr ))
$valArr = [];
if( ! is_null( $params ))
$params = self::setParams( $params, $defaults );
if( is_null( $index )) { // i.e. next
$valArr[] = [self::$LCvalue => $value,
self::$LCparams => $params];
return;
}
$index = $index - 1;
if( isset( $valArr[$index] )) { // replace
$valArr[$index] = [self::$LCvalue => $value,
self::$LCparams => $params];
return;
}
$valArr[$index] = [self::$LCvalue => $value,
self::$LCparams => $params];
ksort( $valArr ); // order
return true;
}
/**
* Return datestamp for calendar component object instance dtstamp
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @return array
* @static
*/
public static function makeDtstamp() {
$date = explode( self::$MINUS, gmdate( self::$YMDHIS3, time()));
return [self::$LCvalue => [self::$LCYEAR => $date[0],
self::$LCMONTH => $date[1],
self::$LCDAY => $date[2],
self::$LCHOUR => $date[3],
self::$LCMIN => $date[4],
self::$LCSEC => $date[5],
self::$LCtz => self::$Z],
self::$LCparams => null];
}
/**
* Return an unique id for a calendar component object instance
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param string $unique_id
* @return array
* @static
*/
public static function makeUid( $unique_id ) {
static $FMT = '%s-%s@%s';
static $TMDTHIS = 'Ymd\THisT';
return [self::$LCvalue => sprintf( $FMT, date( $TMDTHIS ),
substr( microtime(), 2, 4) . self::getRandChars( 6 ),
$unique_id ),
self::$LCparams => null];
}
/**
* Return a random (and unique) sequence of characters
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-18
* @param int $cnt
* @return string
* @access private
* @static
*/
private static function getRandChars( $cnt ) {
$cnt = (int) floor( $cnt / 2 );
$x = 0;
do {
$randChars = bin2hex( openssl_random_pseudo_bytes( $cnt, $cStrong ));
$x += 1;
} while(( 3 > $x ) && ( false == $cStrong ));
return $randChars;
}
/**
* Return true if a date property has NO date parts
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param array $content
* @return bool
* @static
*/
public static function hasNodate( $content ) {
return( ! isset( $content[self::$LCvalue][self::$LCYEAR] ) &&
! isset( $content[self::$LCvalue][self::$LCMONTH] ) &&
! isset( $content[self::$LCvalue][self::$LCDAY] ) &&
! isset( $content[self::$LCvalue][self::$LCHOUR] ) &&
! isset( $content[self::$LCvalue][self::$LCMIN] ) &&
! isset( $content[self::$LCvalue][self::$LCSEC] ));
}
/**
* Return true if property parameter VALUE is set to argument, otherwise false
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-12
* @param array $content
* @param string $arg
* @return bool
* @static
*/
public static function isParamsValueSet( array $content, $arg ) {
return ( isset( $content[self::$LCparams][self::$VALUE] ) &&
( $arg == $content[self::$LCparams][self::$VALUE] ));
}
/**
* Return bool true if name is X-prefixed
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param string $name
* @return bool
* @static
*/
public static function isXprefixed( $name ) {
static $X_ = 'X-';
return ( 0 == strcasecmp( $X_, substr( $name, 0, 2 )));
}
/**
* Return bool true if object class is a DateTime (sub-)class
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.5 - 2017-04-14
* @param object $object
* @return bool
* @static
*/
public static function isDateTimeClass( $object ) {
static $DATETIMEobj = 'DateTime';
return ( is_object( $object ) &&
( 0 == strcasecmp( $DATETIMEobj, substr( get_class( $object ), -8 ))));
}
/**
* Return property name and opt.params and property value
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-16
* @param string $row
* @return string
* @static
*/
public static function getPropName( $row ) {
static $COLONSEMICARR = [':', ';'];
$propName = null;
$cix = 0;
$len = strlen( $row );
while( $cix < $len ) {
if( in_array( $row[$cix], $COLONSEMICARR ))
break;
$propName .= $row[$cix];
$cix++;
} // end while...
if( isset( $row[$cix] ))
$row = substr( $row, $cix);
else {
$propName = self::trimTrailNL( $propName ); // property without colon and content
$row = null;
}
return [$propName, $row];
}
/**
* Return array from content split by '\,'
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-16
* @param string $content
* @return array
* @static
*/
public static function commaSplit( $content ) {
static $DBBS = "\\";
$output = [0 => null];
$cix = $lix = 0;
$len = strlen( $content );
while( $lix < $len ) {
if(( self::$COMMA == $content[$lix] ) &&
( $DBBS != $content[( $lix - 1 )]))
$output[++$cix] = null;
else
$output[$cix] .= $content[$lix];
$lix++;
}
return array_filter( $output );
}
/**
* Return concatenated calendar rows, one row for each property
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param array $rows
* @return array
* @static
*/
public static function concatRows( $rows ) {
$output = [];
$cnt = count( $rows );
for( $i = 0; $i < $cnt; $i++ ) {
$line = rtrim( $rows[$i], self::$CRLF );
while( isset( $rows[$i+1] ) &&
! empty( $rows[$i+1] ) &&
( self::$SP1 == $rows[$i+1]{0} ))
$line .= rtrim( substr( $rows[++$i], 1 ), self::$CRLF );
$output[] = $line;
}
return $output;
}
/**
* Return string with removed ical line folding
*
* Remove any line-endings that may include spaces or tabs
* and convert all line endings (iCal default '\r\n'),
* takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n'
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-03-01
* @param string $text
* @return string
* @static
*/
public static function convEolChar( & $text ) {
static $BASEDELIM = null;
static $BASEDELIMs = null;
static $EMPTYROW = null;
static $FMT = '%1$s%2$75s%1$s';
static $SP0 = '';
static $CRLFs = ["\r\n", "\n\r", "\n", "\r"];
static $CRLFexts = ["\r\n ", "\n\r\t"];
/* fix dummy line separator etc */
if( empty( $BASEDELIM )) {
$BASEDELIM = self::getRandChars( 16 );
$BASEDELIMs = $BASEDELIM . $BASEDELIM;
$EMPTYROW = sprintf( $FMT, $BASEDELIM, $SP0 );
}
/* fix eol chars */
$text = str_replace( $CRLFs, $BASEDELIM, $text );
/* fix empty lines */
$text = str_replace( $BASEDELIMs, $EMPTYROW, $text );
/* fix line folding */
$text = str_replace( $BASEDELIM, util::$CRLF, $text );
$text = str_replace( $CRLFexts, null, $text );
/* split in component/property lines */
return explode( util::$CRLF, $text );
}
/**
* Return wrapped string with (byte oriented) line breaks at pos 75
*
* Lines of text SHOULD NOT be longer than 75 octets, excluding the line
* break. Long content lines SHOULD be split into a multiple line
* representations using a line "folding" technique. That is, a long
* line can be split between any two characters by inserting a CRLF
* immediately followed by a single linear white space character (i.e.,
* SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
* of CRLF followed immediately by a single linear white space character
* is ignored (i.e., removed) when processing the content type.
*
* Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
* the reserved expression "\n" in the arg $string could be broken up by the
* folding of lines, causing ambiguity in the return string.
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-03-01
* @param string $string
* @return string
* @access private
* @static
* @link http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
*/
private static function size75( $string ) {
static $DBS = '\\';
static $LCN = 'n';
static $UCN = 'N';
static $SPBSLCN = ' \n';
static $SP1 = ' ';
$tmp = $string;
$string = null;
$cCnt = $x = 0;
while( true ) {
if( ! isset( $tmp[$x] )) {
$string .= util::$CRLF; // loop breakes here
break;
}
elseif(( 74 <= $cCnt ) &&
( $DBS == $tmp[$x] ) &&
(( $LCN == $tmp[$x+1] ) || ( $UCN == $tmp[$x+1] ))) {
$string .= util::$CRLF . $SPBSLCN; // don't break lines inside '\n'
$x += 2;
if( ! isset( $tmp[$x] )) {
$string .= util::$CRLF;
break;
}
$cCnt = 3;
}
elseif( 75 <= $cCnt ) {
$string .= util::$CRLF . $SP1;
$cCnt = 1;
}
$byte = ord( $tmp[$x] );
$string .= $tmp[$x];
switch( true ) {
case(( $byte >= 0x20 ) && ( $byte <= 0x7F )) :
$cCnt += 1; // characters U-00000000 - U-0000007F (same as ASCII)
break; // add a one byte character
case(( $byte & 0xE0) == 0xC0 ) : // characters U-00000080 - U-000007FF, mask 110XXXXX
if( isset( $tmp[$x+1] )) {
$cCnt += 1;
$string .= $tmp[$x+1];
$x += 1; // add a two bytes character
}
break;
case(( $byte & 0xF0 ) == 0xE0 ) : // characters U-00000800 - U-0000FFFF, mask 1110XXXX
if( isset( $tmp[$x+2] )) {
$cCnt += 1;
$string .= $tmp[$x+1] . $tmp[$x+2];
$x += 2; // add a three bytes character
}
break;
case(( $byte & 0xF8 ) == 0xF0 ) : // characters U-00010000 - U-001FFFFF, mask 11110XXX
if( isset( $tmp[$x+3] )) {
$cCnt += 1;
$string .= $tmp[$x+1] . $tmp[$x+2] . $tmp[$x+3];
$x += 3; // add a four bytes character
}
break;
case(( $byte & 0xFC ) == 0xF8 ) : // characters U-00200000 - U-03FFFFFF, mask 111110XX
if( isset( $tmp[$x+4] )) {
$cCnt += 1;
$string .= $tmp[$x+1] . $tmp[$x+2] . $tmp[$x+3] . $tmp[$x+4];
$x += 4; // add a five bytes character
}
break;
case(( $byte & 0xFE ) == 0xFC ) : // characters U-04000000 - U-7FFFFFFF, mask 1111110X
if( isset( $tmp[$x+5] )) {
$cCnt += 1;
$string .= $tmp[$x+1] . $tmp[$x+2] . $tmp[$x+3] . $tmp[$x+4] . $tmp[$x+5];
$x += 5; // add a six bytes character
}
break;
default: // add any other byte without counting up $cCnt
break;
} // end switch( true )
$x += 1; // next 'byte' to test
} // end while( true )
return $string;
}
/**
* Separate (string) to iCal property value and attributes
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.13 - 2017-05-02
* @param string $line property content
* @param array $propAttr property parameters
* @static
* @TODO same as in util::calAddressCheck() ??
*/
public static function splitContent( & $line, & $propAttr=null ) {
static $CSS = '://';
static $MSTZ = ['utc-', 'utc+', 'gmt-', 'gmt+'];
static $PROTO3 = ['fax:', 'cid:', 'sms:', 'tel:', 'urn:'];
static $PROTO4 = ['crid:', 'news:', 'pres:'];
static $PROTO6 = ['mailto:'];
static $EQ = '=';
$attr = [];
$attrix = -1;
$clen = strlen( $line );
$WithinQuotes = false;
$len = strlen( $line );
$cix = 0;
while( $cix < $len ) {
if( ! $WithinQuotes && ( self::$COLON == $line[$cix] ) &&
( substr( $line,$cix, 3 ) != $CSS ) &&
( ! in_array( strtolower( substr( $line,$cix - 6, 4 )), $MSTZ )) &&
( ! in_array( strtolower( substr( $line,$cix - 3, 4 )), $PROTO3 )) &&
( ! in_array( strtolower( substr( $line,$cix - 4, 5 )), $PROTO4 )) &&
( ! in_array( strtolower( substr( $line,$cix - 6, 7 )), $PROTO6 ))) {
$attrEnd = true;
if(( $cix < ( $clen - 4 )) &&
ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
if( $CSS == substr( $line, $c2ix - 2, 3 )) {
$attrEnd = false;
break; // an URI with a portnr!!
}
}
}
if( $attrEnd) {
$line = substr( $line, ( $cix + 1 ));
break;
}
$cix++;
} // end if( ! $WithinQuotes...
if( self::$QQ == $line[$cix] ) // '"'
$WithinQuotes = ! $WithinQuotes;
if( self::$SEMIC == $line[$cix] ) // ';'
$attr[++$attrix] = null;
else {
if( 0 > $attrix )
$attrix = 0;
$attr[$attrix] .= $line[$cix];
}
$cix++;
} // end while...
/* make attributes in array format */
$propAttr = [];
foreach( $attr as $attribute ) {
$attrsplit = explode( $EQ, $attribute, 2 );
if( 1 < count( $attrsplit ))
$propAttr[$attrsplit[0]] = $attrsplit[1];
}
}
/**
* Special characters management output
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-17
* @param string $string
* @return string
* @static
*/
public static function strrep( $string ) {
static $BSLCN = '\n';
static $SPECCHAR = ['n', 'N', 'r', ',', ';'];
static $DBS = "\\";
static $SQ = "'";
static $BSCOMMA = '\,';
static $BSSEMIC = '\;';
static $BSLCR = "\r";
static $QBSLCN = "\n";
static $BSUCN = '\N';
$string = (string) $string;
$strLen = strlen( $string );
$pos = 0;
while( $pos < $strLen ) {
if( false === ( $pos = strpos( $string, $DBS, $pos )))
break;
if( ! in_array( substr( $string, $pos, 1 ), $SPECCHAR )) {
$string = substr( $string, 0, $pos ) . $DBS . substr( $string, ( $pos + 1 ));
$pos += 1;
}
$pos += 1;
}
if( false !== strpos( $string, self::$QQ ))
$string = str_replace( self::$QQ, $SQ, $string);
if( false !== strpos( $string, self::$COMMA ))
$string = str_replace( self::$COMMA, $BSCOMMA, $string);
if( false !== strpos( $string, self::$SEMIC ))
$string = str_replace( self::$SEMIC, $BSSEMIC, $string);
if( false !== strpos( $string, self::$CRLF ))
$string = str_replace( self::$CRLF, $BSLCN, $string);
elseif( false !== strpos( $string, $BSLCR ))
$string = str_replace( $BSLCR, $BSLCN, $string);
elseif( false !== strpos( $string, $QBSLCN ))
$string = str_replace( $QBSLCN, $BSLCN, $string);
if( false !== strpos( $string, $BSUCN ))
$string = str_replace( $BSUCN, $BSLCN, $string);
$string = str_replace( self::$CRLF, $BSLCN, $string);
return $string;
}
/**
* Special characters management input
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.2 - 2015-06-25
* @param string $string
* @return string
* @static
*/
public static function strunrep( $string ) {
static $BS4 = '\\\\';
static $BS2 = '\\';
static $BSCOMMA = '\,';
static $BSSEMIC = '\;';
$string = str_replace( $BS4, $BS2, $string);
$string = str_replace( $BSCOMMA, self::$COMMA, $string);
$string = str_replace( $BSSEMIC, self::$SEMIC, $string);
return $string;
}
/**
* Return string with trimmed trailing \n
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param string $value
* @return string
* @static
*/
public static function trimTrailNL( $value ) {
static $NL = '\n';
if( $NL == strtolower( substr( $value, -2 )))
$value = substr( $value, 0, ( strlen( $value ) -2 ));
return $value;
}
/**
* Return internal date (format) with parameters based on input date
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.21.11 - 2015-03-21
* @param mixed $year
* @param mixed $month
* @param int $day
* @param int $hour
* @param int $min
* @param int $sec
* @param string $tz
* @param array $params
* @param string $caller
* @param string $objName
* @param string $tzid
* @return array
* @static
*/
public static function setDate( $year,
$month=null,
$day=null,
$hour=null,
$min=null,
$sec=null,
$tz=null,
$params=null,
$caller=null,
$objName=null,
$tzid=null ) {
$input = $parno = null;
$localtime = (( self::$DTSTART == $caller ) &&
in_array( $objName, self::$TZCOMPS )) ? true : false;
self::strDate2arr( $year );
if( self::isArrayDate( $year )) {
$input[self::$LCvalue] = self::chkDateArr( $year );
if( 100 > $input[self::$LCvalue][self::$LCYEAR] )
$input[self::$LCvalue][self::$LCYEAR] += 2000;
if( $localtime )
unset( $month[self::$VALUE], $month[self::$TZID] );
elseif( ! isset( $month[self::$TZID] ) && isset( $tzid ))
$month[self::$TZID] = $tzid;
if( isset( $input[self::$LCvalue][self::$LCtz] ) &&
self::isOffset( $input[self::$LCvalue][self::$LCtz] ))
unset( $month[self::$TZID] );
elseif( ! isset( $input[self::$LCvalue][self::$LCtz] ) &&
isset( $month[self::$TZID] ) &&
self::isOffset( $month[self::$TZID] )) {
$input[self::$LCvalue][self::$LCtz] = $month[self::$TZID];
unset( $month[self::$TZID] );
}
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
$hitval = ( isset( $input[self::$LCvalue][self::$LCtz] )) ? 7 : 6;
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE_TIME,
$hitval );
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE,
3,
count( $input[self::$LCvalue] ),
$parno );
if( 6 > $parno )
unset( $input[self::$LCvalue][self::$LCtz],
$input[self::$LCparams][self::$TZID],
$tzid );
if(( 6 <= $parno ) &&
isset( $input[self::$LCvalue][self::$LCtz] ) &&
( self::$Z != $input[self::$LCvalue][self::$LCtz] ) &&
self::isOffset( $input[self::$LCvalue][self::$LCtz] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCvalue][self::$LCtz] ),
$parno );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT],
$input[self::$LCparams][self::$TZID] );
}
if( isset( $input[self::$LCvalue][self::$LCtz] ) &&
! self::isOffset( $input[self::$LCvalue][self::$LCtz] )) {
$input[self::$LCparams][self::$TZID] = $input[self::$LCvalue][self::$LCtz];
unset( $input[self::$LCvalue][self::$LCtz] );
}
} // end if( self::isArrayDate( $year ))
elseif( self::isArrayTimestampDate( $year )) {
if( $localtime )
unset( $month[self::$LCvalue], $month[self::$TZID] );
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE,
3 );
$hitval = 7;
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE_TIME,
$hitval,
$parno );
if( isset( $year[self::$LCtz] ) && ! empty( $year[self::$LCtz] )) {
if( !self::isOffset( $year[self::$LCtz] )) {
$input[self::$LCparams][self::$TZID] = $year[self::$LCtz];
unset( $year[self::$LCtz], $tzid );
}
else {
if( isset( $input[self::$LCparams][self::$TZID] ) &&
! empty( $input[self::$LCparams][self::$TZID] )) {
if( !self::isOffset( $input[self::$LCparams][self::$TZID] ))
unset( $tzid );
else
unset( $input[self::$LCparams][self::$TZID]);
}
elseif( isset( $tzid ) && ! self::isOffset( $tzid ))
$input[self::$LCparams][self::$TZID] = $tzid;
}
}
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
! empty( $input[self::$LCparams][self::$TZID] )) {
if( self::isOffset( $input[self::$LCparams][self::$TZID] )) {
$year[self::$LCtz] = $input[self::$LCparams][self::$TZID];
unset( $input[self::$LCparams][self::$TZID]);
if( isset( $tzid ) &&
! empty( $tzid ) &&
! self::isOffset( $tzid ))
$input[self::$LCparams][self::$TZID] = $tzid;
}
}
elseif( isset( $tzid ) && ! empty( $tzid )) {
if( self::isOffset( $tzid )) {
$year[self::$LCtz] = $tzid;
unset( $input[self::$LCparams][self::$TZID]);
}
else
$input[self::$LCparams][self::$TZID] = $tzid;
}
$input[self::$LCvalue] = self::timestamp2date( $year, $parno );
} // end elseif( self::isArrayTimestampDate( $year ))
elseif( 8 <= strlen( trim((string) $year ))) { // string ex. "2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone]"
if( $localtime )
unset( $month[self::$LCvalue], $month[self::$TZID] );
elseif( ! isset( $month[self::$TZID] ) && ! empty( $tzid ))
$month[self::$TZID] = $tzid;
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE_TIME,
7,
$parno );
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE,
3,
$parno,
$parno );
$input[self::$LCvalue] = self::strDate2ArrayDate( $year, $parno );
if( 3 == $parno )
unset( $input[self::$LCvalue][self::$LCtz],
$input[self::$LCparams][self::$TZID] );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
if( isset( $input[self::$LCvalue][self::$LCtz] )) {
if( self::isOffset( $input[self::$LCvalue][self::$LCtz] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCvalue][self::$LCtz] ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT],
$input[self::$LCparams][self::$TZID] );
}
else {
$input[self::$LCparams][self::$TZID] = $input[self::$LCvalue][self::$LCtz];
unset( $input[self::$LCvalue][self::$LCtz] );
}
}
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCparams][self::$TZID] ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT],
$input[self::$LCparams][self::$TZID] );
}
} // end elseif( 8 <= strlen( trim((string) $year )))
else { // using all (?) args
if( 100 > $year )
$year += 2000;
if( is_array( $params ))
$input[self::$LCparams] = self::setParams( $params,
self::$DEFAULTVALUEDATETIME );
elseif( is_array( $tz )) {
$input[self::$LCparams] = self::setParams( $tz,
self::$DEFAULTVALUEDATETIME );
$tz = false;
}
elseif( is_array( $hour )) {
$input[self::$LCparams] = self::setParams( $hour,
self::$DEFAULTVALUEDATETIME );
$hour = $min = $sec = $tz = false;
}
if( $localtime )
unset ( $input[self::$LCparams][self::$LCvalue],
$input[self::$LCparams][self::$TZID] );
elseif( ! isset( $tz ) &&
! isset( $input[self::$LCparams][self::$TZID] ) &&
! empty( $tzid ))
$input[self::$LCparams][self::$TZID] = $tzid;
elseif( isset( $tz ) && self::isOffset( $tz ))
unset( $input[self::$LCparams][self::$TZID] );
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] )) {
$tz = $input[self::$LCparams][self::$TZID];
unset( $input[self::$LCparams][self::$TZID] );
}
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE,
3 );
$hitval = ( self::isOffset( $tz )) ? 7 : 6;
$parno = self::existRem( $input[self::$LCparams],
self::$VALUE,
self::$DATE_TIME,
$hitval,
$parno,
$parno );
$input[self::$LCvalue] = [self::$LCYEAR => $year,
self::$LCMONTH => $month,
self::$LCDAY => $day];
if( 3 != $parno ) {
$input[self::$LCvalue][self::$LCHOUR] = ( $hour ) ? $hour : '0';
$input[self::$LCvalue][self::$LCMIN] = ( $min ) ? $min : '0';
$input[self::$LCvalue][self::$LCSEC] = ( $sec ) ? $sec : '0';
if( ! empty( $tz ))
$input[self::$LCvalue][self::$LCtz] = $tz;
$strdate = self::date2strdate( $input[self::$LCvalue], $parno );
if( ! empty( $tz ) && !self::isOffset( $tz ))
$strdate .= ( self::$Z == $tz ) ? $tz : ' '.$tz;
$input[self::$LCvalue] = self::strDate2ArrayDate( $strdate, $parno );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
if( isset( $input[self::$LCvalue][self::$LCtz] )) {
if( self::isOffset( $input[self::$LCvalue][self::$LCtz] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCvalue][self::$LCtz] ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT],
$input[self::$LCparams][self::$TZID] );
}
else {
$input[self::$LCparams][self::$TZID] = $input[self::$LCvalue][self::$LCtz];
unset( $input[self::$LCvalue][self::$LCtz] );
}
}
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCparams][self::$TZID] ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT],
$input[self::$LCparams][self::$TZID] );
}
}
} // end else (i.e. using all arguments)
if(( 3 == $parno ) || self::isParamsValueSet( $input, self::$DATE )) {
$input[self::$LCparams][self::$VALUE] = self::$DATE;
unset( $input[self::$LCvalue][self::$LCHOUR],
$input[self::$LCvalue][self::$LCMIN],
$input[self::$LCvalue][self::$LCSEC],
$input[self::$LCvalue][self::$LCtz],
$input[self::$LCparams][self::$TZID] );
}
elseif( isset( $input[self::$LCparams][self::$TZID] )) {
if(( 0 == strcasecmp( self::$UTC, $input[self::$LCparams][self::$TZID] )) ||
( 0 == strcasecmp( self::$GMT, $input[self::$LCparams][self::$TZID] ))) {
$input[self::$LCvalue][self::$LCtz] = self::$Z;
unset( $input[self::$LCparams][self::$TZID] );
}
else
unset( $input[self::$LCvalue][self::$LCtz] );
}
elseif( isset( $input[self::$LCvalue][self::$LCtz] )) {
if(( 0 == strcasecmp( self::$UTC, $input[self::$LCvalue][self::$LCtz] )) ||
( 0 == strcasecmp( self::$GMT, $input[self::$LCvalue][self::$LCtz] )))
$input[self::$LCvalue][self::$LCtz] = self::$Z;
if( self::$Z != $input[self::$LCvalue][self::$LCtz] ) {
$input[self::$LCparams][self::$TZID] = $input[self::$LCvalue][self::$LCtz];
unset( $input[self::$LCvalue][self::$LCtz] );
}
else
unset( $input[self::$LCparams][self::$TZID] );
}
if( $localtime )
unset( $input[self::$LCvalue][self::$LCtz], $input[self::$LCparams][self::$TZID] );
return $input;
}
/**
* Return input (UTC) date to internal date with parameters
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-17
* @param mixed $year
* @param mixed $month
* @param int $day
* @param int $hour
* @param int $min
* @param int $sec
* @param array $params
* @return array
* @static
*/
public static function setDate2( $year,
$month=null,
$day=null,
$hour=null,
$min=null,
$sec=null,
$params=null ) {
$input = null;
self::strDate2arr( $year );
if( self::isArrayDate( $year )) {
$input[self::$LCvalue] = self::chkDateArr( $year, 7 );
if( isset( $input[self::$LCvalue][self::$LCYEAR] ) &&
( 100 > $input[self::$LCvalue][self::$LCYEAR] ))
$input[self::$LCvalue][self::$LCYEAR] += 2000;
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
if( isset( $input[self::$LCvalue][self::$LCtz] ) &&
self::isOffset( $input[self::$LCvalue][self::$LCtz] ))
$tzid = $input[self::$LCvalue][self::$LCtz];
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] ))
$tzid = $input[self::$LCparams][self::$TZID];
else
$tzid = null;
if( ! empty( $tzid ) && ( self::$Z != $tzid ) && self::isOffset( $tzid )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$tzid ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
}
} // end if( self::isArrayDate( $year ))
elseif( self::isArrayTimestampDate( $year )) {
if( isset( $year[self::$LCtz] ) &&
! self::isOffset( $year[self::$LCtz] ))
$year[self::$LCtz] = self::$UTC;
elseif( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] ))
$year[self::$LCtz] = $input[self::$LCparams][self::$TZID];
else
$year[self::$LCtz] = self::$UTC;
$input[self::$LCvalue] = self::timestamp2date( $year, 7 );
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
} // end elseif( self::isArrayTimestampDate( $year ))
elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18
$input[self::$LCvalue] = self::strDate2ArrayDate( $year, 7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
$input[self::$LCparams] = self::setParams( $month,
self::$DEFAULTVALUEDATETIME );
if(( ! isset( $input[self::$LCvalue][self::$LCtz] ) ||
empty( $input[self::$LCvalue][self::$LCtz] )) &&
isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] )) {
$input[self::$LCvalue] = self::strDate2ArrayDate( sprintf( self::$YMDHISE,
(int) $input[self::$LCvalue][self::$LCYEAR],
(int) $input[self::$LCvalue][self::$LCMONTH],
(int) $input[self::$LCvalue][self::$LCDAY],
(int) $input[self::$LCvalue][self::$LCHOUR],
(int) $input[self::$LCvalue][self::$LCMIN],
(int) $input[self::$LCvalue][self::$LCSEC],
$input[self::$LCparams][self::$TZID] ),
7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
}
} // end elseif( 8 <= strlen( trim((string) $year )))
else {
if( 100 > $year )
$year += 2000;
$input[self::$LCvalue] = [self::$LCYEAR => $year,
self::$LCMONTH => $month,
self::$LCDAY => $day,
self::$LCHOUR => $hour,
self::$LCMIN => $min,
self::$LCSEC => $sec];
if( isset( $tz ))
$input[self::$LCvalue][self::$LCtz] = $tz;
if(( isset( $tz ) && self::isOffset( $tz )) ||
( isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] ))) {
if( ! isset( $tz ) &&
isset( $input[self::$LCparams][self::$TZID] ) &&
self::isOffset( $input[self::$LCparams][self::$TZID] ))
$input[self::$LCvalue][self::$LCtz] = $input[self::$LCparams][self::$TZID];
unset( $input[self::$LCparams][self::$TZID] );
$strdate = self::date2strdate( $input[self::$LCvalue], 7 );
$input[self::$LCvalue] = self::strDate2ArrayDate( $strdate, 7 );
unset( $input[self::$LCvalue][self::$UNPARSEDTEXT] );
}
$input[self::$LCparams] = self::setParams( $params,
self::$DEFAULTVALUEDATETIME );
} // end else
unset( $input[self::$LCparams][self::$VALUE], $input[self::$LCparams][self::$TZID] );
if( ! isset( $input[self::$LCvalue][self::$LCHOUR] ))
$input[self::$LCvalue][self::$LCHOUR] = 0;
if( ! isset( $input[self::$LCvalue][self::$LCMIN] ))
$input[self::$LCvalue][self::$LCMIN] = 0;
if( ! isset( $input[self::$LCvalue][self::$LCSEC] ))
$input[self::$LCvalue][self::$LCSEC] = 0;
$input[self::$LCvalue][self::$LCtz] = self::$Z;
return $input;
}
/**
* Return array (in internal format) for an input date-time/date array (keyed or unkeyed)
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-19
* @param array $datetime
* @param int $parno default null, 3: DATE(Ymd), 6: YmdHis, 7: YmdHis + offset/timezone
* @return array
* @static
*/
public static function chkDateArr( $datetime, $parno=null ) {
static $PLUS4ZERO = '+0000';
static $MINUS4ZERO = '-0000';
static $PLUS6ZERO = '+000000';
static $MINUS6ZERO = '-000000';
$output = [];
if(( is_null( $parno ) || ( 6 <= $parno )) &&
isset( $datetime[3] ) &&
! isset( $datetime[4] )) { // Y-m-d with tz
$temp = $datetime[3];
$datetime[3] = $datetime[4] = $datetime[5] = 0;
$datetime[6] = $temp;
}
foreach( $datetime as $dateKey => $datePart ) {
switch ( $dateKey ) {
case '0':
case self::$LCYEAR :
$output[self::$LCYEAR] = $datePart;
break;
case '1':
case self::$LCMONTH :
$output[self::$LCMONTH] = $datePart;
break;
case '2':
case self::$LCDAY :
$output[self::$LCDAY] = $datePart;
break;
}
if( 3 != $parno ) {
switch ( $dateKey ) {
case '0':
case '1':
case '2':
break;
case '3':
case self::$LCHOUR:
$output[self::$LCHOUR] = $datePart;
break;
case '4':
case self::$LCMIN :
$output[self::$LCMIN] = $datePart;
break;
case '5':
case self::$LCSEC :
$output[self::$LCSEC] = $datePart;
break;
case '6':
case self::$LCtz :
$output[self::$LCtz] = $datePart;
break;
}
}
}
if( 3 != $parno ) {
if( ! isset( $output[self::$LCHOUR] ))
$output[self::$LCHOUR] = 0;
if( ! isset( $output[self::$LCMIN] ))
$output[self::$LCMIN] = 0;
if( ! isset( $output[self::$LCSEC] ))
$output[self::$LCSEC] = 0;
if( isset( $output[self::$LCtz] ) &&
(( $PLUS4ZERO == $output[self::$LCtz] ) ||
( $MINUS4ZERO == $output[self::$LCtz] ) ||
( $PLUS6ZERO == $output[self::$LCtz] ) ||
( $MINUS6ZERO == $output[self::$LCtz] )))
$output[self::$LCtz] = self::$Z;
}
return $output;
}
/**
* Return iCal formatted string for (internal array) date/date-time
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-24
* @param array $datetime
* @param int $parno default 6
* @return string
* @static
*/
public static function date2strdate( $datetime, $parno=null ) {
static $SECONDS = ' seconds';
static $YMDYHIS = 'Ymd\THis';
if( ! isset( $datetime[self::$LCYEAR] ) &&
! isset( $datetime[self::$LCMONTH] ) &&
! isset( $datetime[self::$LCDAY] ) &&
! isset( $datetime[self::$LCHOUR] ) &&
! isset( $datetime[self::$LCMIN] ) &&
! isset( $datetime[self::$LCSEC] ))
return null;
if( is_null( $parno ))
$parno = 6;
$output = null;
foreach( $datetime as $dkey => & $dvalue ) {
if( self::$LCtz != $dkey )
$dvalue = (int) $dvalue;
}
$output = sprintf( self::$YMD, $datetime[self::$LCYEAR],
$datetime[self::$LCMONTH],
$datetime[self::$LCDAY] );
if( 3 == $parno )
return $output;
if( ! isset( $datetime[self::$LCHOUR] ))
$datetime[self::$LCHOUR] = 0;
if( ! isset( $datetime[self::$LCMIN] ))
$datetime[self::$LCMIN] = 0;
if( ! isset( $datetime[self::$LCSEC] ))
$datetime[self::$LCSEC] = 0;
$output .= self::$T . sprintf( self::$HIS, $datetime[self::$LCHOUR],
$datetime[self::$LCMIN],
$datetime[self::$LCSEC] );
if( isset( $datetime[self::$LCtz] )) {
$datetime[self::$LCtz] = trim( $datetime[self::$LCtz] );
if( ! empty( $datetime[self::$LCtz] )) {
if( self::$Z == $datetime[self::$LCtz] )
$parno = 7;
elseif( self::isOffset( $datetime[self::$LCtz] )) {
$parno = 7;
$offset = self::tz2offset( $datetime[self::$LCtz] );
try {
$timezone = new \DateTimeZone( self::$UTC );
$d = new \DateTime( $output, $timezone );
if( 0 != $offset ) // adjust för offset
$d->modify( $offset . $SECONDS );
$output = $d->format( $YMDYHIS );
}
catch( \Exception $e ) {
$output = date( $YMDYHIS, mktime( $datetime[self::$LCHOUR],
$datetime[self::$LCMIN],
( $datetime[self::$LCSEC] - $offset ),
$datetime[self::$LCMONTH],
$datetime[self::$LCDAY],
$datetime[self::$LCYEAR] ));
}
}
if( 7 == $parno )
$output .= self::$Z;
} // end if( ! empty( $datetime[self::$LCtz] ))
}
return $output;
}
/**
* Return array (in internal format) for a (array) duration
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.19.4 - 2014-03-14
* @param array $duration
* @return array
* @static
*/
public static function duration2arr( $duration ) {
$seconds = 0;
foreach( $duration as $durKey => $durValue ) {
if( empty( $durValue )) continue;
switch ( $durKey ) {
case '0': case self::$LCWEEK:
$seconds += (((int) $durValue ) * 60 * 60 * 24 * 7 );
break;
case '1': case self::$LCDAY:
$seconds += (((int) $durValue ) * 60 * 60 * 24 );
break;
case '2': case self::$LCHOUR:
$seconds += (((int) $durValue ) * 60 * 60 );
break;
case '3': case self::$LCMIN:
$seconds += (((int) $durValue ) * 60 );
break;
case '4': case self::$LCSEC:
$seconds += (int) $durValue;
break;
}
}
$output = [];
$output[self::$LCWEEK] = (int) floor( $seconds / ( 60 * 60 * 24 * 7 ));
if(( 0 < $output[self::$LCWEEK] ) &&
( 0 == ( $seconds % ( 60 * 60 * 24 * 7 ))))
return $output;
unset( $output[self::$LCWEEK] );
$output[self::$LCDAY] = (int) floor( $seconds / ( 60 * 60 * 24 ));
$seconds = ( $seconds % ( 60 * 60 * 24 ));
$output[self::$LCHOUR] = (int) floor( $seconds / ( 60 * 60 ));
$seconds = ( $seconds % ( 60 * 60 ));
$output[self::$LCMIN] = (int) floor( $seconds / 60 );
$output[self::$LCSEC] = ( $seconds % 60 );
if( empty( $output[self::$LCDAY] ))
unset( $output[self::$LCDAY] );
if(( 0 == $output[self::$LCHOUR] ) &&
( 0 == $output[self::$LCMIN] ) &&
( 0 == $output[self::$LCSEC] ))
unset( $output[self::$LCHOUR],
$output[self::$LCMIN],
$output[self::$LCSEC] );
return $output;
}
/**
* Return datetime array (in internal format) for startdate + duration
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.21.11 - 2015-03-21
* @param array $startdate
* @param array $duration
* @return array, date format
* @static
*/
public static function duration2date( $startdate, $duration ) {
$dateOnly = ( isset( $startdate[self::$LCHOUR] ) ||
isset( $startdate[self::$LCMIN] ) ||
isset( $startdate[self::$LCSEC] )) ? false : true;
$startdate[self::$LCHOUR] = ( isset( $startdate[self::$LCHOUR] ))
? $startdate[self::$LCHOUR] : 0;
$startdate[self::$LCMIN] = ( isset( $startdate[self::$LCMIN] ))
? $startdate[self::$LCMIN] : 0;
$startdate[self::$LCSEC] = ( isset( $startdate[self::$LCSEC] ))
? $startdate[self::$LCSEC] : 0;
$dtend = 0;
if( isset( $duration[self::$LCWEEK] ))
$dtend += ( $duration[self::$LCWEEK] * 7 * 24 * 60 * 60 );
if( isset( $duration[self::$LCDAY] ))
$dtend += ( $duration[self::$LCDAY] * 24 * 60 * 60 );
if( isset( $duration[self::$LCHOUR] ))
$dtend += ( $duration[self::$LCHOUR] * 60 *60 );
if( isset( $duration[self::$LCMIN] ))
$dtend += ( $duration[self::$LCMIN] * 60 );
if( isset( $duration[self::$LCSEC] ))
$dtend += $duration[self::$LCSEC];
$date = date( self::$YMDHIS3,
mktime((int) $startdate[self::$LCHOUR],
(int) $startdate[self::$LCMIN],
(int) ( $startdate[self::$LCSEC] + $dtend ),
(int) $startdate[self::$LCMONTH],
(int) $startdate[self::$LCDAY],
(int) $startdate[self::$LCYEAR] ));
$d = explode( self::$MINUS, $date );
$dtend2 = [self::$LCYEAR => $d[0],
self::$LCMONTH => $d[1],
self::$LCDAY => $d[2],
self::$LCHOUR => $d[3],
self::$LCMIN => $d[4],
self::$LCSEC => $d[5]];
if( isset( $startdate[self::$LCtz] ))
$dtend2[self::$LCtz] = $startdate[self::$LCtz];
if( $dateOnly &&
(( 0 == $dtend2[self::$LCHOUR] ) &&
( 0 == $dtend2[self::$LCMIN] ) &&
( 0 == $dtend2[self::$LCSEC] )))
unset( $dtend2[self::$LCHOUR],
$dtend2[self::$LCMIN],
$dtend2[self::$LCSEC] );
return $dtend2;
}
/**
* Return an iCal formatted string from (internal array) duration
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.15.8 - 2012-10-30
* @param array $duration, array( week, day, hour, min, sec )
* @return string
* @static
*/
public static function duration2str( array $duration ) {
static $P = 'P';
static $W = 'W';
static $D = 'D';
static $H = 'H';
static $OH = '0H';
static $M = 'M';
static $OM = '0M';
static $S = 'S';
static $OS = '0S';
static $PT0H0M0S = 'PT0H0M0S';
if( isset( $duration[self::$LCWEEK] ) ||
isset( $duration[self::$LCDAY] ) ||
isset( $duration[self::$LCHOUR] ) ||
isset( $duration[self::$LCMIN] ) ||
isset( $duration[self::$LCSEC] ))
$ok = true;
else
return null;
if( isset( $duration[self::$LCWEEK] ) &&
( 0 < $duration[self::$LCWEEK] ))
return $P . $duration[self::$LCWEEK] . $W;
$output = $P;
if( isset($duration[self::$LCDAY] ) &&
( 0 < $duration[self::$LCDAY] ))
$output .= $duration[self::$LCDAY] . $D;
if(( isset( $duration[self::$LCHOUR]) &&
( 0 < $duration[self::$LCHOUR] )) ||
( isset( $duration[self::$LCMIN]) &&
( 0 < $duration[self::$LCMIN] )) ||
( isset( $duration[self::$LCSEC]) &&
( 0 < $duration[self::$LCSEC] ))) {
$output .= self::$T;
$output .= ( isset( $duration[self::$LCHOUR]) &&
( 0 < $duration[self::$LCHOUR] ))
? $duration[self::$LCHOUR] . $H : $OH;
$output .= ( isset( $duration[self::$LCMIN]) &&
( 0 < $duration[self::$LCMIN] ))
? $duration[self::$LCMIN] . $M : $OM;
$output .= ( isset( $duration[self::$LCSEC]) &&
( 0 < $duration[self::$LCSEC] ))
? $duration[self::$LCSEC] . $S : $OS;
}
if( $P == $output )
$output = $PT0H0M0S;
return $output;
}
/**
* Return array (in internal format) from string duration
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-17
* @param array $duration
* @return array|bool false on error
* @static
*/
public static function durationStr2arr( $duration ) {
static $P = 'P';
static $Tt = ['t', 'T'];
static $W = 'W';
static $D = 'D';
static $H = 'H';
static $M = 'M';
static $S = 'S';
$duration = (string) trim( $duration );
while( 0 != strcasecmp( $P, $duration[0] )) {
if( 0 < strlen( $duration ))
$duration = substr( $duration, 1 );
else
return false; // no leading P !?!?
}
$duration = substr( $duration, 1 ); // skip P
$duration = str_replace( $Tt, null, $duration );
$output = [];
$val = null;
$durLen = strlen( $duration );
for( $ix=0; $ix < $durLen; $ix++ ) {
switch( strtoupper( $duration[$ix] )) {
case $W :
$output[self::$LCWEEK] = $val;
$val = null;
break;
case $D :
$output[self::$LCDAY] = $val;
$val = null;
break;
case $H :
$output[self::$LCHOUR] = $val;
$val = null;
break;
case $M :
$output[self::$LCMIN] = $val;
$val = null;
break;
case $S :
$output[self::$LCSEC] = $val;
$val = null;
break;
default:
if( ! ctype_digit( $duration[$ix] ))
return false; // unknown duration control character !?!?
else
$val .= $duration[$ix];
}
}
return self::duration2arr( $output );
}
/**
* Return bool true if input contains a date/time (in array format)
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.16.24 - 2013-07-02
* @param array $input
* @return bool
* @static
*/
public static function isArrayDate( $input ) {
if( ! is_array( $input ) ||
isset( $input[self::$LCWEEK] ) ||
isset( $input[self::$LCTIMESTAMP] ) ||
( 3 > count( $input )))
return false;
if( 7 == count( $input ))
return true;
if( isset( $input[self::$LCYEAR] ) &&
isset( $input[self::$LCMONTH] ) &&
isset( $input[self::$LCDAY] ))
return checkdate( (int) $input[self::$LCMONTH],
(int) $input[self::$LCDAY],
(int) $input[self::$LCYEAR] );
if( isset( $input[self::$LCDAY] ) ||
isset( $input[self::$LCHOUR] ) ||
isset( $input[self::$LCMIN] ) ||
isset( $input[self::$LCSEC] ))
return false;
if(( 0 == $input[0] ) ||
( 0 == $input[1] ) ||
( 0 == $input[2] ))
return false;
if(( 1970 > $input[0] ) ||
( 12 < $input[1] ) ||
( 31 < $input[2] ))
return false;
if(( isset( $input[0] ) &&
isset( $input[1] ) &&
isset( $input[2] )) &&
checkdate((int) $input[1],
(int) $input[2],
(int) $input[0] ))
return true;
$input = self::strDate2ArrayDate( $input[1] .
self::$L .
$input[2] .
self::$L .
$input[0], 3 ); // m - d - Y
if( isset( $input[self::$LCYEAR] ) &&
isset( $input[self::$LCMONTH] ) &&
isset( $input[self::$LCDAY] ))
return checkdate( (int) $input[self::$LCMONTH],
(int) $input[self::$LCDAY],
(int) $input[self::$LCYEAR] );
return false;
}
/**
* Return bool true if input array contains a (keyed) timestamp date
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.4.16 - 2008-10-18
* @param array $input
* @return bool
* @static
*/
public static function isArrayTimestampDate( $input ) {
return ( is_array( $input ) && isset( $input[self::$LCTIMESTAMP] ));
}
/**
* Return bool true if input string contains (trailing) UTC/iCal offset
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.14.1 - 2012-09-21
* @param string $input
* @return bool
* @static
*/
public static function isOffset( $input ) {
static $PLUSMINUSARR = ['+', '-'];
static $ZERO4 = '0000';
static $NINE4 = '9999';
static $ZERO6 = '000000';
static $NINE6 = '999999';
$input = trim( (string) $input );
if( self::$Z == substr( $input, -1 ))
return true;
elseif(( 5 <= strlen( $input )) &&
( in_array( substr( $input, -5, 1 ), $PLUSMINUSARR )) &&
( $ZERO4 <= substr( $input, -4 )) && ( $NINE4 >= substr( $input, -4 )))
return true;
elseif(( 7 <= strlen( $input )) &&
( in_array( substr( $input, -7, 1 ), $PLUSMINUSARR )) &&
( $ZERO6 <= substr( $input, -6 )) && ( $NINE6 >= substr( $input, -6 )))
return true;
return false;
}
/**
* Convert a date from string to (internal, keyed) array format, return true on success
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.11.8 - 2012-01-27
* @param mixed $date
* @return bool, true on success
* @static
*/
public static function strDate2arr( & $date ) {
static $ET = [' ', 't', 'T'];
if( is_array( $date ))
return false;
if( 5 > strlen( (string) $date ))
return false;
$work = $date;
if( 2 == substr_count( $work, self::$MINUS ))
$work = str_replace( self::$MINUS, null, $work );
if( 2 == substr_count( $work, self::$L ))
$work = str_replace( self::$L, null, $work );
if( ! ctype_digit( substr( $work, 0, 8 )))
return false;
$temp = [self::$LCYEAR => (int) substr( $work, 0, 4 ),
self::$LCMONTH => (int) substr( $work, 4, 2 ),
self::$LCDAY => (int) substr( $work, 6, 2 )];
if( ! checkdate( $temp[self::$LCMONTH],
$temp[self::$LCDAY],
$temp[self::$LCYEAR] ))
return false;
if( 8 == strlen( $work )) {
$date = $temp;
return true;
}
if( in_array( $work[8], $ET ))
$work = substr( $work, 9 );
elseif( ctype_digit( $work[8] ))
$work = substr( $work, 8 );
else
return false;
if( 2 == substr_count( $work, self::$COLON ))
$work = str_replace( self::$COLON, null, $work );
if( ! ctype_digit( substr( $work, 0, 4 )))
return false;
$temp[self::$LCHOUR] = substr( $work, 0, 2 );
$temp[self::$LCMIN] = substr( $work, 2, 2 );
if((( 0 > $temp[self::$LCHOUR] ) || ( $temp[self::$LCHOUR] > 23 )) ||
(( 0 > $temp[self::$LCMIN] ) || ( $temp[self::$LCMIN] > 59 )))
return false;
if( ctype_digit( substr( $work, 4, 2 ))) {
$temp[self::$LCSEC] = substr( $work, 4, 2 );
if(( 0 > $temp[self::$LCSEC] ) || ( $temp[self::$LCSEC] > 59 ))
return false;
$len = 6;
}
else {
$temp[self::$LCSEC] = 0;
$len = 4;
}
if( $len < strlen( $work))
$temp[self::$LCtz] = trim( substr( $work, 6 ));
$date = $temp;
return true;
}
/**
* Return string date-time/date as array (in internal format, keyed)
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.22.23 - 2017-02-19
* Modified to also return original string value by Yitzchok Lavi <icalcreator@onebigsystem.com>
* @param string $datetime
* @param int $parno default false
* @param mixed $wtz default null
* @return array
* @static
*/
public static function strDate2ArrayDate( $datetime,
$parno=null,
$wtz=null ) {
static $SECONDS = ' seconds';
$unparseddatetime = $datetime;
$datetime = (string) trim( $datetime );
$tz = null;
$offset = 0;
$tzSts = false;
$len = strlen( $datetime );
if( self::$Z == substr( $datetime, -1 )) {
$tz = self::$Z;
$datetime = trim( substr( $datetime, 0, ( $len - 1 )));
$tzSts = true;
}
if( self::isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset
$tz = substr( $datetime, -5, 5 );
$datetime = trim( substr( $datetime, 0, ($len - 5)));
}
elseif( self::isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset
$tz = substr( $datetime, -7, 7 );
$datetime = trim( substr( $datetime, 0, ($len - 7)));
}
elseif( empty( $wtz ) &&
ctype_digit( substr( $datetime, 0, 4 )) &&
ctype_digit( substr( $datetime, -2, 2 )) &&
self::strDate2arr( $datetime )) {
$output = $datetime;
if( ! empty( $tz ))
$output[self::$LCtz] = self::$Z;
$output[self::$UNPARSEDTEXT] = $unparseddatetime;
return $output;
}
else {
$tx = 0; // find any TRAILING timezone or offset
$len = strlen( $datetime );
for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
$char = substr( $datetime, $cx, 1 );
if(( self::$SP1 == $char ) || ctype_digit( $char ))
break; // if exists, tz ends here.. . ?
else
$tx--; // tz length counter
}
if( 0 > $tx ) { // if any timezone or offset found
$tz = substr( $datetime, $tx );
$datetime = trim( substr( $datetime, 0, $len + $tx ));
}
if(( ctype_digit( substr( $datetime, 0, 8 )) &&
( self::$T == $datetime[8] ) &&
ctype_digit( substr( $datetime, -6, 6 ))) ||
( ctype_digit( substr( $datetime, 0, 14 ))))
$tzSts = true;
}
if( empty( $tz ) && ! empty( $wtz ))
$tz = $wtz;
if( 3 == $parno )
$tz = null;
if( ! empty( $tz )) { // tz set
if(( self::$Z != $tz ) && ( self::isOffset( $tz ))) {
$offset = (string) self::tz2offset( $tz ) * -1;
$tz = self::$UTC;
$tzSts = true;
}
elseif( ! empty( $wtz ))
$tzSts = true;
$tz = trim( $tz );
if(( 0 == strcasecmp( self::$Z, $tz )) ||
( 0 == strcasecmp( self::$GMT, $tz )))
$tz = self::$UTC;
if( 0 < substr_count( $datetime, self::$MINUS ))
$datetime = str_replace( self::$MINUS, self::$L, $datetime );
try {
$timezone = new \DateTimeZone( $tz );
$d = new \DateTime( $datetime, $timezone );
if( 0 != $offset ) // adjust for offset
$d->modify( $offset . $SECONDS );
$datestring = $d->format( self::$YMDHIS3 );
unset( $d );
}
catch( \Exception $e ) {
$datestring = date( self::$YMDHIS3, strtotime( $datetime ));
}
} // end if( ! empty( $tz ))
else
$datestring = date( self::$YMDHIS3, strtotime( $datetime ));
if( self::$UTC == $tz )
$tz = self::$Z;
$d = explode( self::$MINUS, $datestring );
$output = [self::$LCYEAR => $d[0],
self::$LCMONTH => $d[1],
self::$LCDAY => $d[2]];
if( ! empty( $parno ) || ( 3 != $parno )) { // parno is set to 6 or 7
$output[self::$LCHOUR] = $d[3];
$output[self::$LCMIN] = $d[4];
$output[self::$LCSEC] = $d[5];
if(( $tzSts || ( 7 == $parno )) && ! empty( $tz ))
$output[self::$LCtz] = $tz;
}
// return original string in the array in case strtotime failed to make sense of it
$output[self::$UNPARSEDTEXT] = $unparseddatetime;
return $output;
}
/**
* Return string/array timestamp(+ offset/timezone (default UTC)) as array (in internal format, keyed).
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.21.11 - 2015-03-07
* @param mixed $timestamp
* @param int $parno
* @param string $wtz
* @return array
* @static
*/
public static function timestamp2date( $timestamp, $parno=6, $wtz=null ) {
static $FMTTIMESTAMP = '@%s';
static $SPSEC = ' seconds';
if( is_array( $timestamp )) {
$tz = ( isset( $timestamp[self::$LCtz] ))
? $timestamp[self::$LCtz] : $wtz;
$timestamp = $timestamp[self::$LCTIMESTAMP];
}
$tz = ( isset( $tz )) ? $tz : $wtz;
$offset = 0;
if( empty( $tz ) ||
( self::$Z == $tz ) ||
( 0 == strcasecmp( self::$GMT, $tz )))
$tz = self::$UTC;
elseif( self::isOffset( $tz )) {
$offset = self::tz2offset( $tz );
}
try {
$timestamp = sprintf( $FMTTIMESTAMP, $timestamp );
$d = new \DateTime( $timestamp ); // set UTC date
if( 0 != $offset ) // adjust for offset
$d->modify( $offset . $SPSEC );
elseif( self::$UTC != $tz )
$d->setTimezone( new \DateTimeZone( $tz )); // convert to local date
$date = $d->format( self::$YMDHIS3 );
}
catch( \Exception $e ) {
$date = date( self::$YMDHIS3, $timestamp );
}
$date = explode( self::$MINUS, $date );
$output = [self::$LCYEAR => $date[0],
self::$LCMONTH => $date[1],
self::$LCDAY => $date[2]];
if( 3 != $parno ) {
$output[self::$LCHOUR] = $date[3];
$output[self::$LCMIN] = $date[4];
$output[self::$LCSEC] = $date[5];
if(( self::$UTC == $tz ) || ( 0 == $offset ))
$output[self::$LCtz] = self::$Z;
}
return $output;
}
/**
* Return seconds based on an offset, [+/-]HHmm[ss], used when correcting UTC to localtime or v.v.
*
* @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
* @since 2.23.8 - 2017-04-17
* @param string $tz
* @return integer
* @static
*/
public static function tz2offset( $tz ) {
static $ZERO4 = '0000';
static $NINE4 = '9999';
static $ZERO2 = '00';
static $NINE2 = '99';
$tz = trim( (string) $tz );
$offset = 0;
if((( 5 != strlen( $tz )) &&
( 7 != strlen( $tz ))) ||
(( self::$PLUS != $tz[0] &&
( self::$MINUS != $tz[0] ))) ||
(( $ZERO4 >= substr( $tz, 1, 4 )) &&
( $NINE4 < substr( $tz, 1, 4 ))) ||
(( 7 == strlen( $tz )) &&
( $ZERO2 > substr( $tz, 5, 2 )) &&
( $NINE2 < substr( $tz, 5, 2 ))))
return $offset;
$hours2sec = (int) substr( $tz, 1, 2 ) * 3600;
$min2sec = (int) substr( $tz, 3, 2 ) * 60;
$sec = ( 7 == strlen( $tz ))
? (int) substr( $tz, -2 ) : $ZERO2;
$offset = $hours2sec + $min2sec + $sec;
$offset = ( self::$MINUS == $tz[0] )
? $offset * -1 : $offset;
return $offset;
}
}