%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/tjamichg/cursos.tjamich.gob.mx/vendor/kigkonsult/icalcreator/src/util/
Upload File :
Create Path :
Current File : /home/tjamichg/cursos.tjamich.gob.mx/vendor/kigkonsult/icalcreator/src/util/utilRecur.php

<?php
/**
 * iCalcreator, a PHP rfc2445/rfc5545 solution.
 *
 * This file is a part of iCalcreator.
 *
 * Copyright (c) 2007-2017 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
 * Link      http://kigkonsult.se/iCalcreator/index.php
 * Package   iCalcreator
 * Version   2.24
 * License   Subject matter of licence is the software iCalcreator.
 *           The above copyright, link, package and version notices,
 *           this licence notice and the [rfc5545] PRODID as implemented and
 *           invoked in iCalcreator shall be included in all copies or
 *           substantial portions of the iCalcreator.
 *           iCalcreator can be used either under the terms of
 *           a proprietary license, available at <https://kigkonsult.se/>
 *           or the GNU Affero General Public License, version 3:
 *           iCalcreator is free software: you can redistribute it and/or
 *           modify it under the terms of the GNU Affero General Public License
 *           as published by the Free Software Foundation, either version 3 of
 *           the License, or (at your option) any later version.
 *           iCalcreator is distributed in the hope that it will be useful,
 *           but WITHOUT ANY WARRANTY; without even the implied warranty of
 *           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *           GNU Affero General Public License for more details.
 *           You should have received a copy of the GNU Affero General Public
 *           License along with this program.
 *           If not, see <http://www.gnu.org/licenses/>.
 */
namespace kigkonsult\iCalcreator\util;
/**
 * iCalcreator recur support class
 *
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
 * @since 2.23.18 - 2017-06-14
 */
class utilRecur {
/**
 * Static values for recurrence FREQuence
 * @access private
 * @static
 */
  private static $DAILY           = 'DAILY';
  private static $WEEKLY          = 'WEEKLY';
  private static $MONTHLY         = 'MONTHLY';
  private static $YEARLY          = 'YEARLY';
//private static $SECONDLY        = 'SECONDLY';
//private static $MINUTELY        = 'MINUTELY';
//private static $HOURLY          = 'HOURLY';
  private static $DAYNAMES        = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
  private static $YEARCNT_UP      = 'yearcnt_up';
  private static $YEARCNT_DOWN    = 'yearcnt_down';
  private static $MONTHDAYNO_UP   = 'monthdayno_up';
  private static $MONTHDAYNO_DOWN = 'monthdayno_down';
  private static $MONTHCNT_DOWN   = 'monthcnt_down';
  private static $YEARDAYNO_UP    = 'yeardayno_up';
  private static $YEARDAYNO_DOWN  = 'yeardayno_down';
  private static $WEEKNO_UP       = 'weekno_up';
  private static $WEEKNO_DOWN     = 'weekno_down';
  private static $W               = 'W';
/**
 * Sort recur dates
 * @param array  $byDayA
 * @param array  $byDayB
 * @return int
 * @access private
 * @static
 */
  private static function recurBydaySort( $byDayA, $byDayB ) {
    static $days = ['SU' => 0,
                    'MO' => 1,
                    'TU' => 2,
                    'WE' => 3,
                    'TH' => 4,
                    'FR' => 5,
                    'SA' => 6];
    return ( $days[substr( $byDayA, -2 )] < $days[substr( $byDayB, -2 )] ) ? -1 : 1;
  }
/**
 * Return formatted output for calendar component property data value type recur
 *
 * @param string $recurlabel
 * @param array  $recurData
 * @param bool   $allowEmpty
 * @return string
 * @static
 */
  public static function formatRecur( $recurlabel, $recurData, $allowEmpty ) {
    static $FMTFREQEQ    = 'FREQ=%s';
    static $FMTDEFAULTEQ = ';%s=%s';
    static $FMTOTHEREQ   = ';%s=';
    static $RECURBYDAYSORTER = null;
    static $SP0          = '';
    if( is_null( $RECURBYDAYSORTER ))
      $RECURBYDAYSORTER = [get_class(), 'recurBydaySort'];
    if( empty( $recurData ))
      return null;
    $output = null;
    foreach( $recurData as $rx => $theRule ) {
      if( empty( $theRule[util::$LCvalue] )) {
        if( $allowEmpty )
          $output .= util::createElement( $recurlabel );
        continue;
      }
      $attributes = ( isset( $theRule[util::$LCparams] ))
                  ? util::createParams( $theRule[util::$LCparams] )
                  : null;
      $content1  = $content2  = null;
      foreach( $theRule[util::$LCvalue] as $ruleLabel => $ruleValue ) {
        $ruleLabel = strtoupper( $ruleLabel );
        switch( $ruleLabel ) {
          case util::$FREQ :
            $content1 .= sprintf( $FMTFREQEQ, $ruleValue );
            break;
          case util::$UNTIL :
            $parno     = ( isset( $ruleValue[util::$LCHOUR] )) ? 7 : 3;
            $content2 .= sprintf( $FMTDEFAULTEQ, util::$UNTIL,
                                                 util::date2strdate( $ruleValue,
                                                                     $parno ));
            break;
          case util::$COUNT :
          case util::$INTERVAL :
          case util::$WKST :
            $content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue );
            break;
          case util::$BYDAY :
            $byday          = [$SP0];
            $bx             = 0;
            foreach( $ruleValue as $bix => $bydayPart ) {
              if( ! empty( $byday[$bx] ) &&   // new day
                  ! ctype_digit( substr( $byday[$bx], -1 )))
                $byday[++$bx] = $SP0;
              if( ! is_array( $bydayPart ))   // day without order number
                $byday[$bx] .= (string) $bydayPart;
              else {                          // day with order number
                foreach( $bydayPart as $bix2 => $bydayPart2 )
                  $byday[$bx] .= (string) $bydayPart2;
              }
            } // end foreach( $ruleValue as $bix => $bydayPart )
            if( 1 < count( $byday ))
              usort( $byday, $RECURBYDAYSORTER );
            $content2      .= sprintf( $FMTDEFAULTEQ, util::$BYDAY,
                                                      implode( util::$COMMA,
                                                               $byday ));
            break;
          default : // BYSECOND/BYMINUTE/BYHOUR/BYMONTHDAY/BYYEARDAY/BYWEEKNO/BYMONTH/BYSETPOS...
            if( is_array( $ruleValue )) {
              $content2 .= sprintf( $FMTOTHEREQ, $ruleLabel );
              $content2 .= implode( util::$COMMA, $ruleValue );
            }
            else
              $content2 .= sprintf( $FMTDEFAULTEQ, $ruleLabel, $ruleValue );
            break;
        } // end switch( $ruleLabel )
      } // end foreach( $theRule[util::$LCvalue] )) as $ruleLabel => $ruleValue )
      $output .= util::createElement( $recurlabel,
                                      $attributes,
                                      $content1 . $content2 );
    } // end foreach( $recurData as $rx => $theRule )
    return $output;
  }
/**
 * Convert input format for EXRULE and RRULE to internal format
 *
 * @param array $rexrule
 * @return array
 * @static
 */
  public static function setRexrule( $rexrule ) {
    static $BYSECOND = 'BYSECOND';
    static $BYMINUTE = 'BYMINUTE';
    static $BYHOUR   = 'BYHOUR';
    $input       = [];
    if( empty( $rexrule ))
      return $input;
    $rexrule     = array_change_key_case( $rexrule, CASE_UPPER );
    foreach( $rexrule as $rexruleLabel => $rexruleValue ) {
      if( util::$UNTIL != $rexruleLabel )
        $input[$rexruleLabel]   = $rexruleValue;
      else {
        util::strDate2arr( $rexruleValue );
        if( util::isArrayTimestampDate( $rexruleValue )) // timestamp, always date-time UTC
          $input[$rexruleLabel] = util::timestamp2date( $rexruleValue, 7, util::$UTC );
        elseif( util::isArrayDate( $rexruleValue )) { // date or UTC date-time
          $parno = ( isset( $rexruleValue[util::$LCHOUR] ) ||
                     isset( $rexruleValue[4] )) ? 7 : 3;
          $d = util::chkDateArr( $rexruleValue, $parno );
          if(( 3 < $parno ) &&
                       isset( $d[util::$LCtz] ) &&
                ( util::$Z != $d[util::$LCtz] ) &&
             util::isOffset( $d[util::$LCtz] )) {
            $input[$rexruleLabel] = util::strDate2ArrayDate( sprintf( util::$YMDHISE,
                                                                      (int) $d[util::$LCYEAR],
                                                                      (int) $d[util::$LCMONTH],
                                                                      (int) $d[util::$LCDAY],
                                                                      (int) $d[util::$LCHOUR],
                                                                      (int) $d[util::$LCMIN],
                                                                      (int) $d[util::$LCSEC],
                                                                            $d[util::$LCtz] ),
                                                             7 );
            unset( $input[$rexruleLabel][util::$UNPARSEDTEXT] );
          }
          else
           $input[$rexruleLabel] = $d;
        }
        elseif( 8 <= strlen( trim( $rexruleValue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC
          $input[$rexruleLabel] = util::strDate2ArrayDate( $rexruleValue );
          unset( $input[$rexruleLabel][util::$UNPARSEDTEXT] );
        }
        if(( 3 < count( $input[$rexruleLabel] )) &&
               ! isset( $input[$rexruleLabel][util::$LCtz] ))
          $input[$rexruleLabel][util::$LCtz] = util::$Z;
      }
    } // end foreach( $rexrule as $rexruleLabel => $rexruleValue )
            /* set recurrence rule specification in rfc2445 order */
    $input2 = [];
    if( isset( $input[util::$FREQ] ))
      $input2[util::$FREQ]     = $input[util::$FREQ];
    if( isset( $input[util::$UNTIL] ))
      $input2[util::$UNTIL]    = $input[util::$UNTIL];
    elseif( isset( $input[util::$COUNT] ))
      $input2[util::$COUNT]    = $input[util::$COUNT];
    if( isset( $input[util::$INTERVAL] ))
      $input2[util::$INTERVAL] = $input[util::$INTERVAL];
    if( isset( $input[$BYSECOND] ))
      $input2[$BYSECOND]       = $input[$BYSECOND];
    if( isset( $input[$BYMINUTE] ))
      $input2[$BYMINUTE]       = $input[$BYMINUTE];
    if( isset( $input[$BYHOUR] ))
      $input2[$BYHOUR]         = $input[$BYHOUR];
    if( isset( $input[util::$BYDAY] )) {
      if( ! is_array( $input[util::$BYDAY] )) // ensure upper case.. .
        $input2[util::$BYDAY]  = strtoupper( $input[util::$BYDAY] );
      else {
        foreach( $input[util::$BYDAY] as $BYDAYx => $BYDAYv ) {
          if( 0 == strcasecmp( util::$DAY, $BYDAYx ))
             $input2[util::$BYDAY][util::$DAY] = strtoupper( $BYDAYv );
          elseif( ! is_array( $BYDAYv ))
             $input2[util::$BYDAY][$BYDAYx]  = $BYDAYv;
          else {
            foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
              if( 0 == strcasecmp( util::$DAY, $BYDAYx2 ))
                 $input2[util::$BYDAY][$BYDAYx][util::$DAY] = strtoupper( $BYDAYv2 );
              else
                 $input2[util::$BYDAY][$BYDAYx][$BYDAYx2]   = $BYDAYv2;
            }
          }
        }
      }
    } // end if( isset( $input[util::$BYDAY] ))
    if( isset( $input[util::$BYMONTHDAY] ))
      $input2[util::$BYMONTHDAY] = $input[util::$BYMONTHDAY];
    if( isset( $input[util::$BYYEARDAY] ))
      $input2[util::$BYYEARDAY]  = $input[util::$BYYEARDAY];
    if( isset( $input[util::$BYWEEKNO] ))
      $input2[util::$BYWEEKNO]   = $input[util::$BYWEEKNO];
    if( isset( $input[util::$BYMONTH] ))
      $input2[util::$BYMONTH]    = $input[util::$BYMONTH];
    if( isset( $input[util::$BYSETPOS] ))
      $input2[util::$BYSETPOS]   = $input[util::$BYSETPOS];
    if( isset( $input[util::$WKST] ))
      $input2[util::$WKST]       = $input[util::$WKST];
    return $input2;
  }
/**
 * Update array $result with dates based on a recur pattern
 *
 * If missing, UNTIL is set 1 year from startdate (emergency break)
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
 * @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
 * @static
 * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all
 */
  public static function recur2date( & $result,
                                       $recur,
                                       $wdate,
                                       $fcnStart,
                                       $fcnEnd=false ) {
    static $YEAR2DAYARR     = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'];
    static $SU              = 'SU';
    self::reFormatDate( $wdate );
    $wdateYMD     = sprintf( util::$YMD, $wdate[util::$LCYEAR],
                                         $wdate[util::$LCMONTH],
                                         $wdate[util::$LCDAY] );
    $wdateHis     = sprintf( util::$HIS, $wdate[util::$LCHOUR],
                                         $wdate[util::$LCMIN],
                                         $wdate[util::$LCSEC] );
    $untilHis     = $wdateHis;
    self::reFormatDate( $fcnStart );
    $fcnStartYMD = sprintf( util::$YMD, $fcnStart[util::$LCYEAR],
                                        $fcnStart[util::$LCMONTH],
                                        $fcnStart[util::$LCDAY] );
    if( ! empty( $fcnEnd ))
      self::reFormatDate( $fcnEnd );
    else {
      $fcnEnd = $fcnStart;
      $fcnEnd[util::$LCYEAR] += 1;
    }
    $fcnEndYMD = sprintf( util::$YMD, $fcnEnd[util::$LCYEAR],
                                      $fcnEnd[util::$LCMONTH],
                                      $fcnEnd[util::$LCDAY] );
// echo "<b>recur _in_ comp</b> start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."<br>\n";
// echo 'recur='.str_replace( [PHP_EOL, ' '], null, var_export( $recur, true ))."<br> \n"; // test ###
    if( ! isset( $recur[util::$COUNT] ) &&
        ! isset( $recur[util::$UNTIL] ))
      $recur[util::$UNTIL] = $fcnEnd; // create break
    if( isset( $recur[util::$UNTIL] )) {
      foreach( $recur[util::$UNTIL] as $k => $v ) {
        if( ctype_digit( $v ))
          $recur[util::$UNTIL][$k] = (int) $v;
      }
      unset( $recur[util::$UNTIL][util::$LCtz] );
      if( $fcnEnd > $recur[util::$UNTIL] ) {
        $fcnEnd    = $recur[util::$UNTIL]; // emergency break
        $fcnEndYMD = sprintf( util::$YMD, $fcnEnd[util::$LCYEAR],
                                          $fcnEnd[util::$LCMONTH],
                                          $fcnEnd[util::$LCDAY] );
      }
      if( isset( $recur[util::$UNTIL][util::$LCHOUR] ))
        $untilHis  = sprintf( util::$HIS, $recur[util::$UNTIL][util::$LCHOUR],
                                          $recur[util::$UNTIL][util::$LCMIN],
                                          $recur[util::$UNTIL][util::$LCSEC] );
      else
        $untilHis  = sprintf( util::$HIS, 23, 59, 59 );
// echo 'recurUNTIL='.str_replace( [PHP_EOL, ' '], '', var_export( $recur['UNTIL'], true )).", untilHis={$untilHis}<br> \n"; // test ###
    } // end if( isset( $recur[util::$UNTIL] ))
// echo 'fcnEnd:'.$fcnEndYMD.$untilHis."<br>\n"; // test ###
    if( $wdateYMD > $fcnEndYMD ) {
// echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."<br>\n"; // test ###
      return []; // nothing to do.. .
    }
    if( ! isset( $recur[util::$FREQ] )) // "MUST be specified.. ."
      $recur[util::$FREQ] = self::$DAILY; // ??
    $wkst         = ( isset( $recur[util::$WKST] ) &&
                    ( $SU == $recur[util::$WKST] )) ? 24*60*60 : 0; // ??
    if( ! isset( $recur[util::$INTERVAL] ))
      $recur[util::$INTERVAL] = 1;
    $recurCount   = ( ! isset( $recur[util::$BYSETPOS] )) ? 1 : 0; // DTSTART counts as the first occurrence
            /* find out how to step up dates and set index for interval count */
    $step = [];
    if( self::$YEARLY == $recur[util::$FREQ] )
      $step[util::$LCYEAR]  = 1;
    elseif( self::$MONTHLY == $recur[util::$FREQ] )
      $step[util::$LCMONTH] = 1;
    elseif( self::$WEEKLY == $recur[util::$FREQ] )
      $step[util::$LCDAY]   = 7;
    else
      $step[util::$LCDAY]   = 1;
    if( isset( $step[util::$LCYEAR] ) && isset( $recur[util::$BYMONTH] ))
      $step = [util::$LCMONTH => 1];
    if( empty( $step ) && isset( $recur[util::$BYWEEKNO] )) // ??
      $step = [util::$LCDAY => 7];
    if( isset( $recur[util::$BYYEARDAY] ) ||
        isset( $recur[util::$BYMONTHDAY] ) ||
        isset( $recur[util::$BYDAY] ))
      $step = [util::$LCDAY => 1];
    $intervalarr = [];
    if( 1 < $recur[util::$INTERVAL] ) {
      $intervalix = self::recurIntervalIx( $recur[util::$FREQ],
                                           $wdate,
                                           $wkst );
      $intervalarr = [$intervalix => 0];
    }
    if( isset( $recur[util::$BYSETPOS] )) { // save start date + weekno
      $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = [];
      if( is_array( $recur[util::$BYSETPOS] )) {
        foreach( $recur[util::$BYSETPOS] as $bix => $bval )
          $recur[util::$BYSETPOS][$bix] = (int) $bval;
      }
      else
        $recur[util::$BYSETPOS] = [(int) $recur[util::$BYSETPOS]];
      if( self::$YEARLY == $recur[util::$FREQ] ) {
        $wdate[util::$LCMONTH] = $wdate[util::$LCDAY] = 1; // start from beginning of year
        $wdateYMD = sprintf( util::$YMD, $wdate[util::$LCYEAR],
                                         $wdate[util::$LCMONTH],
                                         $wdate[util::$LCDAY] );
        self::stepdate( $fcnEnd, $fcnEndYMD, [util::$LCYEAR => 1] ); // make sure to count last year
      }
      elseif( self::$MONTHLY == $recur[util::$FREQ] ) {
        $wdate[util::$LCDAY]   = 1; // start from beginning of month
        $wdateYMD = sprintf( util::$YMD, $wdate[util::$LCYEAR],
                                         $wdate[util::$LCMONTH],
                                         $wdate[util::$LCDAY] );
        self::stepdate( $fcnEnd, $fcnEndYMD, [util::$LCMONTH => 1] ); // make sure to count last month
      }
      else
        self::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( self::$W,
                                  mktime( 0,
                                          0,
                                          $wkst,
                                          $wdate[util::$LCMONTH],
                                          $wdate[util::$LCDAY],
                                          $wdate[util::$LCYEAR] ));
      $bysetposYold = $wdate[util::$LCYEAR];
      $bysetposMold = $wdate[util::$LCMONTH];
      $bysetposDold = $wdate[util::$LCDAY];
    } // end if( isset( $recur[util::$BYSETPOS] ))
    else
      self::stepdate( $wdate, $wdateYMD, $step);
    $year_old      = null;
             /* MAIN LOOP */
    while( true ) {
// $txt = ( isset( $recur[util::$COUNT] )) ? '. recurCount : ' . $recurCount . ', COUNT : ' . $recur[util::$COUNT] : null; // test ###
// echo "recur start while:<b>{$wdateYMD}</b>, end:{$fcnEndYMD}{$txt}<br>\n"; // test ###
      if( $wdateYMD.$wdateHis > $fcnEndYMD.$untilHis )
        break;
      if( isset( $recur[util::$COUNT] ) &&
         ( $recurCount >= $recur[util::$COUNT] ))
        break;
      if( $year_old != $wdate[util::$LCYEAR] ) { // $year_old=null 1:st time
        $year_old    = $wdate[util::$LCYEAR];
        $daycnts     = self::initDayCnts( $wdate, $recur, $wkst );
      }

            /* check interval */
      if( 1 < $recur[util::$INTERVAL] ) {
            /* create interval index */
        $intervalix = self::recurIntervalIx( $recur[util::$FREQ],
                                             $wdate,
                                             $wkst );
            /* check interval */
        $currentKey = array_keys( $intervalarr );
        $currentKey = end( $currentKey ); // get last index
        if( $currentKey != $intervalix )
          $intervalarr = [$intervalix => ( $intervalarr[$currentKey] + 1 )];
        if(( $recur[util::$INTERVAL] != $intervalarr[$intervalix] ) &&
           ( 0 != $intervalarr[$intervalix] )) {
            /* step up date */
// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n"; // test ###
          self::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[util::$BYMONTH] ))
        $updateOK = self::recurBYcntcheck( $recur[util::$BYMONTH],
                                           $wdate[util::$LCMONTH],
                                         ( $wdate[util::$LCMONTH] - 13 ));
      if( $updateOK && isset( $recur[util::$BYWEEKNO] ))
        $updateOK = self::recurBYcntcheck( $recur[util::$BYWEEKNO],
                                           $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_UP],
                                           $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_DOWN] );
      if( $updateOK && isset( $recur[util::$BYYEARDAY] ))
        $updateOK = self::recurBYcntcheck( $recur[util::$BYYEARDAY],
                                           $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$YEARCNT_UP],
                                           $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$YEARCNT_DOWN] );
      if( $updateOK && isset( $recur[util::$BYMONTHDAY] ))
        $updateOK = self::recurBYcntcheck( $recur[util::$BYMONTHDAY],
                                           $wdate[util::$LCDAY],
                                           $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$MONTHCNT_DOWN] );
// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'true' : 'false'; echo "<br>\n"; // test ######
      if( $updateOK && isset( $recur[util::$BYDAY] )) {
        $updateOK = false;
        $m = $wdate[util::$LCMONTH];
        $d = $wdate[util::$LCDAY];
        if( isset( $recur[util::$BYDAY][util::$DAY] )) { // single day, opt with year/month day order no
          $daynoexists = $daynosw = $daynamesw =  false;
          if( $recur[util::$BYDAY][util::$DAY] == $daycnts[$m][$d][util::$DAY] )
            $daynamesw = true;
          if( isset( $recur[util::$BYDAY][0] )) {
            $daynoexists = true;
            if(( isset( $recur[util::$FREQ] ) &&
                ( $recur[util::$FREQ] == self::$MONTHLY )) ||
               isset( $recur[util::$BYMONTH] ))
              $daynosw = self::recurBYcntcheck( $recur[util::$BYDAY][0],
                                                $daycnts[$m][$d][self::$MONTHDAYNO_UP],
                                                $daycnts[$m][$d][self::$MONTHDAYNO_DOWN] );
            elseif( isset( $recur[util::$FREQ] ) &&
                   ( $recur[util::$FREQ] == self::$YEARLY ))
              $daynosw = self::recurBYcntcheck( $recur[util::$BYDAY][0],
                                                $daycnts[$m][$d][self::$YEARDAYNO_UP],
                                                $daycnts[$m][$d][self::$YEARDAYNO_DOWN] );
          }
          if((   $daynoexists &&   $daynosw && $daynamesw ) ||
             ( ! $daynoexists && ! $daynosw && $daynamesw )) {
            $updateOK = true;
// echo "m=$m d=$d day=".$daycnts[$m][$d][util::$DAY]." yeardayno_up=".$daycnts[$m][$d][self::$YEARDAYNO_UP]." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
          }
// echo "m=$m d=$d day=".$daycnts[$m][$d][util::$DAY]." yeardayno_up=".$daycnts[$m][$d][self::$YEARDAYNO_UP]." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
        }
        else {
          foreach( $recur[util::$BYDAY] as $bydayvalue ) {
            $daynoexists = $daynosw = $daynamesw = false;
            if( isset( $bydayvalue[util::$DAY] ) &&
                     ( $bydayvalue[util::$DAY] == $daycnts[$m][$d][util::$DAY] ))
              $daynamesw = true;
            if( isset( $bydayvalue[0] )) {
              $daynoexists = true;
              if(( isset( $recur[util::$FREQ] ) &&
                  ( $recur[util::$FREQ] == self::$MONTHLY )) ||
                  isset( $recur[util::$BYMONTH] ))
                $daynosw = self::recurBYcntcheck( $bydayvalue['0'],
                                                  $daycnts[$m][$d][self::$MONTHDAYNO_UP],
                                                  $daycnts[$m][$d][self::$MONTHDAYNO_DOWN] );
              elseif( isset( $recur[util::$FREQ] ) &&
                    ( $recur[util::$FREQ] == self::$YEARLY ))
                $daynosw = self::recurBYcntcheck( $bydayvalue['0'],
                                                  $daycnts[$m][$d][self::$YEARDAYNO_UP],
                                                  $daycnts[$m][$d][self::$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[util::$BYSETPOS] ) &&
          ( in_array( $recur[util::$FREQ], $YEAR2DAYARR))) {
          if( isset( $recur[self::$WEEKLY] )) {
            if( $bysetposWold == $daycnts[$wdate[util::$LCMONTH]][$wdate[util::$LCDAY]][self::$WEEKNO_UP] )
              $bysetposw1[] = $wdateYMD;
            else
              $bysetposw2[] = $wdateYMD;
          }
          else {
            if(( isset( $recur[util::$FREQ] ) &&
                ( self::$YEARLY == $recur[util::$FREQ] ) &&
                ( $bysetposYold == $wdate[util::$LCYEAR] ))      ||
               ( isset( $recur[util::$FREQ] ) &&
                ( self::$MONTHLY  == $recur[util::$FREQ] ) &&
                 (( $bysetposYold == $wdate[util::$LCYEAR] ) &&
                  ( $bysetposMold == $wdate[util::$LCMONTH] )))  ||
               ( isset( $recur[util::$FREQ] ) &&
                 ( self::$DAILY    == $recur[util::$FREQ] ) &&
                  (( $bysetposYold == $wdate[util::$LCYEAR] ) &&
                   ( $bysetposMold == $wdate[util::$LCMONTH]) &&
                   ( $bysetposDold == $wdate[util::$LCDAY] )))) {
// 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;
            }
          } // end else
        }
        else {
          if( checkdate((int) $wdate[util::$LCMONTH],
                        (int) $wdate[util::$LCDAY],
                        (int) $wdate[util::$LCYEAR] )) {
            /* update result array if BYSETPOS is not set */
            $recurCount++;
            if( $fcnStartYMD <= $wdateYMD ) { // only output within period
              $result[$wdateYMD] = true;
// echo "recur $wdateYMD, recurCount:{$recurCount}<br>\n"; // test ###
            }
          }
// else echo "recur, no date $wdateYMD<br>\n"; // test ###
          $updateOK = false;
        }
      }
            /* step up date */
      self::stepdate( $wdate, $wdateYMD, $step);
            /* check if BYSETPOS is set for updating result array */
      if( $updateOK && isset( $recur[util::$BYSETPOS] )) {
        $bysetpos       = false;
        if( isset( $recur[util::$FREQ] ) &&
           ( self::$YEARLY == $recur[util::$FREQ] ) &&
           ( $bysetposYold != $wdate[util::$LCYEAR] )) {
          $bysetpos     = true;
          $bysetposYold = $wdate[util::$LCYEAR];
        }
        elseif( isset( $recur[util::$FREQ] ) &&
          ( self::$MONTHLY == $recur[util::$FREQ] &&
          (( $bysetposYold != $wdate[util::$LCYEAR] ) ||
           ( $bysetposMold != $wdate[util::$LCMONTH] )))) {
          $bysetpos     = true;
          $bysetposYold = $wdate[util::$LCYEAR];
          $bysetposMold = $wdate[util::$LCMONTH];
        }
        elseif( isset( $recur[util::$FREQ] ) &&
          ( self::$WEEKLY  == $recur[util::$FREQ] )) {
          $weekno = (int) date( self::$W,
                                mktime( 0,
                                        0,
                                        $wkst,
                                        $wdate[util::$LCMONTH],
                                        $wdate[util::$LCDAY],
                                        $wdate[util::$LCYEAR] ));
          if( $bysetposWold != $weekno ) {
            $bysetposWold = $weekno;
            $bysetpos     = true;
          }
        }
        elseif( isset( $recur[util::$FREQ] ) &&
          ( self::$DAILY   == $recur[util::$FREQ] ) &&
          (( $bysetposYold != $wdate[util::$LCYEAR] )  ||
           ( $bysetposMold != $wdate[util::$LCMONTH] ) ||
           ( $bysetposDold != $wdate[util::$LCDAY] ))) {
          $bysetpos     = true;
          $bysetposYold = $wdate[util::$LCYEAR];
          $bysetposMold = $wdate[util::$LCMONTH];
          $bysetposDold = $wdate[util::$LCDAY];
        }
        if( $bysetpos ) {
          if( isset( $recur[util::$BYWEEKNO] )) {
            $bysetposarr1 = & $bysetposw1;
            $bysetposarr2 = & $bysetposw2;
          }
          else {
            $bysetposarr1 = & $bysetposymd1;
            $bysetposarr2 = & $bysetposymd2;
          }

          foreach( $recur[util::$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;
              }
              $recurCount++;
            }
            if( isset( $recur[util::$COUNT] ) && ( $recurCount >= $recur[util::$COUNT] ))
              break;
          }
// echo "<br>\n"; // test ###
          $bysetposarr1 = $bysetposarr2;
          $bysetposarr2 = [];
        } // end if( $bysetpos )
      } // end if( $updateOK && isset( $recur['BYSETPOS'] ))
    } // end while( true )
// echo 'output='.str_replace( [PHP_EOL, ' '], '', var_export( $result, true ))."<br> \n"; // test ###
  }
/**
 * Checking BYDAY (etc) hits, recur2date help function
 *
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
 * @since 2.6.12 - 2011-01-03
 * @param array $BYvalue
 * @param int   $upValue
 * @param int   $downValue
 * @return bool
 * @access private
 * @static
 */
  private static function recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
    if( is_array( $BYvalue ) &&
      ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
      return true;
    elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
      return true;
    else
      return false;
  }
/**
 * (re-)Calculate internal index, recur2date help function
 *
 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
 * @since 2.6.12 - 2011-01-03
 * @param string $freq
 * @param array  $date
 * @param int    $wkst
 * @return bool
 * @access private
 * @static
 */
  private static function recurIntervalIx( $freq, $date, $wkst ) {
            /* create interval index */
    switch( $freq ) {
      case self::$YEARLY :
        $intervalix = $date[util::$LCYEAR];
        break;
      case self::$MONTHLY :
        $intervalix = $date[util::$LCYEAR] . util::$MINUS . $date[util::$LCMONTH];
        break;
      case self::$WEEKLY :
        $intervalix = (int) date( self::$W,
                                  mktime( 0,
                                          0,
                                          $wkst,
                                          (int) $date[util::$LCMONTH],
                                          (int) $date[util::$LCDAY],
                                          (int) $date[util::$LCYEAR] ));
       break;
      case self::$DAILY :
           default:
        $intervalix = $date[util::$LCYEAR] .
                      util::$MINUS .
                      $date[util::$LCMONTH] .
                      util::$MINUS .
                      $date[util::$LCDAY];
        break;
    }
    return $intervalix;
  }
/**
 * Return updated date, array and timpstamp
 *
 * @param array  $date     date to step
 * @param string $dateYMD  date YMD
 * @param array  $step     default array( util::$LCDAY => 1 )
 * @return void
 * @access private
 * @static
 */
  private static function stepdate( & $date, & $dateYMD, $step=null ) {
    static $t = 't';
    if( is_null( $step ))
      $step   = [util::$LCDAY => 1];
    if( ! isset( $date[util::$LCHOUR] ))
      $date[util::$LCHOUR] = 0;
    if( ! isset( $date[util::$LCMIN] ))
      $date[util::$LCMIN]  = 0;
    if( ! isset( $date[util::$LCSEC] ))
      $date[util::$LCSEC]  = 0;
    if( isset( $step[util::$LCDAY] ))
      $mcnt        = date( $t,
                           mktime( (int) $date[util::$LCHOUR],
                                   (int) $date[util::$LCMIN],
                                   (int) $date[util::$LCSEC],
                                   (int) $date[util::$LCMONTH],
                                   (int) $date[util::$LCDAY],
                                   (int) $date[util::$LCYEAR] ));
    foreach( $step as $stepix => $stepvalue )
      $date[$stepix]            += $stepvalue;
    if( isset( $step[util::$LCMONTH] )) {
      if( 12 < $date[util::$LCMONTH] ) {
        $date[util::$LCYEAR]    += 1;
        $date[util::$LCMONTH]   -= 12;
      }
    }
    elseif( isset( $step[util::$LCDAY] )) {
      if( $mcnt < $date[util::$LCDAY] ) {
        $date[util::$LCDAY]     -= $mcnt;
        $date[util::$LCMONTH]   += 1;
        if( 12 < $date[util::$LCMONTH] ) {
          $date[util::$LCYEAR]  += 1;
          $date[util::$LCMONTH] -= 12;
        }
      }
    }
    $dateYMD       = sprintf( util::$YMD, (int) $date[util::$LCYEAR],
                                          (int) $date[util::$LCMONTH],
                                          (int) $date[util::$LCDAY] );
  }
/**
 * Return initiated $daycnts
 *
 * @param array $wdate
 * @param array $recur
 * @param int   $wkst
 * @return array
 * @access private
 * @static
 */
  private static function initDayCnts( array $wdate, array $recur, $wkst ) {
    $daycnts    = [];
    $yeardaycnt = [];
    $yeardays   = 0;
    foreach( self::$DAYNAMES as $dn )
      $yeardaycnt[$dn] = 0;
    for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
      $daycnts[$m] = [];
      $weekdaycnt = [];
      foreach( self::$DAYNAMES as $dn )
        $weekdaycnt[$dn] = 0;
      $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate[util::$LCYEAR] ));
      for( $d   = 1; $d <= $mcnt; $d++ ) {
        $daycnts[$m][$d] = [];
        if( isset( $recur[util::$BYYEARDAY] )) {
          $yeardays++;
          $daycnts[$m][$d][self::$YEARCNT_UP] = $yeardays;
        }
        if( isset( $recur[util::$BYDAY] )) {
          $day    = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate[util::$LCYEAR] ));
          $day    = self::$DAYNAMES[$day];
          $daycnts[$m][$d][util::$DAY] = $day;
          $weekdaycnt[$day]++;
          $daycnts[$m][$d][self::$MONTHDAYNO_UP] = $weekdaycnt[$day];
          $yeardaycnt[$day]++;
          $daycnts[$m][$d][self::$YEARDAYNO_UP] = $yeardaycnt[$day];
        }
        if( isset( $recur[util::$BYWEEKNO] ) ||
                 ( $recur[util::$FREQ] == self::$WEEKLY ))
          $daycnts[$m][$d][self::$WEEKNO_UP] = (int) date( self::$W,
                                                           mktime( 0,
                                                                   0,
                                                                   $wkst,
                                                                   $m,
                                                                   $d,
                                                                   $wdate[util::$LCYEAR] ));
      } // end for( $d   = 1; $d <= $mcnt; $d++ )
    } // end for( $m = 1; $m <= 12; $m++ )
    $daycnt = 0;
    $yeardaycnt = [];
    if( isset( $recur[util::$BYWEEKNO] ) ||
             ( $recur[util::$FREQ] == self::$WEEKLY )) {
      $weekno = null;
      for( $d=31; $d > 25; $d-- ) { // get last weekno for year
        if( ! $weekno )
          $weekno = $daycnts[12][$d][self::$WEEKNO_UP];
        elseif( $weekno < $daycnts[12][$d][self::$WEEKNO_UP] ) {
          $weekno = $daycnts[12][$d][self::$WEEKNO_UP];
          break;
        }
      }
    }
    for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
      $weekdaycnt = [];
      foreach( self::$DAYNAMES as $dn )
        $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
      $monthcnt = 0;
      $mcnt     = date( 't', mktime( 0, 0, 0, $m, 1, $wdate[util::$LCYEAR] ));
      for( $d   = $mcnt; $d > 0; $d-- ) {
        if( isset( $recur[util::$BYYEARDAY] )) {
          $daycnt -= 1;
          $daycnts[$m][$d][self::$YEARCNT_DOWN] = $daycnt;
        }
        if( isset( $recur[util::$BYMONTHDAY] )) {
          $monthcnt -= 1;
          $daycnts[$m][$d][self::$MONTHCNT_DOWN] = $monthcnt;
        }
        if( isset( $recur[util::$BYDAY] )) {
          $day  = $daycnts[$m][$d][util::$DAY];
          $weekdaycnt[$day] -= 1;
          $daycnts[$m][$d][self::$MONTHDAYNO_DOWN] = $weekdaycnt[$day];
          $yeardaycnt[$day] -= 1;
          $daycnts[$m][$d][self::$YEARDAYNO_DOWN] = $yeardaycnt[$day];
        }
        if( isset( $recur[util::$BYWEEKNO] ) ||
                 ( $recur[util::$FREQ] == self::$WEEKLY ))
          $daycnts[$m][$d][self::$WEEKNO_DOWN] = ( $daycnts[$m][$d][self::$WEEKNO_UP] - $weekno - 1 );
      }
    } // end for( $m = 12; $m > 0; $m-- )
    return $daycnts;
  }
/**
 * Return a reformatted input date
 *
 * @param mixed $aDate
 * @access private
 * @static
 */
  private static function reFormatDate( & $aDate ) {
    static $YMDHIS2 = 'Y-m-d H:i:s';
    switch( true ) {
      case ( is_string( $aDate )) :
        util::strDate2arr( $aDate );
        break;
      case ( util::isDateTimeClass( $aDate )) :
        $aDate = $aDate->format( $YMDHIS2 );
        util::strDate2arr( $aDate );
        break;
      default :
        break;
    }
    foreach( $aDate as $k => $v ) {
      if( ctype_digit( $v ))
        $aDate[$k] = (int) $v;
    }
  }
}

Zerion Mini Shell 1.0