Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/bizproc/lib/Calc/Libs/ |
Upload File : |
<?php namespace Bitrix\Bizproc\Calc\Libs; use Bitrix\Main; use Bitrix\Bizproc; use Bitrix\Bizproc\Calc\Arguments; use Bitrix\Main\Localization\Loc; class DateLib extends BaseLib { private static $weekHolidays; private static $yearHolidays; private static $startWorkDay; private static $endWorkDay; private static $yearWorkdays; private const ONE_HOUR = 3600; private const TWELVE_HOURS = 12 * self::ONE_HOUR; function getFunctions(): array { return [ 'date' => [ 'args' => true, 'func' => 'callDate', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_DATE_DESCRIPTION'), ], 'dateadd' => [ 'args' => true, 'func' => 'callDateAdd', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_DATEADD_DESCRIPTION'), ], 'datediff' => [ 'args' => true, 'func' => 'callDateDiff', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_DATEDIFF_DESCRIPTION'), ], 'addworkdays' => [ 'args' => true, 'func' => 'callAddWorkDays', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_ADDWORKDAYS_DESCRIPTION'), ], 'workdateadd' => [ 'args' => true, 'func' => 'callWorkDateAdd', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_WORKDATEADD_DESCRIPTION'), ], 'isworkday' => [ 'args' => true, 'func' => 'callIsWorkDay', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_ISWORKDAY_DESCRIPTION'), ], 'isworktime' => [ 'args' => true, 'func' => 'callIsWorkTime', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_ISWORKTIME_DESCRIPTION'), ], 'touserdate' => [ 'args' => true, 'func' => 'callToUserDate', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_TOUSERDATE_DESCRIPTION'), ], 'getuserdateoffset' => [ 'args' => true, 'func' => 'callGetUserDateOffset', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_GETUSERDATEOFFSET_DESCRIPTION'), ], 'strtotime' => [ 'args' => true, 'func' => 'callStrtotime', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_STRTOTIME_DESCRIPTION'), ], 'locdate' => [ 'args' => true, 'func' => 'callLocDate', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_LOCDATE_DESCRIPTION'), ], 'settime' => [ 'args' => true, 'func' => 'callSetTime', 'description' => Loc::getMessage('BIZPROC_CALC_FUNCTION_SETTIME_DESCRIPTION'), ], ]; } public function callDateAdd(Arguments $args) { $date = $args->getFirstSingle(); $offset = $this->getDateTimeOffset($date); $date = $this->makeTimestamp($date); $interval = $args->getSecondSingle(); if ($date === false || ($interval && !is_scalar($interval))) { return null; } if (empty($interval)) { return $date; // new Bizproc\BaseType\Value\DateTime($date, $offset); } // 1Y2M3D4H5I6S, -4 days 5 hours, 1month, 5h $interval = trim($interval); $bMinus = false; if (mb_substr($interval, 0, 1) === "-") { $interval = mb_substr($interval, 1); $bMinus = true; } static $arMap = [ "y" => "YYYY", "year" => "YYYY", "years" => "YYYY", "m" => "MM", "month" => "MM", "months" => "MM", "d" => "DD", "day" => "DD", "days" => "DD", "h" => "HH", "hour" => "HH", "hours" => "HH", "i" => "MI", "min" => "MI", "minute" => "MI", "minutes" => "MI", "s" => "SS", "sec" => "SS", "second" => "SS", "seconds" => "SS", ]; $arInterval = []; while (preg_match('/\s*([\d]+)\s*([a-z]+)\s*/i', $interval, $match)) { $match2 = mb_strtolower($match[2]); if (array_key_exists($match2, $arMap)) { $arInterval[$arMap[$match2]] = ($bMinus ? -intval($match[1]) : intval($match[1])); } $p = mb_strpos($interval, $match[0]); $interval = mb_substr($interval, $p + mb_strlen($match[0])); } $date += $offset; // to server $newDate = AddToTimeStamp($arInterval, $date); $newDate -= $offset; // to user timezone return new Bizproc\BaseType\Value\DateTime($newDate, $offset); } public function callWorkDateAdd(Arguments $args) { $date = $args->getFirstSingle(); $offset = $this->getDateTimeOffset($date); $paramInterval = $args->getSecondSingle(); $user = $args->getThird(); if ($user) { $userArgs = clone $args; $userArgs->setArgs([$user, $date]); $date = $this->callToUserDate($userArgs); $offset = $this->getDateTimeOffset($date); } $date = $this->makeTimestamp($date, true); if ($date === false || ($paramInterval && !is_scalar($paramInterval))) { return null; } if (empty($paramInterval) || !Main\Loader::includeModule('calendar')) { return $date; } $paramInterval = trim($paramInterval); $multiplier = 1; if (mb_substr($paramInterval, 0, 1) === "-") { $paramInterval = mb_substr($paramInterval, 1); $multiplier = -1; } $workDayInterval = $this->getWorkDayInterval(); $intervalMap = [ "d" => $workDayInterval, "day" => $workDayInterval, "days" => $workDayInterval, "h" => 3600, "hour" => 3600, "hours" => 3600, "i" => 60, "min" => 60, "minute" => 60, "minutes" => 60, ]; $interval = 0; while (preg_match('/\s*([\d]+)\s*([a-z]+)\s*/i', $paramInterval, $match)) { $match2 = mb_strtolower($match[2]); if (array_key_exists($match2, $intervalMap)) { $interval += intval($match[1]) * $intervalMap[$match2]; } $p = mb_strpos($paramInterval, $match[0]); $paramInterval = mb_substr($paramInterval, $p + mb_strlen($match[0])); } if (date('H:i:s', $date) === '00:00:00') { //add start work day seconds $date += $this->getCalendarWorkTime()[0]; } $date = $this->getNearestWorkTime($date, $multiplier); if ($interval) { $days = (int)floor($interval / $workDayInterval); $hours = $interval % $workDayInterval; $remainTimestamp = $this->getWorkDayRemainTimestamp($date, $multiplier); if ($days) { $date = $this->addWorkDay($date, $days * $multiplier); } if ($hours > $remainTimestamp) { $date += $multiplier < 0 ? -$remainTimestamp - 60 : $remainTimestamp + 60; $date = $this->getNearestWorkTime($date, $multiplier) + (($hours - $remainTimestamp) * $multiplier); } else { $date += $multiplier * $hours; } } $date -= $offset; return new Bizproc\BaseType\Value\DateTime($date, $offset); } public function callAddWorkDays(Arguments $args) { $date = $args->getFirstSingle(); $offset = $this->getDateTimeOffset($date); $days = (int)$args->getSecond(); if (($date = $this->makeTimestamp($date)) === false) { return null; } if ($days === 0 || !Main\Loader::includeModule('calendar')) { return $date; } $date = $this->addWorkDay($date, $days); return new Bizproc\BaseType\Value\DateTime($date, $offset); } public function callIsWorkDay(Arguments $args) { if (!Main\Loader::includeModule('calendar')) { return false; } $date = $args->getFirstSingle(); $user = $args->getSecond(); if ($user) { $userArgs = clone $args; $userArgs->setArgs([$user, $date]); $date = $this->callToUserDate($userArgs); } if (($date = $this->makeTimestamp($date, true)) === false) { return false; } return !$this->isHoliday($date); } public function callIsWorkTime(Arguments $args) { if (!Main\Loader::includeModule('calendar')) { return false; } $date = $args->getFirstSingle(); $user = $args->getSecond(); if ($user) { $userArgs = clone $args; $userArgs->setArgs([$user, $date]); $date = $this->callToUserDate($userArgs); } if (($date = $this->makeTimestamp($date, true)) === false) { return false; } return !$this->isHoliday($date) && $this->isWorkTime($date); } public function callDateDiff(Arguments $args) { $date1 = $args->getFirstSingle(); $date2 = $args->getSecondSingle(); $format = $args->getThird(); if (!$date1 || !$date2 || !is_scalar($format)) { return null; } $date1Formatted = $this->getDateTimeObject($date1); $date2Formatted = $this->getDateTimeObject($date2); if ($date1Formatted === false || $date2Formatted === false) { return null; } $interval = $date1Formatted->diff($date2Formatted); return $interval === false ? null : $interval->format($format); } public function callDate(Arguments $args) { $format = $args->getFirst(); $date = $args->getSecondSingle(); if (!$format || !is_string($format)) { return null; } $ts = $date ? $this->makeTimestamp($date, true) : time(); if (!$ts) { return null; } return date($format, $ts); } public function callToUserDate(Arguments $args) { $user = $args->getFirst(); $date = $args->getSecondSingle(); if (!$user) { return null; } if (!$date) { $date = time(); } elseif (($date = $this->makeTimestamp($date)) === false) { return null; } $userId = \CBPHelper::extractFirstUser($user, $args->getParser()->getActivity()->getDocumentId()); $offset = $userId ? \CTimeZone::GetOffset($userId, true) : 0; return new Bizproc\BaseType\Value\DateTime($date, $offset); } public function callGetUserDateOffset(Arguments $args) { $user = $args->getFirst(); if (!$user) { return null; } $userId = \CBPHelper::extractFirstUser($user, $args->getParser()->getActivity()->getDocumentId()); if (!$userId) { return 0; } return \CTimeZone::GetOffset($userId, true); } public function callStrtotime(Arguments $args) { $datetime = $args->getFirstSingle(); $baseDate = $args->getSecondSingle(); $baseTimestamp = $baseDate ? $this->makeTimestamp($baseDate, true) : time(); if (!$baseTimestamp || !is_scalar($datetime)) { return null; } $timestamp = strtotime($datetime, (int)$baseTimestamp); if ($timestamp === false) { return null; } return new Bizproc\BaseType\Value\DateTime($timestamp); } public function callLocDate(Arguments $args) { $format = $args->getFirst(); $date = $args->getSecondSingle(); if (!$format || !is_string($format)) { return null; } $reformFormat = $this->frameSymbolsInDateFormat($format); $timestamp = $date ? $this->makeTimestamp($date, true) : time(); if (!$timestamp) { return null; } $formattedDate = date($reformFormat, $timestamp); // return \FormatDate($format, $timestamp); - TODO if ($formattedDate === false) { return null; } return $this->replaceDateToLocDate($formattedDate, $reformFormat); } public function callSetTime(Arguments $args) { $date = $args->getFirstSingle(); $currentOffset = $this->getDateTimeOffset($date); $hour = max(0, (int)$args->getSecondSingle()); $minute = max(0, (int)$args->getThirdSingle()); $timeOffset = $hour * self::ONE_HOUR + $minute * 60; $baseOffset = max( -1 * self::TWELVE_HOURS, min(self::TWELVE_HOURS, (int)$args->getArgSingle(3)) ); if ($baseOffset !== 0) { $timeOffset -= $baseOffset; } if (($date = $this->makeTimestamp($date, true)) === false) { return null; } $dateTime = Main\Type\DateTime::createFromTimestamp($date); $dateTime->setTime(0, 0, 0, 0); $date = $dateTime->getTimestamp() + $timeOffset; return new Bizproc\BaseType\Value\DateTime($date, $currentOffset); } private function makeTimestamp($date, $appendOffset = false) { if (!$date || (!is_scalar($date) && !is_object($date))) { return false; } //serialized date string if (is_string($date) && Bizproc\BaseType\Value\Date::isSerialized($date)) { $date = new Bizproc\BaseType\Value\Date($date); } if ($date instanceof Bizproc\BaseType\Value\Date) { return $date->getTimestamp() + ($appendOffset ? $date->getOffset() : 0); } if (intval($date) . "!" === $date . "!") { return $date; } if (($result = MakeTimeStamp($date, FORMAT_DATETIME)) === false) { if (($result = MakeTimeStamp($date, FORMAT_DATE)) === false) { if (($result = MakeTimeStamp($date, "YYYY-MM-DD HH:MI:SS")) === false) { $result = MakeTimeStamp($date, "YYYY-MM-DD"); } } } return $result; } private function getWorkDayTimestamp($date) { return date('H', $date) * 3600 + date('i', $date) * 60; } private function getWorkDayRemainTimestamp($date, $multiplier = 1) { $dayTs = $this->getWorkDayTimestamp($date); [$startSeconds, $endSeconds] = $this->getCalendarWorkTime(); return $multiplier < 0 ? $dayTs - $startSeconds : $endSeconds - $dayTs; } private function getWorkDayInterval() { [$startSeconds, $endSeconds] = $this->getCalendarWorkTime(); return $endSeconds - $startSeconds; } private function isHoliday($date) { [$yearWorkdays] = $this->getCalendarWorkdays(); [$weekHolidays, $yearHolidays] = $this->getCalendarHolidays(); $dayOfYear = date('j.n', $date); if (in_array($dayOfYear, $yearWorkdays, true)) { return false; } $dayOfWeek = date('w', $date); if (in_array($dayOfWeek, $weekHolidays)) { return true; } $dayOfYear = date('j.n', $date); if (in_array($dayOfYear, $yearHolidays, true)) { return true; } return false; } private function isWorkTime($date) { $dayTs = $this->getWorkDayTimestamp($date); [$startSeconds, $endSeconds] = $this->getCalendarWorkTime(); return ($dayTs >= $startSeconds && $dayTs <= $endSeconds); } private function getNearestWorkTime($date, $multiplier = 1) { $reverse = $multiplier < 0; [$startSeconds, $endSeconds] = $this->getCalendarWorkTime(); $dayTimeStamp = $this->getWorkDayTimestamp($date); if ($this->isHoliday($date)) { $date -= $dayTimeStamp; $date += $reverse ? -86400 + $endSeconds : $startSeconds; $dayTimeStamp = $reverse ? $endSeconds : $startSeconds; } if (!$this->isWorkTime($date)) { $date -= $dayTimeStamp; if ($dayTimeStamp < $startSeconds) { $date += $reverse ? -86400 + $endSeconds : $startSeconds; } else { $date += $reverse ? $endSeconds : 86400 + $startSeconds; } } if ($this->isHoliday($date)) { $date = $this->addWorkDay($date, $reverse ? -1 : 1); } return $date; } private function addWorkDay($date, $days) { $delta = 86400; if ($days < 0) { $delta *= -1; } $days = abs($days); $iterations = 0; while ($days > 0 && $iterations < 1000) { ++$iterations; $date += $delta; if ($this->isHoliday($date)) { continue; } --$days; } return $date; } private function getCalendarHolidays() { if (static::$yearHolidays === null) { $calendarSettings = \CCalendar::GetSettings(); $weekHolidays = [0, 6]; $yearHolidays = []; if (isset($calendarSettings['week_holidays'])) { $weekDays = ['SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6]; $weekHolidays = []; foreach ($calendarSettings['week_holidays'] as $day) { $weekHolidays[] = $weekDays[$day]; } } if (isset($calendarSettings['year_holidays'])) { foreach (explode(',', $calendarSettings['year_holidays']) as $yearHoliday) { $date = explode('.', trim($yearHoliday)); if (count($date) == 2 && $date[0] && $date[1]) { $yearHolidays[] = (int)$date[0] . '.' . (int)$date[1]; } } } static::$weekHolidays = $weekHolidays; static::$yearHolidays = $yearHolidays; } return [static::$weekHolidays, static::$yearHolidays]; } private function getCalendarWorkTime() { if (static::$startWorkDay === null) { $startSeconds = 0; $endSeconds = 24 * 3600 - 1; $calendarSettings = \CCalendar::GetSettings(); if (!empty($calendarSettings['work_time_start'])) { $time = explode('.', $calendarSettings['work_time_start']); $startSeconds = $time[0] * 3600; if (!empty($time[1])) { $startSeconds += $time[1] * 60; } } if (!empty($calendarSettings['work_time_end'])) { $time = explode('.', $calendarSettings['work_time_end']); $endSeconds = $time[0] * 3600; if (!empty($time[1])) { $endSeconds += $time[1] * 60; } } static::$startWorkDay = $startSeconds; static::$endWorkDay = $endSeconds; } return [static::$startWorkDay, static::$endWorkDay]; } private function getCalendarWorkdays() { if (static::$yearWorkdays === null) { $yearWorkdays = []; $calendarSettings = \CCalendar::GetSettings(); $calendarYearWorkdays = $calendarSettings['year_workdays'] ?? ''; foreach (explode(',', $calendarYearWorkdays) as $yearWorkday) { $date = explode('.', trim($yearWorkday)); if (count($date) === 2 && $date[0] && $date[1]) { $yearWorkdays[] = (int)$date[0] . '.' . (int)$date[1]; } } static::$yearWorkdays = $yearWorkdays; } return [static::$yearWorkdays]; } private function getDateTimeObject($date) { if ($date instanceof Bizproc\BaseType\Value\Date) { return (new \DateTime())->setTimestamp($date->getTimestamp()); } elseif (is_array($date)) { $date = \CBPHelper::flatten($date); } if (!is_scalar($date)) { return false; } $df = Main\Type\DateTime::getFormat(); $df2 = Main\Type\Date::getFormat(); $date1Formatted = \DateTime::createFromFormat($df, $date); if ($date1Formatted === false) { $date1Formatted = \DateTime::createFromFormat($df2, $date); if ($date1Formatted) { $date1Formatted->setTime(0, 0); } } return $date1Formatted; } private function getDateTimeOffset($date) { if ($date instanceof Bizproc\BaseType\Value\Date) { return $date->getOffset(); } return 0; } private function frameSymbolsInDateFormat($format) { $complexSymbols = ['j F', 'd F', 'jS F']; $symbols = ['D', 'l', 'F', 'M', 'r']; $frameRule = []; foreach ($symbols as $symbol) { $frameRule[$symbol] = '#' . $symbol . '#'; $frameRule['\\' . $symbol] = '\\' . $symbol; } foreach ($complexSymbols as $symbol) { $frameRule[$symbol] = substr($symbol, 0, -1) . '#' . $symbol[-1] . '_1#'; $frameRule['\\' . $symbol] = '\\' . substr($symbol, 0, -1) . '#' . $symbol[-1] . '#'; } return strtr($format, $frameRule); } private function frameNamesInFormattedDateRFC2822($formattedDate) { $matches = []; $pattern = "/#(\w{3}), \d{2} (\w{3}) \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4}#/"; if (preg_match_all($pattern, $formattedDate, $matches)) { foreach ($matches[0] as $key => $match) { $day = $matches[1][$key]; $month = $matches[2][$key]; $reformMatch = str_replace( [$day, $month], ['#' . $day . '#', '#' . $month . '#'], $match ); $reformMatch = substr($reformMatch, 1, -1); $formattedDate = str_replace($match, $reformMatch, $formattedDate); } } return $formattedDate; } private function replaceDateToLocDate($formattedDate, $format) { $lenShortName = 3; $dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; $monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; if (strpos($format, '#r#') !== false) { $formattedDate = $this->frameNamesInFormattedDateRFC2822($formattedDate); } $replacementRule = []; foreach (array_merge($dayNames, $monthNames) as $name) { $replacementRule['#' . $name . '#'] = Loc::getMessage( 'BIZPROC_CALC_FUNCTION_LOCDATE_' . strtoupper($name) ); $shortName = substr($name, 0, $lenShortName); $replacementRule['#' . $shortName . '#'] = Loc::getMessage( 'BIZPROC_CALC_FUNCTION_LOCDATE_' . strtoupper($shortName) . '_SHORT' ); } foreach ($monthNames as $monthName) { $replacementRule['#' . $monthName . '_1' . '#'] = Loc::getMessage( 'BIZPROC_CALC_FUNCTION_LOCDATE_' . strtoupper($monthName) . '_1' ); } return strtr($formattedDate, $replacementRule); } }