• Модуль: calendar
  • Путь к файлу: ~/bitrix/modules/calendar/classes/general/calendar_event.php
  • Класс: CCalendarEvent
  • Вызов: CCalendarEvent::ParseRecursion
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;
			}
		}
	}
}