static function ParseRecursion(&$res, $event, $params = [])
{
$event['DT_LENGTH'] = (int)$event['DT_LENGTH'];// length in seconds
$length = $event['DT_LENGTH'];
$rrule = self::ParseRRULE($event['RRULE']);
$exDate = self::GetExDate($event['EXDATE'] ?? null);
$tsFrom = CCalendar::Timestamp($event['DATE_FROM']);
$tsTo = CCalendar::Timestamp($event['DATE_TO']);
if (($tsTo - $tsFrom) > $event['DT_LENGTH'] + CCalendar::DAY_LENGTH)
{
$toTS = $tsFrom + $event['DT_LENGTH'];
if (($event['DT_SKIP_TIME'] ?? null) === 'Y')
{
$toTS -= CCalendar::GetDayLen();
}
$event['DATE_TO'] = CCalendar::Date($toTS);
}
$h24 = CCalendar::GetDayLen();
$instanceCount = ($params['instanceCount'] && $params['instanceCount'] > 0) ? $params['instanceCount'] : false;
$loadLimit = ($params['loadLimit'] && $params['loadLimit'] > 0) ? $params['loadLimit'] : false;
$preciseLimits = $params['preciseLimits'];
if ($length < 0) // Protection from infinite recursion
{
$length = $h24;
}
// Time boundaries
if (isset($params['fromLimitTs']))
{
$limitFromTS = (int)$params['fromLimitTs'];
}
else if (!empty($params['fromLimit']))
{
$limitFromTS = CCalendar::Timestamp($params['fromLimit']);
}
else
{
$limitFromTS = CCalendar::Timestamp(CCalendar::GetMinDate());
}
if (isset($params['toLimitTs']))
{
$limitToTS = (int)$params['toLimitTs'];
}
else if (!empty($params['toLimit']))
{
$limitToTS = CCalendar::Timestamp($params['toLimit']);
}
else
{
$limitToTS = CCalendar::Timestamp(CCalendar::GetMaxDate());
}
$evFromTS = CCalendar::Timestamp($event['DATE_FROM']);
$limitFromTS += $event['TZ_OFFSET_FROM'];
$limitToTS += $event['TZ_OFFSET_TO'];
$limitToTS += CCalendar::GetDayLen();
$limitFromTSReal = $limitFromTS;
$skipTime = $event['DT_SKIP_TIME'] === 'Y';
if ($skipTime && $length > CCalendar::GetDayLen())
{
$limitFromTSReal += $length - CCalendar::GetDayLen();
}
if ($limitFromTS < $event['DATE_FROM_TS_UTC'])
{
$limitFromTS = $event['DATE_FROM_TS_UTC'];
}
if ($limitToTS > $event['DATE_TO_TS_UTC'])
{
$limitToTS = $event['DATE_TO_TS_UTC'];
}
$fromTS = $evFromTS;
if ($skipTime)
{
$event['~DATE_FROM'] = CCalendar::Date(CCalendar::Timestamp($event['DATE_FROM']), false);
$event['~DATE_TO'] = CCalendar::Date(CCalendar::Timestamp($event['DATE_TO']), false);
}
else
{
$event['~DATE_FROM'] = $event['DATE_FROM'];
$event['~DATE_TO'] = $event['DATE_TO'];
}
$hour = date("H", $fromTS);
$min = date("i", $fromTS);
$sec = date("s", $fromTS);
$orig_d = date("d", $fromTS);
$orig_m = date("m", $fromTS);
$orig_y = date("Y", $fromTS);
$realCount = 0;
$dispCount = 0;
while(true)
{
$d = date("d", $fromTS);
$m = date("m", $fromTS);
$y = date("Y", $fromTS);
$toTS = mktime($hour, $min, $sec + $length, $m, $d, $y);
if (
(isset($rrule['COUNT']) && $rrule['COUNT'] > 0 && $realCount >= $rrule['COUNT'])
|| ($loadLimit && $dispCount >= $loadLimit)
|| ($fromTS >= $limitToTS)
|| ($instanceCount && $dispCount >= $instanceCount)
|| (!$fromTS || $fromTS < $evFromTS - CCalendar::GetDayLen()) // Emergensy exit (mantis: 56981)
)
{
break;
}
// Common handling
$event['DATE_FROM'] = CCalendar::Date($fromTS, !$skipTime, false);
$event['RRULE'] = $rrule;
$event['RINDEX'] = $realCount;
$exclude = false;
if (!empty($exDate))
{
$fromDate = CCalendar::Date($fromTS, false);
$exclude = in_array($fromDate, $exDate, true);
}
if ($rrule['FREQ'] === 'WEEKLY')
{
$weekDay = CCalendar::WeekDayByInd(date("w", $fromTS));
if (!empty($rrule['BYDAY'][$weekDay]))
{
if (($preciseLimits && $toTS >= $limitFromTSReal) || (!$preciseLimits && $toTS > $limitFromTS - $h24))
{
if (($event['DT_SKIP_TIME'] ?? null) === 'Y')
{
$toTS -= CCalendar::GetDayLen();
}
$event['DATE_TO'] = CCalendar::Date($toTS - ($event['TZ_OFFSET_FROM'] - $event['TZ_OFFSET_TO']), !$skipTime, false);
if (!$exclude)
{
self::HandleEvent($res, $event, $params['userId']);
$dispCount++;
}
}
$realCount++;
}
if (isset($weekDay) && $weekDay === 'SU')
{
$delta = ($rrule['INTERVAL'] - 1) * 7 + 1;
}
else
{
$delta = 1;
}
$fromTS = mktime($hour, $min, $sec, $m, $d + $delta, $y);
}
else // HOURLY, DAILY, MONTHLY, YEARLY
{
if (($event['DT_SKIP_TIME'] ?? null) === 'Y')
{
$toTS -= CCalendar::GetDayLen();
}
if (($preciseLimits && $toTS >= $limitFromTSReal) ||
(!$preciseLimits && $toTS > $limitFromTS - $h24))
{
$event['DATE_TO'] = CCalendar::Date($toTS - ($event['TZ_OFFSET_FROM'] - $event['TZ_OFFSET_TO']), !$skipTime, false);
//$event['DATE_TO'] = CCalendar::Date($toTS, !$skipTime, false);
if (!$exclude)
{
self::HandleEvent($res, $event, $params['userId']);
$dispCount++;
}
}
$realCount++;
switch ($rrule['FREQ'])
{
case 'DAILY':
$fromTS = mktime($hour, $min, $sec, $m, $d + $rrule['INTERVAL'], $y);
break;
case 'MONTHLY':
$durOffset = $realCount * $rrule['INTERVAL'];
$day = $orig_d;
$month = $orig_m + $durOffset;
$year = $orig_y;
if ($month > 12)
{
$delta_y = floor($month / 12);
$delta_m = $month - $delta_y * 12;
$month = $delta_m;
$year = $orig_y + $delta_y;
}
// 1. Check only for 29-31 dates. 2.We are out of range in this month
if ($orig_d > 28 && $orig_d > date("t", mktime($hour, $min, $sec, $month, 1, $year)))
{
$month++;
$day = 1;
}
$fromTS = mktime($hour, $min, $sec, $month, $day, $year);
break;
case 'YEARLY':
$fromTS = mktime($hour, $min, $sec, $orig_m, $orig_d, $y + $rrule['INTERVAL']);
break;
}
}
}
}