source: trunk/prototype/app/plugins/icalcreator/iCalUtilityFunctions.class.php @ 5341

Revision 5341, 65.5 KB checked in by wmerlotto, 12 years ago (diff)

Ticket #2434 - Commit inicial do novo módulo de agenda do Expresso - expressoCalendar

Line 
1<?php
2/**
3 * iCalcreator class v2.10.5
4 * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
5 * www.kigkonsult.se/iCalcreator/index.php
6 * ical@kigkonsult.se
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22/**
23 * moving all utility (static) functions to a utility class
24 *
25 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
26 * @since 2.10.1 - 2011-07-16
27 *
28 */
29class iCalUtilityFunctions {
30  // Store the single instance of iCalUtilityFunctions
31  private static $m_pInstance;
32
33  // Private constructor to limit object instantiation to within the class
34  private function __construct() {
35    $m_pInstance = FALSE;
36  }
37
38  // Getter method for creating/returning the single instance of this class
39  public static function getInstance() {
40    if (!self::$m_pInstance)
41      self::$m_pInstance = new iCalUtilityFunctions();
42
43    return self::$m_pInstance;
44  }
45/**
46 * check a date(-time) for an opt. timezone and if it is a DATE-TIME or DATE
47 *
48 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
49 * @since 2.4.16 - 2008-10-25
50 * @param array $date, date to check
51 * @param int $parno, no of date parts (i.e. year, month.. .)
52 * @return array $params, property parameters
53 */
54  public static function _chkdatecfg( $theDate, & $parno, & $params ) {
55    if( isset( $params['TZID'] ))
56      $parno = 6;
57    elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
58      $parno = 3;
59    else {
60      if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
61        $parno = 7;
62      if( is_array( $theDate )) {
63        if( isset( $theDate['timestamp'] ))
64          $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
65        else
66          $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
67        if( !empty( $tzid )) {
68          $parno = 7;
69          if( !iCalUtilityFunctions::_isOffset( $tzid ))
70            $params['TZID'] = $tzid; // save only timezone
71        }
72        elseif( !$parno && ( 3 == count( $theDate )) &&
73          ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
74          $parno = 3;
75        else
76          $parno = 6;
77      }
78      else { // string
79        $date = trim( $theDate );
80        if( 'Z' == substr( $date, -1 ))
81          $parno = 7; // UTC DATE-TIME
82        elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
83          ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
84          $parno = 3; // DATE
85        $date = iCalUtilityFunctions::_date_time_string( $date, $parno );
86        if( !empty( $date['tz'] )) {
87          $parno = 7;
88          if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
89            $params['TZID'] = $date['tz']; // save only timezone
90        }
91        elseif( empty( $parno ))
92          $parno = 6;
93      }
94      if( isset( $params['TZID'] ))
95        $parno = 6;
96    }
97  }
98/**
99 * create (very simple) timezone and standard/daylight components
100 *
101 * Result when 'Europe/Stockholm' is used as timezone:
102 *
103 * BEGIN:VTIMEZONE
104 * TZID:Europe/Stockholm
105 * BEGIN:STANDARD
106 * DTSTART:20101031T020000
107 * TZOFFSETFROM:+0200
108 * TZOFFSETTO:+0100
109 * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
110 * TZNAME:CET
111 * END:STANDARD
112 * BEGIN:DAYLIGHT
113 * DTSTART:20100328T030000
114 * TZOFFSETFROM:+0100
115 * TZOFFSETTO:+0200
116 * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
117 * TZNAME:CEST
118 * END:DAYLIGHT
119 * END:VTIMEZONE
120 *
121 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
122 * @since 2.10.3 - 2011-08-01
123 * @param object $calendar, reference to an iCalcreator calendar instance
124 * @param string $timezone, a PHP5 (DateTimeZone) valid timezone
125 * @param array $xProp, *[x-propName => x-propValue], optional
126 * @return bool
127 */
128  public static function createTimezone( & $calendar, $timezone, $xProp=array() ) {
129    if( !class_exists( 'DateTimeZone' ))
130      return FALSE;
131    if( empty( $timezone ))
132      return FALSE;
133    try {
134      $dtz = new DateTimeZone( $timezone );
135    }
136    catch( Exception $e ) {
137      return FALSE;
138    }
139    $stdDTSTART  = $stdTZOFFSETTO = $stdTZOFFSETFROM = $stdTZNAME = $dlghtDTSTART = $dlghtTZOFFSETTO = $dlghtTZOFFSETFROM = $dlghtTZNAME = FALSE;
140    $dateNow     = new DateTime();
141    $transitions = $dtz->getTransitions();
142    foreach( $transitions as $trans ) {
143      if( FALSE === ( $date = DateTime::createFromFormat( 'Y-m-d', substr( $trans['time'], 0, 10 ))))
144        continue;
145      if( $date > $dateNow )
146        break;
147      if( TRUE !== $trans['isdst'] ) {
148        $stdDTSTART    = $trans['time'];
149        $stdTZOFFSETTO = $dlghtTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His( $trans['offset'] );
150        $stdTZNAME     = $trans['abbr'];
151      }
152      else {
153        $dlghtDTSTART    = $trans['time'];
154        $dlghtTZOFFSETTO = $stdTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His( $trans['offset'] );
155        $dlghtTZNAME     = $trans['abbr'];
156      }
157    }
158    if( !$stdDTSTART || !$stdTZOFFSETTO || !$stdTZOFFSETFROM )
159      return FALSE;
160    $tz  = & $calendar->newComponent( 'vtimezone' );
161    $tz->setproperty( 'tzid', $timezone );
162    if( !empty( $xProp )) {
163      foreach( $xProp as $xPropName => $xPropValue )
164        if( 'x-' == strtolower( substr( $xPropName, 0, 2 )))
165          $tz->setproperty( $xPropName, $xPropValue );
166    }
167    $std = & $tz->newComponent( 'standard' );
168    $std->setProperty( 'dtstart',        $stdDTSTART );
169    if( $stdTZNAME )
170      $std->setProperty( 'tzname',       $stdTZNAME );
171    $std->setProperty( 'tzoffsetto',     $stdTZOFFSETTO );
172    $std->setProperty( 'tzoffsetfrom',   $stdTZOFFSETFROM );
173    if(( $stdTZOFFSETTO != $stdTZOFFSETFROM  ) && ( FALSE === iCalUtilityFunctions::_setTZrrule( $std )))
174      $std->setProperty( 'RRULE', array( 'FREQ' => 'YEARLY', 'BYDAY' => array( '-1', 'DAY' => 'SU' ), 'BYMONTH' => 10 ));
175    if(( !$dlghtDTSTART || !$dlghtTZOFFSETTO || !$dlghtTZOFFSETFROM ) || ( $dlghtTZOFFSETTO == $dlghtTZOFFSETFROM ))
176      return TRUE;
177    $dlght = & $tz->newComponent( 'daylight' );
178    $dlght->setProperty( 'dtstart',      $dlghtDTSTART );
179    if( $dlghtTZNAME )
180      $dlght->setProperty( 'tzname',     $dlghtTZNAME );
181    $dlght->setProperty( 'tzoffsetto',   $dlghtTZOFFSETTO );
182    $dlght->setProperty( 'tzoffsetfrom', $dlghtTZOFFSETFROM );
183    if( FALSE === iCalUtilityFunctions::_setTZrrule( $dlght ))
184      $dlght->setProperty( 'RRULE', array( 'FREQ' => 'YEARLY', 'BYDAY' => array( '-1', 'DAY' => 'SU' ), 'BYMONTH' => 3 ));
185    return TRUE;
186  }
187/**
188 * convert date/datetime to timestamp
189 *
190 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
191 * @since 2.4.8 - 2008-10-30
192 * @param array  $datetime  datetime/(date)
193 * @param string $tz        timezone
194 * @return timestamp
195 */
196  public static function _date2timestamp( $datetime, $tz=null ) {
197    $output = null;
198    if( !isset( $datetime['hour'] )) $datetime['hour'] = '0';
199    if( !isset( $datetime['min'] ))  $datetime['min']  = '0';
200    if( !isset( $datetime['sec'] ))  $datetime['sec']  = '0';
201    foreach( $datetime as $dkey => $dvalue ) {
202      if( 'tz' != $dkey )
203        $datetime[$dkey] = (integer) $dvalue;
204    }
205    if( $tz )
206      $datetime['tz'] = $tz;
207    $offset = ( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) ? iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) : 0;
208    $output = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year'] );
209    return $output;
210  }
211/**
212 * ensures internal date-time/date format for input date-time/date in array format
213 *
214 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
215 * @since 0.3.0 - 2006-08-15
216 * @param array $datetime
217 * @param int $parno optional, default FALSE
218 * @return array
219 */
220  public static function _date_time_array( $datetime, $parno=FALSE ) {
221    $output = array();
222    foreach( $datetime as $dateKey => $datePart ) {
223      switch ( $dateKey ) {
224        case '0': case 'year':   $output['year']  = $datePart; break;
225        case '1': case 'month':  $output['month'] = $datePart; break;
226        case '2': case 'day':    $output['day']   = $datePart; break;
227      }
228      if( 3 != $parno ) {
229        switch ( $dateKey ) {
230          case '0':
231          case '1':
232          case '2': break;
233          case '3': case 'hour': $output['hour']  = $datePart; break;
234          case '4': case 'min' : $output['min']   = $datePart; break;
235          case '5': case 'sec' : $output['sec']   = $datePart; break;
236          case '6': case 'tz'  : $output['tz']    = $datePart; break;
237        }
238      }
239    }
240    if( 3 != $parno ) {
241      if( !isset( $output['hour'] ))
242        $output['hour'] = 0;
243      if( !isset( $output['min']  ))
244        $output['min'] = 0;
245      if( !isset( $output['sec']  ))
246        $output['sec'] = 0;
247    }
248    return $output;
249  }
250/**
251 * ensures internal date-time/date format for input date-time/date in string fromat
252 *
253 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
254 * @since 2.6.35 - 2010-12-03
255 * @param array $datetime
256 * @param int $parno optional, default FALSE
257 * @return array
258 */
259  public static function _date_time_string( $datetime, $parno=FALSE ) {
260    $datetime = (string) trim( $datetime );
261    $tz  = null;
262    $len = strlen( $datetime ) - 1;
263    if( 'Z' == substr( $datetime, -1 )) {
264      $tz = 'Z';
265      $datetime = trim( substr( $datetime, 0, $len ));
266    }
267    elseif( ( ctype_digit( substr( $datetime, -2, 2 ))) && // time or date
268                  ( '-' == substr( $datetime, -3, 1 )) ||
269                  ( ':' == substr( $datetime, -3, 1 )) ||
270                  ( '.' == substr( $datetime, -3, 1 ))) {
271      $continue = TRUE;
272    }
273    elseif( ( ctype_digit( substr( $datetime, -4, 4 ))) && // 4 pos offset
274            ( ' +' == substr( $datetime, -6, 2 )) ||
275            ( ' -' == substr( $datetime, -6, 2 ))) {
276      $tz = substr( $datetime, -5, 5 );
277      $datetime = substr( $datetime, 0, ($len - 5));
278    }
279    elseif( ( ctype_digit( substr( $datetime, -6, 6 ))) && // 6 pos offset
280            ( ' +' == substr( $datetime, -8, 2 )) ||
281            ( ' -' == substr( $datetime, -8, 2 ))) {
282      $tz = substr( $datetime, -7, 7 );
283      $datetime = substr( $datetime, 0, ($len - 7));
284    }
285    elseif( ( 6 < $len ) && ( ctype_digit( substr( $datetime, -6, 6 )))) {
286      $continue = TRUE;
287    }
288    elseif( 'T' ==  substr( $datetime, -7, 1 )) {
289      $continue = TRUE;
290    }
291    else {
292      $cx  = $tx = 0;    //  19970415T133000 US-Eastern
293      for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
294        $char = substr( $datetime, $cx, 1 );
295        if(( ' ' == $char) || ctype_digit( $char))
296          break; // if exists, tz ends here.. . ?
297        else
298           $tx--; // tz length counter
299      }
300      if( 0 > $tx ) {
301        $tz = substr( $datetime, $tx );
302        $datetime = trim( substr( $datetime, 0, $len + $tx + 1 ));
303      }
304    }
305    if( 0 < substr_count( $datetime, '-' )) {
306      $datetime = str_replace( '-', '/', $datetime );
307    }
308    elseif( ctype_digit( substr( $datetime, 0, 8 )) &&
309           ( 'T' ==      substr( $datetime, 8, 1 )) &&
310            ctype_digit( substr( $datetime, 9, 6 ))) {
311      $datetime = substr( $datetime,  4, 2 )
312             .'/'.substr( $datetime,  6, 2 )
313             .'/'.substr( $datetime,  0, 4 )
314             .' '.substr( $datetime,  9, 2 )
315             .':'.substr( $datetime, 11, 2 )
316             .':'.substr( $datetime, 13);
317    }
318    $datestring = date( 'Y-m-d H:i:s', strtotime( $datetime ));
319    $tz                = trim( $tz );
320    $output            = array();
321    $output['year']    = substr( $datestring, 0, 4 );
322    $output['month']   = substr( $datestring, 5, 2 );
323    $output['day']     = substr( $datestring, 8, 2 );
324    if(( 6 == $parno ) || ( 7 == $parno ) || ( !$parno && ( 'Z' == $tz ))) {
325      $output['hour']  = substr( $datestring, 11, 2 );
326      $output['min']   = substr( $datestring, 14, 2 );
327      $output['sec']   = substr( $datestring, 17, 2 );
328      if( !empty( $tz ))
329        $output['tz']  = $tz;
330    }
331    elseif( 3 != $parno ) {
332      if(( '00' < substr( $datestring, 11, 2 )) ||
333         ( '00' < substr( $datestring, 14, 2 )) ||
334         ( '00' < substr( $datestring, 17, 2 ))) {
335        $output['hour']  = substr( $datestring, 11, 2 );
336        $output['min']   = substr( $datestring, 14, 2 );
337        $output['sec']   = substr( $datestring, 17, 2 );
338      }
339      if( !empty( $tz ))
340        $output['tz']  = $tz;
341    }
342    return $output;
343  }
344/**
345 * convert local startdate/enddate (Ymd[His]) to duration array
346 *
347 * uses this component dates if missing input dates
348 *
349 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
350 * @since 2.6.11 - 2010-10-21
351 * @param array $startdate
352 * @param array $duration
353 * @return array duration
354 */
355  public static function _date2duration( $startdate, $enddate ) {
356    $startWdate  = mktime( 0, 0, 0, $startdate['month'], $startdate['day'], $startdate['year'] );
357    $endWdate    = mktime( 0, 0, 0, $enddate['month'],   $enddate['day'],   $enddate['year'] );
358    $wduration   = $endWdate - $startWdate;
359    $dur         = array();
360    $dur['week'] = (int) floor( $wduration / ( 7 * 24 * 60 * 60 ));
361    $wduration   =              $wduration % ( 7 * 24 * 60 * 60 );
362    $dur['day']  = (int) floor( $wduration / ( 24 * 60 * 60 ));
363    $wduration   =              $wduration % ( 24 * 60 * 60 );
364    $dur['hour'] = (int) floor( $wduration / ( 60 * 60 ));
365    $wduration   =              $wduration % ( 60 * 60 );
366    $dur['min']  = (int) floor( $wduration / ( 60 ));
367    $dur['sec']  = (int)        $wduration % ( 60 );
368    return $dur;
369  }
370/**
371 * ensures internal duration format for input in array format
372 *
373 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
374 * @since 2.1.1 - 2007-06-24
375 * @param array $duration
376 * @return array
377 */
378  public static function _duration_array( $duration ) {
379    $output = array();
380    if(    is_array( $duration )        &&
381       ( 1 == count( $duration ))       &&
382              isset( $duration['sec'] ) &&
383              ( 60 < $duration['sec'] )) {
384      $durseconds  = $duration['sec'];
385      $output['week'] = floor( $durseconds / ( 60 * 60 * 24 * 7 ));
386      $durseconds  =           $durseconds % ( 60 * 60 * 24 * 7 );
387      $output['day']  = floor( $durseconds / ( 60 * 60 * 24 ));
388      $durseconds  =           $durseconds % ( 60 * 60 * 24 );
389      $output['hour'] = floor( $durseconds / ( 60 * 60 ));
390      $durseconds  =           $durseconds % ( 60 * 60 );
391      $output['min']  = floor( $durseconds / ( 60 ));
392      $output['sec']  =      ( $durseconds % ( 60 ));
393    }
394    else {
395      foreach( $duration as $durKey => $durValue ) {
396        if( empty( $durValue )) continue;
397        switch ( $durKey ) {
398          case '0': case 'week': $output['week']  = $durValue; break;
399          case '1': case 'day':  $output['day']   = $durValue; break;
400          case '2': case 'hour': $output['hour']  = $durValue; break;
401          case '3': case 'min':  $output['min']   = $durValue; break;
402          case '4': case 'sec':  $output['sec']   = $durValue; break;
403        }
404      }
405    }
406    if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
407      unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
408      return $output;
409    }
410    unset( $output['week'] );
411    if( empty( $output['day'] ))
412      unset( $output['day'] );
413    if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) {
414      if( !isset( $output['hour'] )) $output['hour'] = 0;
415      if( !isset( $output['min']  )) $output['min']  = 0;
416      if( !isset( $output['sec']  )) $output['sec']  = 0;
417      if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
418        unset( $output['hour'], $output['min'], $output['sec'] );
419    }
420    return $output;
421  }
422/**
423 * ensures internal duration format for input in string format
424 *
425 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
426 * @since 2.0.5 - 2007-03-14
427 * @param string $duration
428 * @return array
429 */
430  public static function _duration_string( $duration ) {
431    $duration = (string) trim( $duration );
432    while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
433      if( 0 < strlen( $duration ))
434        $duration = substr( $duration, 1 );
435      else
436        return false; // no leading P !?!?
437    }
438    $duration = substr( $duration, 1 ); // skip P
439    $duration = str_replace ( 't', 'T', $duration );
440    $duration = str_replace ( 'T', '', $duration );
441    $output = array();
442    $val    = null;
443    for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
444      switch( strtoupper( substr( $duration, $ix, 1 ))) {
445       case 'W':
446         $output['week'] = $val;
447         $val            = null;
448         break;
449       case 'D':
450         $output['day']  = $val;
451         $val            = null;
452         break;
453       case 'H':
454         $output['hour'] = $val;
455         $val            = null;
456         break;
457       case 'M':
458         $output['min']  = $val;
459         $val            = null;
460         break;
461       case 'S':
462         $output['sec']  = $val;
463         $val            = null;
464         break;
465       default:
466         if( !ctype_digit( substr( $duration, $ix, 1 )))
467           return false; // unknown duration control character  !?!?
468         else
469           $val .= substr( $duration, $ix, 1 );
470      }
471    }
472    return iCalUtilityFunctions::_duration_array( $output );
473  }
474/**
475 * convert duration to date in array format
476 *
477 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
478 * @since 2.8.7 - 2011-03-03
479 * @param array $startdate
480 * @param array $duration
481 * @return array, date format
482 */
483  public static function _duration2date( $startdate=null, $duration=null ) {
484    if( empty( $startdate )) return FALSE;
485    if( empty( $duration ))  return FALSE;
486    $dateOnly          = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
487    $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
488    $startdate['min']  = ( isset( $startdate['min'] ))  ? $startdate['min']  : 0;
489    $startdate['sec']  = ( isset( $startdate['sec'] ))  ? $startdate['sec']  : 0;
490    $dtend = 0;
491    if(    isset( $duration['week'] ))
492      $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
493    if(    isset( $duration['day'] ))
494      $dtend += ( $duration['day'] * 24 * 60 * 60 );
495    if(    isset( $duration['hour'] ))
496      $dtend += ( $duration['hour'] * 60 *60 );
497    if(    isset( $duration['min'] ))
498      $dtend += ( $duration['min'] * 60 );
499    if(    isset( $duration['sec'] ))
500      $dtend +=   $duration['sec'];
501    $dtend  = mktime( $startdate['hour'], $startdate['min'], ( $startdate['sec'] + $dtend ), $startdate['month'], $startdate['day'], $startdate['year'] );
502    $dtend2 = array();
503    $dtend2['year']   = date('Y', $dtend );
504    $dtend2['month']  = date('m', $dtend );
505    $dtend2['day']    = date('d', $dtend );
506    $dtend2['hour']   = date('H', $dtend );
507    $dtend2['min']    = date('i', $dtend );
508    $dtend2['sec']    = date('s', $dtend );
509    if( isset( $startdate['tz'] ))
510      $dtend2['tz']   = $startdate['tz'];
511    if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
512      unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
513    return $dtend2;
514  }
515/**
516 * if not preSet, if exist, remove key with expected value from array and return hit value else return elseValue
517 *
518 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
519 * @since 2.4.16 - 2008-11-08
520 * @param array $array
521 * @param string $expkey, expected key
522 * @param string $expval, expected value
523 * @param int $hitVal optional, return value if found
524 * @param int $elseVal optional, return value if not found
525 * @param int $preSet optional, return value if already preset
526 * @return int
527 */
528  public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
529    if( $preSet )
530      return $preSet;
531    if( !is_array( $array ) || ( 0 == count( $array )))
532      return $elseVal;
533    foreach( $array as $key => $value ) {
534      if( strtoupper( $expkey ) == strtoupper( $key )) {
535        if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
536          unset( $array[$key] );
537          return $hitVal;
538        }
539      }
540    }
541    return $elseVal;
542  }
543/**
544 * creates formatted output for calendar component property data value type date/date-time
545 *
546 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
547 * @since 2.4.8 - 2008-10-30
548 * @param array   $datetime
549 * @param int     $parno, optional, default 6
550 * @return string
551 */
552  public static function _format_date_time( $datetime, $parno=6 ) {
553    if( !isset( $datetime['year'] )  &&
554        !isset( $datetime['month'] ) &&
555        !isset( $datetime['day'] )   &&
556        !isset( $datetime['hour'] )  &&
557        !isset( $datetime['min'] )   &&
558        !isset( $datetime['sec'] ))
559      return ;
560    $output = null;
561    // if( !isset( $datetime['day'] )) { $o=''; foreach($datetime as $k=>$v) {if(is_array($v)) $v=implode('-',$v);$o.=" $k=>$v";} echo " day SAKNAS : $o <br />\n"; }
562    foreach( $datetime as $dkey => & $dvalue )
563      if( 'tz' != $dkey ) $dvalue = (integer) $dvalue;
564    $output = date('Ymd', mktime( 0, 0, 0, $datetime['month'], $datetime['day'], $datetime['year']));
565    if( isset( $datetime['hour'] )  ||
566        isset( $datetime['min'] )   ||
567        isset( $datetime['sec'] )   ||
568        isset( $datetime['tz'] )) {
569      if( isset( $datetime['tz'] )  &&
570         !isset( $datetime['hour'] ))
571        $datetime['hour'] = 0;
572      if( isset( $datetime['hour'] )  &&
573         !isset( $datetime['min'] ))
574        $datetime['min'] = 0;
575      if( isset( $datetime['hour'] )  &&
576          isset( $datetime['min'] )   &&
577         !isset( $datetime['sec'] ))
578        $datetime['sec'] = 0;
579      $date = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year']);
580      $output .= date('\THis', $date );
581      if( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) {
582        $datetime['tz'] = trim( $datetime['tz'] );
583        if( 'Z' == $datetime['tz'] )
584          $output .= 'Z';
585        $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
586        if( 0 != $offset ) {
587          $date = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year']);
588          $output    = date( 'Ymd\THis\Z', $date );
589        }
590      }
591      elseif( 7 == $parno )
592        $output .= 'Z';
593    }
594    return $output;
595  }
596/**
597 * creates formatted output for calendar component property data value type duration
598 *
599 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
600 * @since 2.9.9 - 2011-06-17
601 * @param array $duration ( week, day, hour, min, sec )
602 * @return string
603 */
604  public static function _format_duration( $duration ) {
605    if( isset( $duration['week'] ) ||
606        isset( $duration['day'] )  ||
607        isset( $duration['hour'] ) ||
608        isset( $duration['min'] )  ||
609        isset( $duration['sec'] ))
610       $ok = TRUE;
611    else
612      return;
613    if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
614      return 'P'.$duration['week'].'W';
615    $output = 'P';
616    if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
617      $output .= $duration['day'].'D';
618    if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
619       ( isset( $duration['min'])  && ( 0 < $duration['min'] ))  ||
620       ( isset( $duration['sec'])  && ( 0 < $duration['sec'] )))
621      $output .= 'T';
622    $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '';
623    $output .= ( isset( $duration['min'])  && ( 0 < $duration['min'] ))  ? $duration['min']. 'M' : '';
624    $output .= ( isset( $duration['sec'])  && ( 0 < $duration['sec'] ))  ? $duration['sec']. 'S' : '';
625    if( 'P' == $output )
626      $output = 'PT0S';
627    return $output;
628  }
629/**
630 * checks if input array contains a date
631 *
632 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
633 * @since 2.4.16 - 2008-10-25
634 * @param array $input
635 * @return bool
636 */
637  public static function _isArrayDate( $input ) {
638    if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 ))))
639      return FALSE;
640    if( 7 == count( $input ))
641      return TRUE;
642    if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
643      return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
644    if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
645      return FALSE;
646    if( in_array( 0, $input ))
647      return FALSE;
648    if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
649      return FALSE;
650    if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
651         checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
652      return TRUE;
653    $input = iCalUtilityFunctions::_date_time_string( $input[1].'/'.$input[2].'/'.$input[0], 3 ); //  m - d - Y
654    if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
655      return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
656    return FALSE;
657  }
658/**
659 * checks if input array contains a timestamp date
660 *
661 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
662 * @since 2.4.16 - 2008-10-18
663 * @param array $input
664 * @return bool
665 */
666  public static function _isArrayTimestampDate( $input ) {
667    return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
668  }
669/**
670 * controll if input string contains trailing UTC offset
671 *
672 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
673 * @since 2.4.16 - 2008-10-19
674 * @param string $input
675 * @return bool
676 */
677  public static function _isOffset( $input ) {
678    $input         = trim( (string) $input );
679    if( 'Z' == substr( $input, -1 ))
680      return TRUE;
681    elseif((   5 <= strlen( $input )) &&
682       ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
683       (   '0000'  < substr( $input, -4 )) && (   '9999' >= substr( $input, -4 )))
684      return TRUE;
685    elseif((    7 <= strlen( $input )) &&
686       ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
687       ( '000000'  < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
688      return TRUE;
689    return FALSE;
690
691  }
692/**
693 * transform offset in seconds to [-/+]hhmm[ss]
694 *
695 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
696 * @since 2011-05-02
697 * @param string $seconds
698 * @return string
699 */
700  public static function offsetSec2His( $seconds ) {
701    if( '-' == substr( $seconds, 0, 1 )) {
702      $prefix  = '-';
703      $seconds = substr( $seconds, 1 );
704    }
705    elseif( '+' == substr( $seconds, 0, 1 )) {
706      $prefix  = '+';
707      $seconds = substr( $seconds, 1 );
708    }
709    else
710      $prefix  = '+';
711    $output  = '';
712    $hour    = (int) floor( $seconds / 3600 );
713    if( 10 > $hour )
714      $hour  = '0'.$hour;
715    $seconds = $seconds % 3600;
716    $min     = (int) floor( $seconds / 60 );
717    if( 10 > $min )
718      $min   = '0'.$min;
719    $output  = $hour.$min;
720    $seconds = $seconds % 60;
721    if( 0 < $seconds) {
722      if( 9 < $seconds)
723        $output .= $seconds;
724      else
725        $output .= '0'.$seconds;
726    }
727    return $prefix.$output;
728  }
729/**
730 * remakes a recur pattern to an array of dates
731 *
732 * if missing, UNTIL is set 1 year from startdate (emergency break)
733 *
734 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
735 * @since 2.9.10 - 2011-07-07
736 * @param array $result, array to update, array([timestamp] => timestamp)
737 * @param array $recur, pattern for recurrency (only value part, params ignored)
738 * @param array $wdate, component start date
739 * @param array $startdate, start date
740 * @param array $enddate, optional
741 * @return array of recurrence (start-)dates as index
742 * @todo BYHOUR, BYMINUTE, BYSECOND, ev. BYSETPOS due to ambiguity, WEEKLY at year end/start
743 */
744  public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) {
745    foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
746    $wdateStart  = $wdate;
747    $wdatets     = iCalUtilityFunctions::_date2timestamp( $wdate );
748    $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate );
749    if( !$enddate ) {
750      $enddate = $startdate;
751      $enddate['year'] += 1;
752    }
753// echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
754    $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break
755    if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
756      $recur['UNTIL'] = $enddate; // create break
757    if( isset( $recur['UNTIL'] )) {
758      $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] );
759      if( $endDatets > $tdatets ) {
760        $endDatets = $tdatets; // emergency break
761        $enddate   = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
762      }
763      else
764        $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
765    }
766    if( $wdatets > $endDatets ) {
767// echo "recur out of date ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
768      return array(); // nothing to do.. .
769    }
770    if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
771      $recur['FREQ'] = 'DAILY'; // ??
772    $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
773    $weekStart = (int) date( 'W', ( $wdatets + $wkst ));
774    if( !isset( $recur['INTERVAL'] ))
775      $recur['INTERVAL'] = 1;
776    $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
777            /* find out how to step up dates and set index for interval count */
778    $step = array();
779    if( 'YEARLY' == $recur['FREQ'] )
780      $step['year']  = 1;
781    elseif( 'MONTHLY' == $recur['FREQ'] )
782      $step['month'] = 1;
783    elseif( 'WEEKLY' == $recur['FREQ'] )
784      $step['day']   = 7;
785    else
786      $step['day']   = 1;
787    if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
788      $step = array( 'month' => 1 );
789    if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
790      $step = array( 'day' => 7 );
791    if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
792      $step = array( 'day' => 1 );
793    $intervalarr = array();
794    if( 1 < $recur['INTERVAL'] ) {
795      $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
796      $intervalarr = array( $intervalix => 0 );
797    }
798    if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
799      $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
800// echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ###
801      if( is_array( $recur['BYSETPOS'] )) {
802        foreach( $recur['BYSETPOS'] as $bix => $bval )
803          $recur['BYSETPOS'][$bix] = (int) $bval;
804      }
805      else
806        $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
807      if( 'YEARLY' == $recur['FREQ'] ) {
808        $wdate['month'] = $wdate['day'] = 1; // start from beginning of year
809        $wdatets        = iCalUtilityFunctions::_date2timestamp( $wdate );
810        iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year
811      }
812      elseif( 'MONTHLY' == $recur['FREQ'] ) {
813        $wdate['day']   = 1; // start from beginning of month
814        $wdatets        = iCalUtilityFunctions::_date2timestamp( $wdate );
815        iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month
816      }
817      else
818        iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
819// echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test###
820      $bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
821      $bysetposYold = $wdate['year'];
822      $bysetposMold = $wdate['month'];
823      $bysetposDold = $wdate['day'];
824    }
825    else
826      iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
827    $year_old     = null;
828    $daynames     = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
829             /* MAIN LOOP */
830// echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
831    while( TRUE ) {
832      if( isset( $endDatets ) && ( $wdatets > $endDatets ))
833        break;
834      if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
835        break;
836      if( $year_old != $wdate['year'] ) {
837        $year_old   = $wdate['year'];
838        $daycnts    = array();
839        $yeardays   = $weekno = 0;
840        $yeardaycnt = array();
841        for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
842          $daycnts[$m] = array();
843          $weekdaycnt = array();
844          foreach( $daynames as $dn )
845            $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
846          $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
847          for( $d   = 1; $d <= $mcnt; $d++ ) {
848            $daycnts[$m][$d] = array();
849            if( isset( $recur['BYYEARDAY'] )) {
850              $yeardays++;
851              $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
852            }
853            if( isset( $recur['BYDAY'] )) {
854              $day    = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
855              $day    = $daynames[$day];
856              $daycnts[$m][$d]['DAY'] = $day;
857              $weekdaycnt[$day]++;
858              $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
859              $yeardaycnt[$day]++;
860              $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
861            }
862            if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
863              $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
864          }
865        }
866        $daycnt = 0;
867        $yeardaycnt = array();
868        if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
869          $weekno = null;
870          for( $d=31; $d > 25; $d-- ) { // get last weekno for year
871            if( !$weekno )
872              $weekno = $daycnts[12][$d]['weekno_up'];
873            elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
874              $weekno = $daycnts[12][$d]['weekno_up'];
875              break;
876            }
877          }
878        }
879        for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
880          $weekdaycnt = array();
881          foreach( $daynames as $dn )
882            $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
883          $monthcnt = 0;
884          $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
885          for( $d   = $mcnt; $d > 0; $d-- ) {
886            if( isset( $recur['BYYEARDAY'] )) {
887              $daycnt -= 1;
888              $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
889            }
890            if( isset( $recur['BYMONTHDAY'] )) {
891              $monthcnt -= 1;
892              $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
893            }
894            if( isset( $recur['BYDAY'] )) {
895              $day  = $daycnts[$m][$d]['DAY'];
896              $weekdaycnt[$day] -= 1;
897              $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
898              $yeardaycnt[$day] -= 1;
899              $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
900            }
901            if(  isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
902              $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
903          }
904        }
905      }
906            /* check interval */
907      if( 1 < $recur['INTERVAL'] ) {
908            /* create interval index */
909        $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
910            /* check interval */
911        $currentKey = array_keys( $intervalarr );
912        $currentKey = end( $currentKey ); // get last index
913        if( $currentKey != $intervalix )
914          $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
915        if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
916           ( 0 != $intervalarr[$intervalix] )) {
917            /* step up date */
918// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
919          iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
920          continue;
921        }
922        else // continue within the selected interval
923          $intervalarr[$intervalix] = 0;
924// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
925      }
926      $updateOK = TRUE;
927      if( $updateOK && isset( $recur['BYMONTH'] ))
928        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
929                                           , $wdate['month']
930                                           ,($wdate['month'] - 13));
931      if( $updateOK && isset( $recur['BYWEEKNO'] ))
932        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
933                                           , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
934                                           , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
935      if( $updateOK && isset( $recur['BYYEARDAY'] ))
936        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
937                                           , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
938                                           , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
939      if( $updateOK && isset( $recur['BYMONTHDAY'] ))
940        $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
941                                           , $wdate['day']
942                                           , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
943// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
944      if( $updateOK && isset( $recur['BYDAY'] )) {
945        $updateOK = FALSE;
946        $m = $wdate['month'];
947        $d = $wdate['day'];
948        if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
949          $daynoexists = $daynosw = $daynamesw =  FALSE;
950          if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
951            $daynamesw = TRUE;
952          if( isset( $recur['BYDAY'][0] )) {
953            $daynoexists = TRUE;
954            if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
955              $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
956                                                , $daycnts[$m][$d]['monthdayno_up']
957                                                , $daycnts[$m][$d]['monthdayno_down'] );
958            elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
959              $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
960                                                , $daycnts[$m][$d]['yeardayno_up']
961                                                , $daycnts[$m][$d]['yeardayno_down'] );
962          }
963          if((  $daynoexists &&  $daynosw && $daynamesw ) ||
964             ( !$daynoexists && !$daynosw && $daynamesw )) {
965            $updateOK = TRUE;
966          }
967// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
968        }
969        else {
970          foreach( $recur['BYDAY'] as $bydayvalue ) {
971            $daynoexists = $daynosw = $daynamesw = FALSE;
972            if( isset( $bydayvalue['DAY'] ) &&
973                     ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
974              $daynamesw = TRUE;
975            if( isset( $bydayvalue[0] )) {
976              $daynoexists = TRUE;
977              if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
978                   isset( $recur['BYMONTH'] ))
979                $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
980                                                  , $daycnts[$m][$d]['monthdayno_up']
981                                                  , $daycnts[$m][$d]['monthdayno_down'] );
982              elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
983                $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
984                                                  , $daycnts[$m][$d]['yeardayno_up']
985                                                  , $daycnts[$m][$d]['yeardayno_down'] );
986            }
987// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
988            if((  $daynoexists &&  $daynosw && $daynamesw ) ||
989               ( !$daynoexists && !$daynosw && $daynamesw )) {
990              $updateOK = TRUE;
991              break;
992            }
993          }
994        }
995      }
996// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
997            /* check BYSETPOS */
998      if( $updateOK ) {
999        if( isset( $recur['BYSETPOS'] ) &&
1000          ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
1001          if( isset( $recur['WEEKLY'] )) {
1002            if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
1003              $bysetposw1[] = $wdatets;
1004            else
1005              $bysetposw2[] = $wdatets;
1006          }
1007          else {
1008            if(( isset( $recur['FREQ'] ) && ( 'YEARLY'      == $recur['FREQ'] )  &&
1009                                            ( $bysetposYold == $wdate['year'] ))   ||
1010               ( isset( $recur['FREQ'] ) && ( 'MONTHLY'     == $recur['FREQ'] )  &&
1011                                           (( $bysetposYold == $wdate['year'] )  &&
1012                                            ( $bysetposMold == $wdate['month'] ))) ||
1013               ( isset( $recur['FREQ'] ) && ( 'DAILY'       == $recur['FREQ'] )  &&
1014                                           (( $bysetposYold == $wdate['year'] )  &&
1015                                            ( $bysetposMold == $wdate['month'])  &&
1016                                            ( $bysetposDold == $wdate['day'] )))) {
1017// echo "bysetposymd1[]=".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1018              $bysetposymd1[] = $wdatets;
1019            }
1020            else {
1021// echo "bysetposymd2[]=".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1022              $bysetposymd2[] = $wdatets;
1023            }
1024          }
1025        }
1026        else {
1027            /* update result array if BYSETPOS is set */
1028          $countcnt++;
1029          if( $startdatets <= $wdatets ) { // only output within period
1030            $result[$wdatets] = TRUE;
1031// echo "recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1032          }
1033// echo "recur undate ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))." okdatstart ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$startdatets),6))."<br />\n";//test
1034          $updateOK = FALSE;
1035        }
1036      }
1037            /* step up date */
1038      iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
1039            /* check if BYSETPOS is set for updating result array */
1040      if( $updateOK && isset( $recur['BYSETPOS'] )) {
1041        $bysetpos       = FALSE;
1042        if( isset( $recur['FREQ'] ) && ( 'YEARLY'  == $recur['FREQ'] ) &&
1043          ( $bysetposYold != $wdate['year'] )) {
1044          $bysetpos     = TRUE;
1045          $bysetposYold = $wdate['year'];
1046        }
1047        elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
1048         (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
1049          $bysetpos     = TRUE;
1050          $bysetposYold = $wdate['year'];
1051          $bysetposMold = $wdate['month'];
1052        }
1053        elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY'  == $recur['FREQ'] )) {
1054          $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
1055          if( $bysetposWold != $weekno ) {
1056            $bysetposWold = $weekno;
1057            $bysetpos     = TRUE;
1058          }
1059        }
1060        elseif( isset( $recur['FREQ'] ) && ( 'DAILY'   == $recur['FREQ'] ) &&
1061         (( $bysetposYold != $wdate['year'] )  ||
1062          ( $bysetposMold != $wdate['month'] ) ||
1063          ( $bysetposDold != $wdate['day'] ))) {
1064          $bysetpos     = TRUE;
1065          $bysetposYold = $wdate['year'];
1066          $bysetposMold = $wdate['month'];
1067          $bysetposDold = $wdate['day'];
1068        }
1069        if( $bysetpos ) {
1070          if( isset( $recur['BYWEEKNO'] )) {
1071            $bysetposarr1 = & $bysetposw1;
1072            $bysetposarr2 = & $bysetposw2;
1073          }
1074          else {
1075            $bysetposarr1 = & $bysetposymd1;
1076            $bysetposarr2 = & $bysetposymd2;
1077          }
1078// echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ###
1079          foreach( $recur['BYSETPOS'] as $ix ) {
1080            if( 0 > $ix ) // both positive and negative BYSETPOS allowed
1081              $ix = ( count( $bysetposarr1 ) + $ix + 1);
1082            $ix--;
1083            if( isset( $bysetposarr1[$ix] )) {
1084              if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
1085//                $testdate   = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 );                // test ###
1086//                $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ###
1087// echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)";   // test ###
1088                $result[$bysetposarr1[$ix]] = TRUE;
1089// echo " recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$bysetposarr1[$ix]),6)); // test ###
1090              }
1091              $countcnt++;
1092            }
1093            if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
1094              break;
1095          }
1096// echo "<br />\n"; // test ###
1097          $bysetposarr1 = $bysetposarr2;
1098          $bysetposarr2 = array();
1099        }
1100      }
1101    }
1102  }
1103  public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
1104    if( is_array( $BYvalue ) &&
1105      ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
1106      return TRUE;
1107    elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
1108      return TRUE;
1109    else
1110      return FALSE;
1111  }
1112  public static function _recurIntervalIx( $freq, $date, $wkst ) {
1113            /* create interval index */
1114    switch( $freq ) {
1115      case 'YEARLY':
1116        $intervalix = $date['year'];
1117        break;
1118      case 'MONTHLY':
1119        $intervalix = $date['year'].'-'.$date['month'];
1120        break;
1121      case 'WEEKLY':
1122        $wdatets    = iCalUtilityFunctions::_date2timestamp( $date );
1123        $intervalix = (int) date( 'W', ( $wdatets + $wkst ));
1124       break;
1125      case 'DAILY':
1126           default:
1127        $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
1128        break;
1129    }
1130    return $intervalix;
1131  }
1132/**
1133 * convert input format for exrule and rrule to internal format
1134 *
1135 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1136 * @since 2.9.10 - 2011-07-07
1137 * @param array $rexrule
1138 * @return array
1139 */
1140  public static function _setRexrule( $rexrule ) {
1141    $input          = array();
1142    if( empty( $rexrule ))
1143      return $input;
1144    foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
1145      $rexrulelabel = strtoupper( $rexrulelabel );
1146      if( 'UNTIL'  != $rexrulelabel )
1147        $input[$rexrulelabel]   = $rexrulevalue;
1148      else {
1149        if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp date
1150          $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 6 );
1151        elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) // date-time
1152          $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_array( $rexrulevalue, 6 );
1153        elseif( 8 <= strlen( trim( $rexrulevalue ))) // ex. 2006-08-03 10:12:18
1154          $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_string( $rexrulevalue );
1155        if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
1156          $input[$rexrulelabel]['tz'] = 'Z';
1157      }
1158    }
1159            /* set recurrence rule specification in rfc2445 order */
1160    $input2 = array();
1161    if( isset( $input['FREQ'] ))
1162      $input2['FREQ']       = $input['FREQ'];
1163    if( isset( $input['UNTIL'] ))
1164      $input2['UNTIL']      = $input['UNTIL'];
1165    elseif( isset( $input['COUNT'] ))
1166      $input2['COUNT']      = $input['COUNT'];
1167    if( isset( $input['INTERVAL'] ))
1168      $input2['INTERVAL']   = $input['INTERVAL'];
1169    if( isset( $input['BYSECOND'] ))
1170      $input2['BYSECOND']   = $input['BYSECOND'];
1171    if( isset( $input['BYMINUTE'] ))
1172      $input2['BYMINUTE']   = $input['BYMINUTE'];
1173    if( isset( $input['BYHOUR'] ))
1174      $input2['BYHOUR']     = $input['BYHOUR'];
1175    if( isset( $input['BYDAY'] )) {
1176      if( !is_array( $input['BYDAY'] )) // ensure upper case.. .
1177        $input2['BYDAY']    = strtoupper( $input['BYDAY'] );
1178      else {
1179        foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) {
1180          if( 'DAY'        == strtoupper( $BYDAYx ))
1181             $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv );
1182          elseif( !is_array( $BYDAYv )) {
1183             $input2['BYDAY'][$BYDAYx]  = $BYDAYv;
1184          }
1185          else {
1186            foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
1187              if( 'DAY'    == strtoupper( $BYDAYx2 ))
1188                 $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
1189              else
1190                 $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
1191            }
1192          }
1193        }
1194      }
1195    }
1196    if( isset( $input['BYMONTHDAY'] ))
1197      $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
1198    if( isset( $input['BYYEARDAY'] ))
1199      $input2['BYYEARDAY']  = $input['BYYEARDAY'];
1200    if( isset( $input['BYWEEKNO'] ))
1201      $input2['BYWEEKNO']   = $input['BYWEEKNO'];
1202    if( isset( $input['BYMONTH'] ))
1203      $input2['BYMONTH']    = $input['BYMONTH'];
1204    if( isset( $input['BYSETPOS'] ))
1205      $input2['BYSETPOS']   = $input['BYSETPOS'];
1206    if( isset( $input['WKST'] ))
1207      $input2['WKST']       = $input['WKST'];
1208    return $input2;
1209  }
1210/**
1211 * convert format for input date to internal date with parameters
1212 *
1213 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1214 * @since 2.10.4 - 2011-08-03
1215 * @param mixed $year
1216 * @param mixed $month optional
1217 * @param int $day optional
1218 * @param int $hour optional
1219 * @param int $min optional
1220 * @param int $sec optional
1221 * @param string $tz optional
1222 * @param array $params optional
1223 * @param string $caller optional
1224 * @param string $objName optional
1225 * @param string $tzid optional
1226 * @return array
1227 */
1228  public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) {
1229    $input = $parno = null;
1230    $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
1231    if( iCalUtilityFunctions::_isArrayDate( $year )) {
1232      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1233      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1234      if( isset( $input['params']['TZID'] )) {
1235        $input['params']['VALUE'] = 'DATE-TIME';
1236        unset( $year['tz'] );
1237      }
1238      $hitval          = (( !empty( $year['tz'] ) || !empty( $year[6] ))) ? 7 : 6;
1239      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
1240      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $year ), $parno );
1241      $input['value']  = iCalUtilityFunctions::_date_time_array( $year, $parno );
1242    }
1243    elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1244      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1245      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1246      if( isset( $input['params']['TZID'] )) {
1247        $input['params']['VALUE'] = 'DATE-TIME';
1248        unset( $year['tz'] );
1249      }
1250      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1251      $hitval          = ( isset( $year['tz'] )) ? 7 : 6;
1252      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
1253      $input['value']  = iCalUtilityFunctions::_timestamp2date( $year, $parno );
1254    }
1255    elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
1256      if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1257      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1258      if( isset( $input['params']['TZID'] )) {
1259        $input['params']['VALUE'] = 'DATE-TIME';
1260        $parno = 6;
1261      }
1262      elseif( $tzid && iCalUtilityFunctions::_isOffset( substr( $year, -7 ))) {
1263        if(( in_array( substr( $year, -5, 1 ), array( '+', '-' ))) &&
1264           (   '0000'  < substr( $year, -4 )) && (   '9999' >= substr( $year, -4 )))
1265          $year = substr( $year, 0, ( strlen( $year ) - 5 ));
1266        elseif(( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
1267               ( '000000'  < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
1268          $year = substr( $year, 0, ( strlen( $year ) - 7 ));
1269        $parno = 6;
1270      }
1271      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
1272      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
1273      $input['value']  = iCalUtilityFunctions::_date_time_string( $year, $parno );
1274    }
1275    else {
1276      if( is_array( $params )) {
1277        if( $localtime ) unset ( $params['VALUE'], $params['TZID'] );
1278        $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1279      }
1280      elseif( is_array( $tz )) {
1281        $input['params'] = iCalUtilityFunctions::_setParams( $tz,     array( 'VALUE' => 'DATE-TIME' ));
1282        $tz = FALSE;
1283      }
1284      elseif( is_array( $hour )) {
1285        $input['params'] = iCalUtilityFunctions::_setParams( $hour,   array( 'VALUE' => 'DATE-TIME' ));
1286        $hour = $min = $sec = $tz = FALSE;
1287      }
1288      if( isset( $input['params']['TZID'] )) {
1289        $tz            = null;
1290        $input['params']['VALUE'] = 'DATE-TIME';
1291      }
1292      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1293      $hitval          = ( !empty( $tz )) ? 7 : 6;
1294      $parno           = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
1295      $input['value']  = array( 'year'  => $year, 'month' => $month, 'day'   => $day );
1296      if( 3 != $parno ) {
1297        $input['value']['hour'] = ( $hour ) ? $hour : '0';
1298        $input['value']['min']  = ( $min )  ? $min  : '0';
1299        $input['value']['sec']  = ( $sec )  ? $sec  : '0';
1300        if( !empty( $tz ))
1301          $input['value']['tz'] = $tz;
1302      }
1303    }
1304    if( 3 == $parno ) {
1305      $input['params']['VALUE'] = 'DATE';
1306      unset( $input['value']['tz'] );
1307      unset( $input['params']['TZID'] );
1308    }
1309    elseif( isset( $input['params']['TZID'] ))
1310      unset( $input['value']['tz'] );
1311    if( $localtime )
1312      unset( $input['value']['tz'], $input['params']['TZID'] );
1313    elseif(( !isset( $input['params']['VALUE'] ) || ( $input['params']['VALUE'] != 'DATE' )) && !isset( $input['params']['TZID'] ) && $tzid )
1314      $input['params']['TZID'] = $tzid;
1315    if( isset( $input['value']['tz'] ))
1316      $input['value']['tz'] = (string) $input['value']['tz'];
1317    if( !empty( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) &&
1318      ( !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )))
1319      $input['params']['TZID'] = $input['value']['tz'];
1320    return $input;
1321  }
1322/**
1323 * convert format for input date (UTC) to internal date with parameters
1324 *
1325 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1326 * @since 2.4.17 - 2008-10-31
1327 * @param mixed $year
1328 * @param mixed $month optional
1329 * @param int $day optional
1330 * @param int $hour optional
1331 * @param int $min optional
1332 * @param int $sec optional
1333 * @param array $params optional
1334 * @return array
1335 */
1336  public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
1337    $input = null;
1338    if( iCalUtilityFunctions::_isArrayDate( $year )) {
1339      $input['value']  = iCalUtilityFunctions::_date_time_array( $year, 7 );
1340      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1341    }
1342    elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1343      $input['value']  = iCalUtilityFunctions::_timestamp2date( $year, 7 );
1344      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1345    }
1346    elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
1347      $input['value']  = iCalUtilityFunctions::_date_time_string( $year, 7 );
1348      $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1349    }
1350    else {
1351      $input['value']  = array( 'year'  => $year
1352                              , 'month' => $month
1353                              , 'day'   => $day
1354                              , 'hour'  => $hour
1355                              , 'min'   => $min
1356                              , 'sec'   => $sec );
1357      $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1358    }
1359    $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
1360    if( !isset( $input['value']['hour'] ))
1361      $input['value']['hour'] = 0;
1362    if( !isset( $input['value']['min'] ))
1363      $input['value']['min'] = 0;
1364    if( !isset( $input['value']['sec'] ))
1365      $input['value']['sec'] = 0;
1366    if( !isset( $input['value']['tz'] ) || !iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
1367      $input['value']['tz'] = 'Z';
1368    return $input;
1369  }
1370/**
1371 * check index and set (an indexed) content in multiple value array
1372 *
1373 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1374 * @since 2.6.12 - 2011-01-03
1375 * @param array $valArr
1376 * @param mixed $value
1377 * @param array $params
1378 * @param array $defaults
1379 * @param int $index
1380 * @return void
1381 */
1382  public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
1383    if( !is_array( $valArr )) $valArr = array();
1384    if( $index )
1385      $index = $index - 1;
1386    elseif( 0 < count( $valArr )) {
1387      $keys  = array_keys( $valArr );
1388      $index = end( $keys ) + 1;
1389    }
1390    else
1391      $index = 0;
1392    $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
1393    ksort( $valArr );
1394  }
1395/**
1396 * set input (formatted) parameters- component property attributes
1397 *
1398 * default parameters can be set, if missing
1399 *
1400 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1401 * @since 1.x.x - 2007-05-01
1402 * @param array $params
1403 * @param array $defaults
1404 * @return array
1405 */
1406  public static function _setParams( $params, $defaults=FALSE ) {
1407    if( !is_array( $params))
1408      $params = array();
1409    $input = array();
1410    foreach( $params as $paramKey => $paramValue ) {
1411      if( is_array( $paramValue )) {
1412        foreach( $paramValue as $pkey => $pValue ) {
1413          if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
1414            $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
1415        }
1416      }
1417      elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
1418        $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
1419      if( 'VALUE' == strtoupper( $paramKey ))
1420        $input['VALUE']                 = strtoupper( $paramValue );
1421      else
1422        $input[strtoupper( $paramKey )] = $paramValue;
1423    }
1424    if( is_array( $defaults )) {
1425      foreach( $defaults as $paramKey => $paramValue ) {
1426        if( !isset( $input[$paramKey] ))
1427          $input[$paramKey] = $paramValue;
1428      }
1429    }
1430    return (0 < count( $input )) ? $input : null;
1431  }
1432/**
1433 * set RRULE in vtimezone standard/daylight components based on component dtstart
1434 *
1435 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1436 * @since 2.10.3 - 2011-08-01
1437 * @param object $obj, reference to an iCalcreator vtimezone standard/daylight instance
1438 * @return bool
1439 */
1440  public static function _setTZrrule( & $obj ) {
1441    if( FALSE === ( $date = $obj->getProperty( 'dtstart' )))
1442      return FALSE;
1443    $ts      = mktime( (int) $date['hour'], (int) $date['min'], (int) $date['sec'], (int) $date['month'], (int) $date['day'], (int) $date['year'] );
1444    $daysNm  = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU' );
1445    $day     = $daysNm[date( 'N', $ts )];
1446    $daycnt  = date( 't', $ts ) - $date['day'];
1447    if( 8 > $daycnt )
1448      $ordwk = -1;
1449    elseif( 15 > $daycnt)
1450      $ordwk = -2;
1451    elseif( 8 > $date['day'] )
1452      $ordwk = 1;
1453    else
1454      $ordwk = 2;
1455    $obj->setProperty( 'RRULE', array( 'FREQ' => 'YEARLY', 'BYDAY' => array( (string) $ordwk, 'DAY' => $day ), 'BYMONTH' => (int) $date['month'] ));
1456    return TRUE;
1457  }
1458/**
1459 * step date, return updated date, array and timpstamp
1460 *
1461 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1462 * @since 2.4.16 - 2008-10-18
1463 * @param array $date, date to step
1464 * @param int $timestamp
1465 * @param array $step, default array( 'day' => 1 )
1466 * @return void
1467 */
1468  public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) {
1469    foreach( $step as $stepix => $stepvalue )
1470      $date[$stepix] += $stepvalue;
1471    $timestamp  = iCalUtilityFunctions::_date2timestamp( $date );
1472    $date       = iCalUtilityFunctions::_timestamp2date( $timestamp, 6 );
1473    foreach( $date as $k => $v ) {
1474      if( ctype_digit( $v ))
1475        $date[$k] = (int) $v;
1476    }
1477  }
1478/**
1479 * convert timestamp to date array
1480 *
1481 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1482 * @since 2.4.16 - 2008-11-01
1483 * @param mixed $timestamp
1484 * @param int $parno
1485 * @return array
1486 */
1487  public static function _timestamp2date( $timestamp, $parno=6 ) {
1488    if( is_array( $timestamp )) {
1489      if(( 7 == $parno ) && !empty( $timestamp['tz'] ))
1490        $tz = $timestamp['tz'];
1491      $timestamp = $timestamp['timestamp'];
1492    }
1493    $output = array( 'year'  => date( 'Y', $timestamp )
1494                   , 'month' => date( 'm', $timestamp )
1495                   , 'day'   => date( 'd', $timestamp ));
1496    if( 3 != $parno ) {
1497             $output['hour'] =  date( 'H', $timestamp );
1498             $output['min']  =  date( 'i', $timestamp );
1499             $output['sec']  =  date( 's', $timestamp );
1500      if( isset( $tz ))
1501        $output['tz'] = $tz;
1502    }
1503    return $output;
1504  }
1505/**
1506 * convert timestamp to duration in array format
1507 *
1508 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1509 * @since 2.6.23 - 2010-10-23
1510 * @param int $timestamp
1511 * @return array, duration format
1512 */
1513  public static function _timestamp2duration( $timestamp ) {
1514    $dur         = array();
1515    $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 ));
1516    $timestamp   =              $timestamp % ( 7 * 24 * 60 * 60 );
1517    $dur['day']  = (int) floor( $timestamp / ( 24 * 60 * 60 ));
1518    $timestamp   =              $timestamp % ( 24 * 60 * 60 );
1519    $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 ));
1520    $timestamp   =              $timestamp % ( 60 * 60 );
1521    $dur['min']  = (int) floor( $timestamp / ( 60 ));
1522    $dur['sec']  = (int)        $timestamp % ( 60 );
1523    return $dur;
1524  }
1525/**
1526 * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
1527 *
1528 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1529 * @since 2.10.5 - 2011-08-03
1530 * @param string $date    (date to alter)
1531 * @param string $tzFrom, PHP valid old timezone
1532 * @param string $tzTo,   PHP valid new timezone, default 'UTC'
1533 * @param string $format, (opt) date output format, default 'Ymd\THis'
1534 * @return bool
1535 */
1536  public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) {
1537    if( !class_exists( 'DateTime' ) || !class_exists( 'DateTimeZone' ))
1538      return FALSE;
1539    if( FALSE === ( $timestamp = strtotime( $date )))
1540      return FALSE;
1541    try {
1542      $d = new DateTime( date( 'Y-m-d H:i:s', $timestamp ), new DateTimeZone( $tzFrom ));
1543      $d->setTimezone( new DateTimeZone( $tzTo ));
1544    }
1545    catch (Exception $e) {
1546      return FALSE;
1547    }
1548    $date = $d->format( $format );
1549    return TRUE;
1550  }
1551/**
1552 * convert (numeric) local time offset to seconds correcting localtime to GMT
1553 *
1554 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1555 * @since 2.4.16 - 2008-10-19
1556 * @param string $offset
1557 * @return integer
1558 */
1559  public static function _tz2offset( $tz ) {
1560    $tz           = trim( (string) $tz );
1561    $offset       = 0;
1562    if(((     5  != strlen( $tz )) && ( 7  != strlen( $tz ))) ||
1563       ((    '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
1564       (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
1565           (( 7  == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
1566      return $offset;
1567    $hours2sec    = (int) substr( $tz, 1, 2 ) * 3600;
1568    $min2sec      = (int) substr( $tz, 3, 2 ) *   60;
1569    $sec          = ( 7  == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
1570    $offset       = $hours2sec + $min2sec + $sec;
1571    $offset       = ('-' == substr( $tz, 0, 1 )) ? $offset : -1 * $offset;
1572    return $offset;
1573  }
1574}
1575?>
Note: See TracBrowser for help on using the repository browser.