Пример #1
0
 /**
  * updates an array with dates based on a recur pattern
  *
  * if missing, UNTIL is set 1 year from startdate (emergency break)
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.10.19 - 2011-10-31
  * @param array $result, array to update, array([timestamp] => timestamp)
  * @param array $recur, pattern for recurrency (only value part, params ignored)
  * @param array $wdate, component start date
  * @param array $startdate, start date
  * @param array $enddate, optional
  * @return void
  * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start
  */
 public static function _recur2date(&$result, $recur, $wdate, $startdate, $enddate = FALSE)
 {
     foreach ($wdate as $k => $v) {
         if (ctype_digit($v)) {
             $wdate[$k] = (int) $v;
         }
     }
     $wdateStart = $wdate;
     $wdatets = iCalUtilityFunctions::_date2timestamp($wdate);
     $startdatets = iCalUtilityFunctions::_date2timestamp($startdate);
     if (!$enddate) {
         $enddate = $startdate;
         $enddate['year'] += 1;
     }
     // echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
     $endDatets = iCalUtilityFunctions::_date2timestamp($enddate);
     // fix break
     if (!isset($recur['COUNT']) && !isset($recur['UNTIL'])) {
         $recur['UNTIL'] = $enddate;
     }
     // create break
     if (isset($recur['UNTIL'])) {
         $tdatets = iCalUtilityFunctions::_date2timestamp($recur['UNTIL']);
         if ($endDatets > $tdatets) {
             $endDatets = $tdatets;
             // emergency break
             $enddate = iCalUtilityFunctions::_timestamp2date($endDatets, 6);
         } else {
             $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date($endDatets, 6);
         }
     }
     if ($wdatets > $endDatets) {
         // echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
         return array();
         // nothing to do.. .
     }
     if (!isset($recur['FREQ'])) {
         // "MUST be specified.. ."
         $recur['FREQ'] = 'DAILY';
     }
     // ??
     $wkst = isset($recur['WKST']) && 'SU' == $recur['WKST'] ? 24 * 60 * 60 : 0;
     // ??
     $weekStart = (int) date('W', $wdatets + $wkst);
     if (!isset($recur['INTERVAL'])) {
         $recur['INTERVAL'] = 1;
     }
     $countcnt = !isset($recur['BYSETPOS']) ? 1 : 0;
     // DTSTART counts as the first occurrence
     /* find out how to step up dates and set index for interval count */
     $step = array();
     if ('YEARLY' == $recur['FREQ']) {
         $step['year'] = 1;
     } elseif ('MONTHLY' == $recur['FREQ']) {
         $step['month'] = 1;
     } elseif ('WEEKLY' == $recur['FREQ']) {
         $step['day'] = 7;
     } else {
         $step['day'] = 1;
     }
     if (isset($step['year']) && isset($recur['BYMONTH'])) {
         $step = array('month' => 1);
     }
     if (empty($step) && isset($recur['BYWEEKNO'])) {
         // ??
         $step = array('day' => 7);
     }
     if (isset($recur['BYYEARDAY']) || isset($recur['BYMONTHDAY']) || isset($recur['BYDAY'])) {
         $step = array('day' => 1);
     }
     $intervalarr = array();
     if (1 < $recur['INTERVAL']) {
         $intervalix = iCalUtilityFunctions::_recurIntervalIx($recur['FREQ'], $wdate, $wkst);
         $intervalarr = array($intervalix => 0);
     }
     if (isset($recur['BYSETPOS'])) {
         // save start date + weekno
         $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
         // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ###
         if (is_array($recur['BYSETPOS'])) {
             foreach ($recur['BYSETPOS'] as $bix => $bval) {
                 $recur['BYSETPOS'][$bix] = (int) $bval;
             }
         } else {
             $recur['BYSETPOS'] = array((int) $recur['BYSETPOS']);
         }
         if ('YEARLY' == $recur['FREQ']) {
             $wdate['month'] = $wdate['day'] = 1;
             // start from beginning of year
             $wdatets = iCalUtilityFunctions::_date2timestamp($wdate);
             iCalUtilityFunctions::_stepdate($enddate, $endDatets, array('year' => 1));
             // make sure to count whole last year
         } elseif ('MONTHLY' == $recur['FREQ']) {
             $wdate['day'] = 1;
             // start from beginning of month
             $wdatets = iCalUtilityFunctions::_date2timestamp($wdate);
             iCalUtilityFunctions::_stepdate($enddate, $endDatets, array('month' => 1));
             // make sure to count whole last month
         } else {
             iCalUtilityFunctions::_stepdate($enddate, $endDatets, $step);
         }
         // make sure to count whole last period
         // echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test###
         $bysetposWold = (int) date('W', $wdatets + $wkst);
         $bysetposYold = $wdate['year'];
         $bysetposMold = $wdate['month'];
         $bysetposDold = $wdate['day'];
     } else {
         iCalUtilityFunctions::_stepdate($wdate, $wdatets, $step);
     }
     $year_old = null;
     $daynames = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
     /* MAIN LOOP */
     // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
     while (TRUE) {
         if (isset($endDatets) && $wdatets > $endDatets) {
             break;
         }
         if (isset($recur['COUNT']) && $countcnt >= $recur['COUNT']) {
             break;
         }
         if ($year_old != $wdate['year']) {
             $year_old = $wdate['year'];
             $daycnts = array();
             $yeardays = $weekno = 0;
             $yeardaycnt = array();
             foreach ($daynames as $dn) {
                 $yeardaycnt[$dn] = 0;
             }
             for ($m = 1; $m <= 12; $m++) {
                 // count up and update up-counters
                 $daycnts[$m] = array();
                 $weekdaycnt = array();
                 foreach ($daynames as $dn) {
                     $weekdaycnt[$dn] = 0;
                 }
                 $mcnt = date('t', mktime(0, 0, 0, $m, 1, $wdate['year']));
                 for ($d = 1; $d <= $mcnt; $d++) {
                     $daycnts[$m][$d] = array();
                     if (isset($recur['BYYEARDAY'])) {
                         $yeardays++;
                         $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
                     }
                     if (isset($recur['BYDAY'])) {
                         $day = date('w', mktime(0, 0, 0, $m, $d, $wdate['year']));
                         $day = $daynames[$day];
                         $daycnts[$m][$d]['DAY'] = $day;
                         $weekdaycnt[$day]++;
                         $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
                         $yeardaycnt[$day]++;
                         $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
                     }
                     if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                         $daycnts[$m][$d]['weekno_up'] = (int) date('W', mktime(0, 0, $wkst, $m, $d, $wdate['year']));
                     }
                 }
             }
             $daycnt = 0;
             $yeardaycnt = array();
             if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                 $weekno = null;
                 for ($d = 31; $d > 25; $d--) {
                     // get last weekno for year
                     if (!$weekno) {
                         $weekno = $daycnts[12][$d]['weekno_up'];
                     } elseif ($weekno < $daycnts[12][$d]['weekno_up']) {
                         $weekno = $daycnts[12][$d]['weekno_up'];
                         break;
                     }
                 }
             }
             for ($m = 12; $m > 0; $m--) {
                 // count down and update down-counters
                 $weekdaycnt = array();
                 foreach ($daynames as $dn) {
                     $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
                 }
                 $monthcnt = 0;
                 $mcnt = date('t', mktime(0, 0, 0, $m, 1, $wdate['year']));
                 for ($d = $mcnt; $d > 0; $d--) {
                     if (isset($recur['BYYEARDAY'])) {
                         $daycnt -= 1;
                         $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
                     }
                     if (isset($recur['BYMONTHDAY'])) {
                         $monthcnt -= 1;
                         $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
                     }
                     if (isset($recur['BYDAY'])) {
                         $day = $daycnts[$m][$d]['DAY'];
                         $weekdaycnt[$day] -= 1;
                         $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
                         $yeardaycnt[$day] -= 1;
                         $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
                     }
                     if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                         $daycnts[$m][$d]['weekno_down'] = $daycnts[$m][$d]['weekno_up'] - $weekno - 1;
                     }
                 }
             }
         }
         /* check interval */
         if (1 < $recur['INTERVAL']) {
             /* create interval index */
             $intervalix = iCalUtilityFunctions::_recurIntervalIx($recur['FREQ'], $wdate, $wkst);
             /* check interval */
             $currentKey = array_keys($intervalarr);
             $currentKey = end($currentKey);
             // get last index
             if ($currentKey != $intervalix) {
                 $intervalarr = array($intervalix => $intervalarr[$currentKey] + 1);
             }
             if ($recur['INTERVAL'] != $intervalarr[$intervalix] && 0 != $intervalarr[$intervalix]) {
                 /* step up date */
                 // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
                 iCalUtilityFunctions::_stepdate($wdate, $wdatets, $step);
                 continue;
             } else {
                 // continue within the selected interval
                 $intervalarr[$intervalix] = 0;
             }
             // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
         }
         $updateOK = TRUE;
         if ($updateOK && isset($recur['BYMONTH'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYMONTH'], $wdate['month'], $wdate['month'] - 13);
         }
         if ($updateOK && isset($recur['BYWEEKNO'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYWEEKNO'], $daycnts[$wdate['month']][$wdate['day']]['weekno_up'], $daycnts[$wdate['month']][$wdate['day']]['weekno_down']);
         }
         if ($updateOK && isset($recur['BYYEARDAY'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYYEARDAY'], $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'], $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down']);
         }
         if ($updateOK && isset($recur['BYMONTHDAY'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYMONTHDAY'], $wdate['day'], $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down']);
         }
         // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
         if ($updateOK && isset($recur['BYDAY'])) {
             $updateOK = FALSE;
             $m = $wdate['month'];
             $d = $wdate['day'];
             if (isset($recur['BYDAY']['DAY'])) {
                 // single day, opt with year/month day order no
                 $daynoexists = $daynosw = $daynamesw = FALSE;
                 if ($recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY']) {
                     $daynamesw = TRUE;
                 }
                 if (isset($recur['BYDAY'][0])) {
                     $daynoexists = TRUE;
                     if (isset($recur['FREQ']) && $recur['FREQ'] == 'MONTHLY' || isset($recur['BYMONTH'])) {
                         $daynosw = iCalUtilityFunctions::_recurBYcntcheck($recur['BYDAY'][0], $daycnts[$m][$d]['monthdayno_up'], $daycnts[$m][$d]['monthdayno_down']);
                     } elseif (isset($recur['FREQ']) && $recur['FREQ'] == 'YEARLY') {
                         $daynosw = iCalUtilityFunctions::_recurBYcntcheck($recur['BYDAY'][0], $daycnts[$m][$d]['yeardayno_up'], $daycnts[$m][$d]['yeardayno_down']);
                     }
                 }
                 if ($daynoexists && $daynosw && $daynamesw || !$daynoexists && !$daynosw && $daynamesw) {
                     $updateOK = TRUE;
                     // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br />\n"; // test ###
                 }
                 //echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br />\n"; // test ###
             } else {
                 foreach ($recur['BYDAY'] as $bydayvalue) {
                     $daynoexists = $daynosw = $daynamesw = FALSE;
                     if (isset($bydayvalue['DAY']) && $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY']) {
                         $daynamesw = TRUE;
                     }
                     if (isset($bydayvalue[0])) {
                         $daynoexists = TRUE;
                         if (isset($recur['FREQ']) && $recur['FREQ'] == 'MONTHLY' || isset($recur['BYMONTH'])) {
                             $daynosw = iCalUtilityFunctions::_recurBYcntcheck($bydayvalue['0'], $daycnts[$m][$d]['monthdayno_up'], $daycnts[$m][$d]['monthdayno_down']);
                         } elseif (isset($recur['FREQ']) && $recur['FREQ'] == 'YEARLY') {
                             $daynosw = iCalUtilityFunctions::_recurBYcntcheck($bydayvalue['0'], $daycnts[$m][$d]['yeardayno_up'], $daycnts[$m][$d]['yeardayno_down']);
                         }
                     }
                     // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
                     if ($daynoexists && $daynosw && $daynamesw || !$daynoexists && !$daynosw && $daynamesw) {
                         $updateOK = TRUE;
                         break;
                     }
                 }
             }
         }
         // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
         /* check BYSETPOS */
         if ($updateOK) {
             if (isset($recur['BYSETPOS']) && in_array($recur['FREQ'], array('YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'))) {
                 if (isset($recur['WEEKLY'])) {
                     if ($bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up']) {
                         $bysetposw1[] = $wdatets;
                     } else {
                         $bysetposw2[] = $wdatets;
                     }
                 } else {
                     if (isset($recur['FREQ']) && 'YEARLY' == $recur['FREQ'] && $bysetposYold == $wdate['year'] || isset($recur['FREQ']) && 'MONTHLY' == $recur['FREQ'] && ($bysetposYold == $wdate['year'] && $bysetposMold == $wdate['month']) || isset($recur['FREQ']) && 'DAILY' == $recur['FREQ'] && ($bysetposYold == $wdate['year'] && $bysetposMold == $wdate['month'] && $bysetposDold == $wdate['day'])) {
                         // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
                         $bysetposymd1[] = $wdatets;
                     } else {
                         // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
                         $bysetposymd2[] = $wdatets;
                     }
                 }
             } else {
                 /* update result array if BYSETPOS is set */
                 $countcnt++;
                 if ($startdatets <= $wdatets) {
                     // only output within period
                     $result[$wdatets] = TRUE;
                     // echo "recur ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
                 }
                 // echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."<br />\n";//test
                 $updateOK = FALSE;
             }
         }
         /* step up date */
         iCalUtilityFunctions::_stepdate($wdate, $wdatets, $step);
         /* check if BYSETPOS is set for updating result array */
         if ($updateOK && isset($recur['BYSETPOS'])) {
             $bysetpos = FALSE;
             if (isset($recur['FREQ']) && 'YEARLY' == $recur['FREQ'] && $bysetposYold != $wdate['year']) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
             } elseif (isset($recur['FREQ']) && ('MONTHLY' == $recur['FREQ'] && ($bysetposYold != $wdate['year'] || $bysetposMold != $wdate['month']))) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
                 $bysetposMold = $wdate['month'];
             } elseif (isset($recur['FREQ']) && 'WEEKLY' == $recur['FREQ']) {
                 $weekno = (int) date('W', mktime(0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
                 if ($bysetposWold != $weekno) {
                     $bysetposWold = $weekno;
                     $bysetpos = TRUE;
                 }
             } elseif (isset($recur['FREQ']) && 'DAILY' == $recur['FREQ'] && ($bysetposYold != $wdate['year'] || $bysetposMold != $wdate['month'] || $bysetposDold != $wdate['day'])) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
                 $bysetposMold = $wdate['month'];
                 $bysetposDold = $wdate['day'];
             }
             if ($bysetpos) {
                 if (isset($recur['BYWEEKNO'])) {
                     $bysetposarr1 =& $bysetposw1;
                     $bysetposarr2 =& $bysetposw2;
                 } else {
                     $bysetposarr1 =& $bysetposymd1;
                     $bysetposarr2 =& $bysetposymd2;
                 }
                 // echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ###
                 foreach ($recur['BYSETPOS'] as $ix) {
                     if (0 > $ix) {
                         // both positive and negative BYSETPOS allowed
                         $ix = count($bysetposarr1) + $ix + 1;
                     }
                     $ix--;
                     if (isset($bysetposarr1[$ix])) {
                         if ($startdatets <= $bysetposarr1[$ix]) {
                             // only output within period
                             //                $testdate   = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 );                // test ###
                             //                $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ###
                             // echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)";   // test ###
                             $result[$bysetposarr1[$ix]] = TRUE;
                             // echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ###
                         }
                         $countcnt++;
                     }
                     if (isset($recur['COUNT']) && $countcnt >= $recur['COUNT']) {
                         break;
                     }
                 }
                 // echo "<br />\n"; // test ###
                 $bysetposarr1 = $bysetposarr2;
                 $bysetposarr2 = array();
             }
         }
     }
 }
 /**
  * updates an array with dates based on a recur pattern
  *
  * if missing, UNTIL is set 1 year from startdate (emergency break)
  *
  * @author Kjell-Inge Gustafsson, kigkonsult <*****@*****.**>
  * @since 2.21.11 - 2015-03-10
  * @param array $result    array to update, array([Y-m-d] => bool)
  * @param array $recur     pattern for recurrency (only value part, params ignored)
  * @param mixed $wdate     component start date, string / array / (datetime) obj
  * @param mixed $fcnStart  start date, string / array / (datetime) obj
  * @param mixed $fcnEnd    end date, string / array / (datetime) obj
  * @uses iCalUtilityFunctions::_strDate2arr()
  * @uses iCalUtilityFunctions::$fmt
  * @uses iCalUtilityFunctions::_stepdate()
  * @uses iCalUtilityFunctions::_recurIntervalIx()
  * @uses iCalUtilityFunctions::_recurBYcntcheck()
  * @return void
  * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all
  */
 public static function _recur2date(&$result, $recur, $wdate, $fcnStart, $fcnEnd = FALSE)
 {
     if (is_string($wdate)) {
         iCalUtilityFunctions::_strDate2arr($wdate);
     } elseif (is_a($wdate, 'DateTime')) {
         $wdate = $wdate->format(iCalUtilityFunctions::$fmt['YmdHis2']);
         iCalUtilityFunctions::_strDate2arr($wdate);
     }
     foreach ($wdate as $k => $v) {
         if (ctype_digit($v)) {
             $wdate[$k] = (int) $v;
         }
     }
     $wdateYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day']);
     $wdateHis = sprintf(iCalUtilityFunctions::$fmt['His'], $wdate['hour'], $wdate['min'], $wdate['sec']);
     $untilHis = $wdateHis;
     if (is_string($fcnStart)) {
         iCalUtilityFunctions::_strDate2arr($fcnStart);
     } elseif (is_a($fcnStart, 'DateTime')) {
         $fcnStart = $fcnStart->format(iCalUtilityFunctions::$fmt['YmdHis2']);
         iCalUtilityFunctions::_strDate2arr($fcnStart);
     }
     foreach ($fcnStart as $k => $v) {
         if (ctype_digit($v)) {
             $fcnStart[$k] = (int) $v;
         }
     }
     $fcnStartYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $fcnStart['year'], $fcnStart['month'], $fcnStart['day']);
     if (is_string($fcnEnd)) {
         iCalUtilityFunctions::_strDate2arr($fcnEnd);
     } elseif (is_a($fcnEnd, 'DateTime')) {
         $fcnEnd = $fcnEnd->format(iCalUtilityFunctions::$fmt['YmdHis2']);
         iCalUtilityFunctions::_strDate2arr($fcnEnd);
     }
     if (!$fcnEnd) {
         $fcnEnd = $fcnStart;
         $fcnEnd['year'] += 1;
     }
     foreach ($fcnEnd as $k => $v) {
         if (ctype_digit($v)) {
             $fcnEnd[$k] = (int) $v;
         }
     }
     $fcnEndYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day']);
     // echo "<b>recur _in_ comp</b> start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."<br>\n";
     // echo 'recur='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur, TRUE ))."<br> \n"; // test ###
     if (!isset($recur['COUNT']) && !isset($recur['UNTIL'])) {
         $recur['UNTIL'] = $fcnEnd;
     }
     // create break
     if (isset($recur['UNTIL'])) {
         foreach ($recur['UNTIL'] as $k => $v) {
             if (ctype_digit($v)) {
                 $recur['UNTIL'][$k] = (int) $v;
             }
         }
         unset($recur['UNTIL']['tz']);
         if ($fcnEnd > $recur['UNTIL']) {
             $fcnEnd = $recur['UNTIL'];
             // emergency break
             $fcnEndYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day']);
         }
         if (isset($recur['UNTIL']['hour'])) {
             $untilHis = sprintf(iCalUtilityFunctions::$fmt['His'], $recur['UNTIL']['hour'], $recur['UNTIL']['min'], $recur['UNTIL']['sec']);
         } else {
             $untilHis = sprintf(iCalUtilityFunctions::$fmt['His'], 23, 59, 59);
         }
         // echo 'recurUNTIL='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur['UNTIL'], TRUE )).", untilHis={$untilHis}<br> \n"; // test ###
     }
     // echo 'fcnEnd:'.$fcnEndYMD.$untilHis."<br>\n";//test
     if ($wdateYMD > $fcnEndYMD) {
         // echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."<br>\n";//test
         return array();
         // nothing to do.. .
     }
     if (!isset($recur['FREQ'])) {
         // "MUST be specified.. ."
         $recur['FREQ'] = 'DAILY';
     }
     // ??
     $wkst = isset($recur['WKST']) && 'SU' == $recur['WKST'] ? 24 * 60 * 60 : 0;
     // ??
     if (!isset($recur['INTERVAL'])) {
         $recur['INTERVAL'] = 1;
     }
     $countcnt = !isset($recur['BYSETPOS']) ? 1 : 0;
     // DTSTART counts as the first occurrence
     /* find out how to step up dates and set index for interval count */
     $step = array();
     if ('YEARLY' == $recur['FREQ']) {
         $step['year'] = 1;
     } elseif ('MONTHLY' == $recur['FREQ']) {
         $step['month'] = 1;
     } elseif ('WEEKLY' == $recur['FREQ']) {
         $step['day'] = 7;
     } else {
         $step['day'] = 1;
     }
     if (isset($step['year']) && isset($recur['BYMONTH'])) {
         $step = array('month' => 1);
     }
     if (empty($step) && isset($recur['BYWEEKNO'])) {
         // ??
         $step = array('day' => 7);
     }
     if (isset($recur['BYYEARDAY']) || isset($recur['BYMONTHDAY']) || isset($recur['BYDAY'])) {
         $step = array('day' => 1);
     }
     $intervalarr = array();
     if (1 < $recur['INTERVAL']) {
         $intervalix = iCalUtilityFunctions::_recurIntervalIx($recur['FREQ'], $wdate, $wkst);
         $intervalarr = array($intervalix => 0);
     }
     if (isset($recur['BYSETPOS'])) {
         // save start date + weekno
         $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
         // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br>\n"; // test ###
         if (is_array($recur['BYSETPOS'])) {
             foreach ($recur['BYSETPOS'] as $bix => $bval) {
                 $recur['BYSETPOS'][$bix] = (int) $bval;
             }
         } else {
             $recur['BYSETPOS'] = array((int) $recur['BYSETPOS']);
         }
         if ('YEARLY' == $recur['FREQ']) {
             $wdate['month'] = $wdate['day'] = 1;
             // start from beginning of year
             $wdateYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day']);
             iCalUtilityFunctions::_stepdate($fcnEnd, $fcnEndYMD, array('year' => 1));
             // make sure to count whole last year
         } elseif ('MONTHLY' == $recur['FREQ']) {
             $wdate['day'] = 1;
             // start from beginning of month
             $wdateYMD = sprintf(iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day']);
             iCalUtilityFunctions::_stepdate($fcnEnd, $fcnEndYMD, array('month' => 1));
             // make sure to count whole last month
         } else {
             iCalUtilityFunctions::_stepdate($fcnEnd, $fcnEndYMD, $step);
         }
         // make sure to count whole last period
         // echo "BYSETPOS endDat =".implode('-',$fcnEnd).' step='.var_export($step,TRUE)."<br>\n";//test###
         $bysetposWold = (int) date('W', mktime(0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
         $bysetposYold = $wdate['year'];
         $bysetposMold = $wdate['month'];
         $bysetposDold = $wdate['day'];
     } else {
         iCalUtilityFunctions::_stepdate($wdate, $wdateYMD, $step);
     }
     $year_old = null;
     static $daynames = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
     /* MAIN LOOP */
     // echo "recur start:$wdateYMD, end:$fcnEndYMD<br>\n";//test
     while (TRUE) {
         // echo "recur while:$wdateYMD, end:$fcnEndYMD<br>\n";//test
         if ($wdateYMD . $wdateHis > $fcnEndYMD . $untilHis) {
             break;
         }
         if (isset($recur['COUNT']) && $countcnt >= $recur['COUNT']) {
             break;
         }
         if ($year_old != $wdate['year']) {
             $year_old = $wdate['year'];
             $daycnts = array();
             $yeardays = $weekno = 0;
             $yeardaycnt = array();
             foreach ($daynames as $dn) {
                 $yeardaycnt[$dn] = 0;
             }
             for ($m = 1; $m <= 12; $m++) {
                 // count up and update up-counters
                 $daycnts[$m] = array();
                 $weekdaycnt = array();
                 foreach ($daynames as $dn) {
                     $weekdaycnt[$dn] = 0;
                 }
                 $mcnt = date('t', mktime(0, 0, 0, $m, 1, $wdate['year']));
                 for ($d = 1; $d <= $mcnt; $d++) {
                     $daycnts[$m][$d] = array();
                     if (isset($recur['BYYEARDAY'])) {
                         $yeardays++;
                         $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
                     }
                     if (isset($recur['BYDAY'])) {
                         $day = date('w', mktime(0, 0, 0, $m, $d, $wdate['year']));
                         $day = $daynames[$day];
                         $daycnts[$m][$d]['DAY'] = $day;
                         $weekdaycnt[$day]++;
                         $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
                         $yeardaycnt[$day]++;
                         $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
                     }
                     if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                         $daycnts[$m][$d]['weekno_up'] = (int) date('W', mktime(0, 0, $wkst, $m, $d, $wdate['year']));
                     }
                 }
                 // end for( $d   = 1; $d <= $mcnt; $d++ )
             }
             // end for( $m = 1; $m <= 12; $m++ )
             $daycnt = 0;
             $yeardaycnt = array();
             if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                 $weekno = null;
                 for ($d = 31; $d > 25; $d--) {
                     // get last weekno for year
                     if (!$weekno) {
                         $weekno = $daycnts[12][$d]['weekno_up'];
                     } elseif ($weekno < $daycnts[12][$d]['weekno_up']) {
                         $weekno = $daycnts[12][$d]['weekno_up'];
                         break;
                     }
                 }
             }
             for ($m = 12; $m > 0; $m--) {
                 // count down and update down-counters
                 $weekdaycnt = array();
                 foreach ($daynames as $dn) {
                     $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
                 }
                 $monthcnt = 0;
                 $mcnt = date('t', mktime(0, 0, 0, $m, 1, $wdate['year']));
                 for ($d = $mcnt; $d > 0; $d--) {
                     if (isset($recur['BYYEARDAY'])) {
                         $daycnt -= 1;
                         $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
                     }
                     if (isset($recur['BYMONTHDAY'])) {
                         $monthcnt -= 1;
                         $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
                     }
                     if (isset($recur['BYDAY'])) {
                         $day = $daycnts[$m][$d]['DAY'];
                         $weekdaycnt[$day] -= 1;
                         $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
                         $yeardaycnt[$day] -= 1;
                         $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
                     }
                     if (isset($recur['BYWEEKNO']) || $recur['FREQ'] == 'WEEKLY') {
                         $daycnts[$m][$d]['weekno_down'] = $daycnts[$m][$d]['weekno_up'] - $weekno - 1;
                     }
                 }
             }
             // end for( $m = 12; $m > 0; $m-- )
         }
         // end if( $year_old != $wdate['year'] )
         /* check interval */
         if (1 < $recur['INTERVAL']) {
             /* create interval index */
             $intervalix = iCalUtilityFunctions::_recurIntervalIx($recur['FREQ'], $wdate, $wkst);
             /* check interval */
             $currentKey = array_keys($intervalarr);
             $currentKey = end($currentKey);
             // get last index
             if ($currentKey != $intervalix) {
                 $intervalarr = array($intervalix => $intervalarr[$currentKey] + 1);
             }
             if ($recur['INTERVAL'] != $intervalarr[$intervalix] && 0 != $intervalarr[$intervalix]) {
                 /* step up date */
                 // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n";//test
                 iCalUtilityFunctions::_stepdate($wdate, $wdateYMD, $step);
                 continue;
             } else {
                 // continue within the selected interval
                 $intervalarr[$intervalix] = 0;
             }
             // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n";//test
         }
         // endif( 1 < $recur['INTERVAL'] )
         $updateOK = TRUE;
         if ($updateOK && isset($recur['BYMONTH'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYMONTH'], $wdate['month'], $wdate['month'] - 13);
         }
         if ($updateOK && isset($recur['BYWEEKNO'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYWEEKNO'], $daycnts[$wdate['month']][$wdate['day']]['weekno_up'], $daycnts[$wdate['month']][$wdate['day']]['weekno_down']);
         }
         if ($updateOK && isset($recur['BYYEARDAY'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYYEARDAY'], $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'], $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down']);
         }
         if ($updateOK && isset($recur['BYMONTHDAY'])) {
             $updateOK = iCalUtilityFunctions::_recurBYcntcheck($recur['BYMONTHDAY'], $wdate['day'], $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down']);
         }
         // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br>\n";//test###
         if ($updateOK && isset($recur['BYDAY'])) {
             $updateOK = FALSE;
             $m = $wdate['month'];
             $d = $wdate['day'];
             if (isset($recur['BYDAY']['DAY'])) {
                 // single day, opt with year/month day order no
                 $daynoexists = $daynosw = $daynamesw = FALSE;
                 if ($recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY']) {
                     $daynamesw = TRUE;
                 }
                 if (isset($recur['BYDAY'][0])) {
                     $daynoexists = TRUE;
                     if (isset($recur['FREQ']) && $recur['FREQ'] == 'MONTHLY' || isset($recur['BYMONTH'])) {
                         $daynosw = iCalUtilityFunctions::_recurBYcntcheck($recur['BYDAY'][0], $daycnts[$m][$d]['monthdayno_up'], $daycnts[$m][$d]['monthdayno_down']);
                     } elseif (isset($recur['FREQ']) && $recur['FREQ'] == 'YEARLY') {
                         $daynosw = iCalUtilityFunctions::_recurBYcntcheck($recur['BYDAY'][0], $daycnts[$m][$d]['yeardayno_up'], $daycnts[$m][$d]['yeardayno_down']);
                     }
                 }
                 if ($daynoexists && $daynosw && $daynamesw || !$daynoexists && !$daynosw && $daynamesw) {
                     $updateOK = TRUE;
                     // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
                 }
                 // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
             } else {
                 foreach ($recur['BYDAY'] as $bydayvalue) {
                     $daynoexists = $daynosw = $daynamesw = FALSE;
                     if (isset($bydayvalue['DAY']) && $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY']) {
                         $daynamesw = TRUE;
                     }
                     if (isset($bydayvalue[0])) {
                         $daynoexists = TRUE;
                         if (isset($recur['FREQ']) && $recur['FREQ'] == 'MONTHLY' || isset($recur['BYMONTH'])) {
                             $daynosw = iCalUtilityFunctions::_recurBYcntcheck($bydayvalue['0'], $daycnts[$m][$d]['monthdayno_up'], $daycnts[$m][$d]['monthdayno_down']);
                         } elseif (isset($recur['FREQ']) && $recur['FREQ'] == 'YEARLY') {
                             $daynosw = iCalUtilityFunctions::_recurBYcntcheck($bydayvalue['0'], $daycnts[$m][$d]['yeardayno_up'], $daycnts[$m][$d]['yeardayno_down']);
                         }
                     }
                     // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br>\n"; // test ###
                     if ($daynoexists && $daynosw && $daynamesw || !$daynoexists && !$daynosw && $daynamesw) {
                         $updateOK = TRUE;
                         break;
                     }
                 }
             }
         }
         // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br>\n"; // test ###
         /* check BYSETPOS */
         if ($updateOK) {
             if (isset($recur['BYSETPOS']) && in_array($recur['FREQ'], array('YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'))) {
                 if (isset($recur['WEEKLY'])) {
                     if ($bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up']) {
                         $bysetposw1[] = $wdateYMD;
                     } else {
                         $bysetposw2[] = $wdateYMD;
                     }
                 } else {
                     if (isset($recur['FREQ']) && 'YEARLY' == $recur['FREQ'] && $bysetposYold == $wdate['year'] || isset($recur['FREQ']) && 'MONTHLY' == $recur['FREQ'] && ($bysetposYold == $wdate['year'] && $bysetposMold == $wdate['month']) || isset($recur['FREQ']) && 'DAILY' == $recur['FREQ'] && ($bysetposYold == $wdate['year'] && $bysetposMold == $wdate['month'] && $bysetposDold == $wdate['day'])) {
                         // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n";//test
                         $bysetposymd1[] = $wdateYMD;
                     } else {
                         // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n";//test
                         $bysetposymd2[] = $wdateYMD;
                     }
                 }
             } else {
                 if (checkdate($wdate['month'], $wdate['day'], $wdate['year'])) {
                     /* update result array if BYSETPOS is not set */
                     $countcnt++;
                     if ($fcnStartYMD <= $wdateYMD) {
                         // only output within period
                         $result[$wdateYMD] = TRUE;
                         // echo "recur $wdateYMD<br>\n";//test
                     }
                 }
                 // else echo "recur, no date $wdateYMD<br>\n";//test
                 $updateOK = FALSE;
             }
         }
         /* step up date */
         iCalUtilityFunctions::_stepdate($wdate, $wdateYMD, $step);
         /* check if BYSETPOS is set for updating result array */
         if ($updateOK && isset($recur['BYSETPOS'])) {
             $bysetpos = FALSE;
             if (isset($recur['FREQ']) && 'YEARLY' == $recur['FREQ'] && $bysetposYold != $wdate['year']) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
             } elseif (isset($recur['FREQ']) && ('MONTHLY' == $recur['FREQ'] && ($bysetposYold != $wdate['year'] || $bysetposMold != $wdate['month']))) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
                 $bysetposMold = $wdate['month'];
             } elseif (isset($recur['FREQ']) && 'WEEKLY' == $recur['FREQ']) {
                 $weekno = (int) date('W', mktime(0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
                 if ($bysetposWold != $weekno) {
                     $bysetposWold = $weekno;
                     $bysetpos = TRUE;
                 }
             } elseif (isset($recur['FREQ']) && 'DAILY' == $recur['FREQ'] && ($bysetposYold != $wdate['year'] || $bysetposMold != $wdate['month'] || $bysetposDold != $wdate['day'])) {
                 $bysetpos = TRUE;
                 $bysetposYold = $wdate['year'];
                 $bysetposMold = $wdate['month'];
                 $bysetposDold = $wdate['day'];
             }
             if ($bysetpos) {
                 if (isset($recur['BYWEEKNO'])) {
                     $bysetposarr1 =& $bysetposw1;
                     $bysetposarr2 =& $bysetposw2;
                 } else {
                     $bysetposarr1 =& $bysetposymd1;
                     $bysetposarr2 =& $bysetposymd2;
                 }
                 foreach ($recur['BYSETPOS'] as $ix) {
                     if (0 > $ix) {
                         // both positive and negative BYSETPOS allowed
                         $ix = count($bysetposarr1) + $ix + 1;
                     }
                     $ix--;
                     if (isset($bysetposarr1[$ix])) {
                         if ($fcnStartYMD <= $bysetposarr1[$ix]) {
                             // only output within period
                             //                $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, (int) substr( $bysetposarr1[$ix], 4, 2 ), (int) substr( $bysetposarr1[$ix], 6, 2 ), (int) substr( $bysetposarr1[$ix], 0, 3 ))); // test ###
                             // echo " testYMD (weekno)=$bysetposarr1[$ix] ($testweekno)";   // test ###
                             $result[$bysetposarr1[$ix]] = TRUE;
                         }
                         $countcnt++;
                     }
                     if (isset($recur['COUNT']) && $countcnt >= $recur['COUNT']) {
                         break;
                     }
                 }
                 // echo "<br>\n"; // test ###
                 $bysetposarr1 = $bysetposarr2;
                 $bysetposarr2 = array();
             }
             // end if( $bysetpos )
         }
         // end if( $updateOK && isset( $recur['BYSETPOS'] ))
     }
     // end while( TRUE )
     // echo 'output='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $result, TRUE ))."<br> \n"; // test ###
 }