PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/main/lib/text/dateconverter.php

https://gitlab.com/alexprowars/bitrix
PHP | 1114 lines | 1001 code | 76 blank | 37 comment | 251 complexity | 4870576334a1608fd5e2ab095bc17416 MD5 | raw file
  1. <?php
  2. namespace Bitrix\Main\Text;
  3. use Bitrix\Main\Localization\Loc as Loc;
  4. class DateConverter
  5. {
  6. /**
  7. * Creates Date object from Text (return array of result object)
  8. *
  9. * Examples: "end of next week", "tomorrow morning", "friday 25.10"
  10. *
  11. * @param string $text
  12. * @param integer $limit
  13. * @return \Bitrix\Main\Text\DateConverterResult[]
  14. */
  15. public static function decode($text, $limit = 0)
  16. {
  17. $result = Array();
  18. if ($text == '')
  19. {
  20. return $result;
  21. }
  22. $metrics = Array();
  23. $date = new \Bitrix\Main\Type\DateTime();
  24. if ($limit > 0 && mb_strlen($text) > $limit)
  25. {
  26. $text = mb_substr($text, 0, $limit);
  27. }
  28. $originalText = $text;
  29. $text = mb_strtolower($text);
  30. $workTimeStart = explode('.', \Bitrix\Main\Config\Option::get('calendar', 'work_time_start', '9'));
  31. $timeOfStartDate = str_pad(intval($workTimeStart[0]), 2, "0", STR_PAD_LEFT).':'.str_pad(intval($workTimeStart[1]), 2, "0", STR_PAD_LEFT);
  32. $workTimeEnd = explode('.', \Bitrix\Main\Config\Option::get('calendar', 'work_time_end', '18'));
  33. $timeOfEndDate = str_pad(intval($workTimeEnd[0]), 2, "0", STR_PAD_LEFT).':'.str_pad(intval($workTimeEnd[1]), 2, "0", STR_PAD_LEFT);
  34. $defaultPregExceptionSearch = Array('.', '/', '-', ':');
  35. $defaultPregExceptionReplace = Array('\.', '\/', '\-', '\:');
  36. // metric 1: near date
  37. $pattern = Array();
  38. for ($i = 1; $i <= 11; $i++)
  39. {
  40. $pattern[$i] = mb_strtolower(Loc::getMessage("MAIN_TDC_METRIC_1_".$i));
  41. $pattern[$i] = $pattern[$i] <> ''? $pattern[$i]: 'bxt2dmetricskip';
  42. $pattern[$i] = str_replace($defaultPregExceptionSearch, $defaultPregExceptionReplace, $pattern[$i]);
  43. }
  44. if (preg_match_all("/(".implode('|', $pattern).")/", $text, $match, PREG_OFFSET_CAPTURE))
  45. {
  46. foreach ($match[0] as $matchPattern)
  47. {
  48. $matchType = '';
  49. switch($matchPattern[0])
  50. {
  51. case $pattern[1]:
  52. $matchType = 'DAYAFTERTOMORROW';
  53. break;
  54. case $pattern[2]:
  55. $matchType = 'TOMORROW';
  56. break;
  57. case $pattern[3]:
  58. $matchType = 'TODAY';
  59. break;
  60. case $pattern[4]:
  61. $matchType = 'YESTERDAY';
  62. break;
  63. case $pattern[5]:
  64. $matchType = 'DAYBEFOREYESTERDAY';
  65. break;
  66. case $pattern[6]:
  67. case $pattern[7]:
  68. case $pattern[8]:
  69. $matchType = '';
  70. break;
  71. case $pattern[9]:
  72. $matchType = 'WEEK';
  73. break;
  74. case $pattern[10]:
  75. $matchType = 'WEEKEND';
  76. break;
  77. case $pattern[11]:
  78. $matchType = 'MONTH';
  79. break;
  80. }
  81. if ($matchType <> '')
  82. {
  83. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  84. $matchWord = self::getMatchWord($originalText, $position);
  85. $metrics[1][] = Array(
  86. 'TYPE' => $matchType,
  87. 'COUNT' => mb_strlen($matchWord),
  88. 'POSITION' => $position,
  89. 'MATCH' => $matchWord
  90. );
  91. }
  92. }
  93. }
  94. // metric 2: day of weeks
  95. $pattern = Array();
  96. for ($i = 1; $i <= 10; $i++)
  97. {
  98. $pattern[$i] = mb_strtolower(Loc::getMessage("MAIN_TDC_METRIC_2_".$i));
  99. $pattern[$i] = $pattern[$i] <> ''? $pattern[$i]: 'bxt2dmetricskip';
  100. $pattern[$i] = str_replace($defaultPregExceptionSearch, $defaultPregExceptionReplace, $pattern[$i]);
  101. }
  102. if (preg_match_all("/(".implode('|', $pattern).")/", $text, $match, PREG_OFFSET_CAPTURE))
  103. {
  104. foreach ($match[0] as $matchPattern)
  105. {
  106. $matchType = '';
  107. switch($matchPattern[0])
  108. {
  109. case $pattern[1]:
  110. $matchType = 'MONDAY';
  111. if (is_array($metrics[1]))
  112. {
  113. foreach ($metrics[1] as $key => $metric)
  114. {
  115. if ($metric['TYPE'] == 'WEEK')
  116. {
  117. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  118. $matchWord = self::getMatchWord($originalText, $position);
  119. if ($matchPattern[1] < $metric['POSITION'] && $matchPattern[1] + mb_strlen($matchWord) >= $metric['POSITION'] + $metric['COUNT'])
  120. {
  121. unset($metrics[1][$key]);
  122. if (count($metrics[1]) == 0)
  123. {
  124. unset($metrics[1]);
  125. }
  126. }
  127. }
  128. }
  129. }
  130. break;
  131. case $pattern[2]:
  132. $matchType = 'TUESDAY';
  133. break;
  134. case $pattern[3]:
  135. case $pattern[4]:
  136. case $pattern[5]:
  137. $matchType = 'WEDNESDAY';
  138. break;
  139. case $pattern[6]:
  140. $matchType = 'THURSDAY';
  141. break;
  142. case $pattern[7]:
  143. $matchType = 'FRIDAY';
  144. break;
  145. case $pattern[8]:
  146. $matchType = 'SATURDAY';
  147. break;
  148. case $pattern[9]:
  149. case $pattern[10]:
  150. $matchType = 'SUNDAY';
  151. break;
  152. }
  153. if ($matchType <> '')
  154. {
  155. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  156. $matchWord = self::getMatchWord($originalText, $position);
  157. $metrics[2][] = Array(
  158. 'TYPE' => $matchType,
  159. 'COUNT' => mb_strlen($matchWord),
  160. 'POSITION' => $position,
  161. 'MATCH' => $matchWord
  162. );
  163. }
  164. }
  165. }
  166. // metric 3: modificators before/after and time
  167. $pattern = Array();
  168. for ($i = 1; $i <= 9; $i++)
  169. {
  170. $pattern[$i] = mb_strtolower(Loc::getMessage("MAIN_TDC_METRIC_3_".$i));
  171. $pattern[$i] = $pattern[$i] <> ''? $pattern[$i]: 'bxt2dmetricskip';
  172. $pattern[$i] = str_replace($defaultPregExceptionSearch, $defaultPregExceptionReplace, $pattern[$i]);
  173. }
  174. if (preg_match_all("/(".implode('|', $pattern).")/", $text, $match, PREG_OFFSET_CAPTURE))
  175. {
  176. foreach ($match[0] as $matchPattern)
  177. {
  178. $matchType = '';
  179. switch($matchPattern[0])
  180. {
  181. case $pattern[1]:
  182. $matchType = 'BEFORE';
  183. break;
  184. case $pattern[2]:
  185. $matchType = 'AFTER';
  186. break;
  187. case $pattern[3]:
  188. $matchType = 'NEXT';
  189. break;
  190. case $pattern[4]:
  191. $matchType = 'PREVIOUS';
  192. break;
  193. case $pattern[5]:
  194. case $pattern[6]:
  195. $matchType = 'ENDOF';
  196. break;
  197. case $pattern[7]:
  198. $matchType = 'MIDDLEOF';
  199. break;
  200. case $pattern[8]:
  201. $matchType = 'STARTOF';
  202. break;
  203. case $pattern[9]:
  204. case $pattern[10]:
  205. $matchType = 'THIS';
  206. break;
  207. }
  208. if (in_array($matchType, Array('BEFORE', 'AFTER')) && isset($metrics[1]))
  209. {
  210. foreach ($metrics[1] as $key => $metric)
  211. {
  212. if (in_array($metric['TYPE'], Array('DAYAFTERTOMORROW', 'DAYBEFOREYESTERDAY')))
  213. {
  214. if ($metric['POSITION'] == $matchPattern[1])
  215. {
  216. $matchType = '';
  217. }
  218. }
  219. else if (in_array($metric['TYPE'], Array('TOMORROW', 'YESTERDAY')))
  220. {
  221. if ($metric['POSITION'] == $matchPattern[1] + mb_strlen($matchPattern[0])+1)
  222. {
  223. if ($metric['TYPE'] == 'TOMORROW')
  224. {
  225. $metrics[1][$key]['TYPE'] = $matchType == 'AFTER'? 'DAYAFTERTOMORROW': 'TODAY';
  226. }
  227. else
  228. {
  229. $metrics[1][$key]['TYPE'] = $matchType == 'AFTER'? 'TODAY': 'DAYBEFOREYESTERDAY';
  230. }
  231. $matchType = '';
  232. }
  233. }
  234. }
  235. }
  236. if ($matchType <> '')
  237. {
  238. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  239. $matchWord = self::getMatchWord($originalText, $position);
  240. $metrics[3][] = Array(
  241. 'TYPE' => $matchType,
  242. 'COUNT' => mb_strlen($matchWord),
  243. 'POSITION' => $position,
  244. 'MATCH' => $matchWord
  245. );
  246. }
  247. }
  248. }
  249. if (preg_match_all("/([0-2][0-9]:[0-5][0-9])/", $text, $match, PREG_OFFSET_CAPTURE))
  250. {
  251. foreach ($match[0] as $matchPattern)
  252. {
  253. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  254. $matchWord = self::getMatchWord($originalText, $position);
  255. $metrics[3][] = Array(
  256. 'TYPE' => 'HHMM',
  257. 'VALUE' => $matchPattern[0],
  258. 'COUNT' => mb_strlen($matchPattern[0]),
  259. 'POSITION' => $position,
  260. 'MATCH' => $matchWord
  261. );
  262. }
  263. }
  264. // metric 4: date
  265. $pattern = Array();
  266. $patternOriginal = Array();
  267. $patternLength = Array();
  268. for ($i = 1; $i <= 3; $i++)
  269. {
  270. $patternOriginal[$i] = mb_strtolower(Loc::getMessage("MAIN_TDC_METRIC_4_".$i));
  271. $patternOriginal[$i] = $patternOriginal[$i] <> ''? $patternOriginal[$i]: 'bxt2dmetricskip';
  272. $patternLength[$i] = mb_strlen($patternOriginal[$i]);
  273. if ($patternOriginal[$i] != 'bxt2dmetricskip')
  274. {
  275. $pattern[$i] = str_replace($defaultPregExceptionSearch, $defaultPregExceptionReplace, $patternOriginal[$i]);
  276. $pattern[$i] = str_replace(
  277. Array('yyyy', 'yy', 'mm', 'dd'),
  278. Array('[1-2][0-9][0-9][0-9]', '[0-9][0-9]', '[0-1][0-9]', '[0-3][0-9]'),
  279. $pattern[$i]
  280. );
  281. $pattern[$i] = '^(?>[\s|\t]?)('.$pattern[$i].')|[\s|^|\t]('.$pattern[$i].')(?=[\s|$|\r|\t])';
  282. }
  283. }
  284. if (preg_match_all("/".implode('|', $pattern)."/", $text, $match, PREG_OFFSET_CAPTURE))
  285. {
  286. $count = count($match)-1;
  287. for ($i = 1; $i <= $count; $i++)
  288. {
  289. foreach ($match[$i] as $matchPattern)
  290. {
  291. if (!$matchPattern[0])
  292. {
  293. continue;
  294. }
  295. $matchType = '';
  296. $matchLength = '';
  297. switch(mb_strlen($matchPattern[0]))
  298. {
  299. case $patternLength[1]:
  300. $matchType = 'DDMMYYYY';
  301. $matchYear = mb_substr($matchPattern[0], mb_strpos($patternOriginal[1], 'yyyy'), 4);
  302. $matchMonth = mb_substr($matchPattern[0], mb_strpos($patternOriginal[1], 'mm'), 2);
  303. $matchDay = mb_substr($matchPattern[0], mb_strpos($patternOriginal[1], 'dd'), 2);
  304. $matchPattern[0] = $matchYear.'-'.$matchMonth.'-'.$matchDay;
  305. $matchLength = $patternLength[1];
  306. break;
  307. case $patternLength[2]:
  308. $matchType = 'DDMMYY';
  309. $matchYear = mb_substr($matchPattern[0], mb_strpos($patternOriginal[2], 'yy'), 2);
  310. $matchMonth = mb_substr($matchPattern[0], mb_strpos($patternOriginal[2], 'mm'), 2);
  311. $matchDay = mb_substr($matchPattern[0], mb_strpos($patternOriginal[2], 'dd'), 2);
  312. $matchPattern[0] = '20'.$matchYear.'-'.$matchMonth.'-'.$matchDay;
  313. $matchLength = $patternLength[2];
  314. break;
  315. case $patternLength[3]:
  316. $matchType = 'DDMM';
  317. $matchMonth = mb_substr($matchPattern[0], mb_strpos($patternOriginal[3], 'mm'), 2);
  318. $matchDay = mb_substr($matchPattern[0], mb_strpos($patternOriginal[3], 'dd'), 2);
  319. $matchPattern[0] = $date->format('Y').'-'.$matchMonth.'-'.$matchDay;
  320. $matchLength = $patternLength[3];
  321. break;
  322. }
  323. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  324. $matchWord = mb_substr($originalText, $position, $matchLength);
  325. $metrics[4][] = Array(
  326. 'TYPE' => $matchType,
  327. 'VALUE' => $matchPattern[0],
  328. 'COUNT' => $matchLength,
  329. 'POSITION' => $position,
  330. 'MATCH' => $matchWord
  331. );
  332. }
  333. }
  334. }
  335. // metric 5: modificators of time
  336. $pattern = Array();
  337. for ($i = 1; $i <= 5; $i++)
  338. {
  339. $pattern[$i] = mb_strtolower(Loc::getMessage("MAIN_TDC_METRIC_5_".$i));
  340. $pattern[$i] = $pattern[$i] <> ''? $pattern[$i]: 'bxt2dmetricskip';
  341. $pattern[$i] = str_replace($defaultPregExceptionSearch, $defaultPregExceptionReplace, $pattern[$i]);
  342. }
  343. if (preg_match_all("/(".implode('|', $pattern).")/", $text, $match, PREG_OFFSET_CAPTURE))
  344. {
  345. foreach ($match[0] as $matchPattern)
  346. {
  347. $matchType = '';
  348. $matchValue = '';
  349. switch($matchPattern[0])
  350. {
  351. case $pattern[1]:
  352. $matchType = 'MORNING';
  353. $matchValue = $timeOfStartDate;
  354. break;
  355. case $pattern[2]:
  356. $matchType = 'LUNCH';
  357. $matchValue = '14:00';
  358. break;
  359. case $pattern[3]:
  360. break;
  361. case $pattern[4]:
  362. $matchType = 'WORKDAY';
  363. $matchValue = $timeOfEndDate;
  364. if (isset($metrics[1]))
  365. {
  366. foreach ($metrics[1] as $key => $metric)
  367. {
  368. if ($metric['TYPE'] == 'TODAY')
  369. {
  370. if ($metric['POSITION'] == $matchPattern[1] + mb_strlen($matchPattern[3]))
  371. {
  372. $matchType = '';
  373. }
  374. }
  375. }
  376. }
  377. break;
  378. case $pattern[5]:
  379. $matchType = 'EVENING';
  380. $matchValue = '20:00';
  381. break;
  382. }
  383. if ($matchType <> '')
  384. {
  385. $position = defined("BX_UTF")? mb_strlen(substr($text, 0, $matchPattern[1])) : $matchPattern[1];
  386. $matchWord = self::getMatchWord($originalText, $position);
  387. $metrics[5][] = Array(
  388. 'TYPE' => $matchType,
  389. 'VALUE' => $matchValue,
  390. 'COUNT' => mb_strlen($matchWord),
  391. 'POSITION' => $position,
  392. 'MATCH' => $matchWord
  393. );
  394. }
  395. }
  396. }
  397. $countOfMetrics = 0;
  398. foreach ($metrics as $values)
  399. {
  400. if (count($values) > 0)
  401. {
  402. $countOfMetrics++;
  403. }
  404. }
  405. $useDefault = false;
  406. if ($countOfMetrics == 1 && (isset($metrics[3]) || isset($metrics[5])))
  407. {}
  408. else if ($countOfMetrics == 1)
  409. {
  410. $useDefault = true;
  411. }
  412. else if ($countOfMetrics == 2 && isset($metrics[3]))
  413. {
  414. $useDefault = !isset($metrics[5]);
  415. }
  416. else if ($countOfMetrics == 2 && isset($metrics[5]) && (isset($metrics[1]) || isset($metrics[2]) || isset($metrics[4])))
  417. {
  418. if (isset($metrics[1]))
  419. {
  420. foreach ($metrics[1] as $metric)
  421. {
  422. $progressDate = self::createDateUsingMetrics(1, $metric, $metrics[5]);
  423. $matchParams = self::getTextForReplace($originalText, $metric, $metrics[5]);
  424. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_RELATIVE, $metric, $metrics[5]);
  425. }
  426. }
  427. else if (isset($metrics[2]))
  428. {
  429. foreach ($metrics[2] as $metric)
  430. {
  431. $progressDate = self::createDateUsingMetrics(2, $metric, $metrics[5]);
  432. $matchParams = self::getTextForReplace($originalText, $metric, $metrics[5]);
  433. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_DAYOFWEEK, $metric, $metrics[5]);
  434. }
  435. }
  436. else if (isset($metrics[4]))
  437. {
  438. foreach ($metrics[4] as $metric)
  439. {
  440. $progressDate = self::createDateUsingMetrics(4, $metric, $metrics[5]);
  441. $matchParams = self::getTextForReplace($originalText, $metric, $metrics[5]);
  442. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_CALENDAR, $metric, $metrics[5]);
  443. }
  444. }
  445. }
  446. else if ($countOfMetrics == 2 && isset($metrics[1]) && (isset($metrics[2]) || isset($metrics[4])))
  447. {
  448. if (isset($metrics[2]))
  449. {
  450. foreach ($metrics[2] as $metric)
  451. {
  452. $array = array();
  453. $progressDate = self::createDateUsingMetrics(2, $metric, $array);
  454. $matchParams = self::getTextForReplace($originalText, $metric, array());
  455. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_DAYOFWEEK, $metric);
  456. }
  457. }
  458. elseif (isset($metrics[4]))
  459. {
  460. foreach ($metrics[4] as $metric)
  461. {
  462. $array = array();
  463. $progressDate = self::createDateUsingMetrics(4, $metric, $array);
  464. $matchParams = self::getTextForReplace($originalText, $metric, array());
  465. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_CALENDAR, $metric);
  466. }
  467. }
  468. }
  469. else
  470. {
  471. $useDefault = true;
  472. }
  473. if ($useDefault)
  474. {
  475. $modificators = isset($metrics[3])? $metrics[3]: array();
  476. if (isset($metrics[4]) && count($metrics[4]))
  477. {
  478. foreach ($metrics[4] as $metric)
  479. {
  480. $progressDate = self::createDateUsingMetrics(4, $metric, $modificators);
  481. $matchParams = self::getTextForReplace($originalText, $metric, $modificators);
  482. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_CALENDAR, $metric, $modificators);
  483. }
  484. }
  485. else if (isset($metrics[2]) && count($metrics[2]))
  486. {
  487. foreach ($metrics[2] as $metric)
  488. {
  489. $progressDate = self::createDateUsingMetrics(2, $metric, $modificators);
  490. $matchParams = self::getTextForReplace($originalText, $metric, $modificators);
  491. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_DAYOFWEEK, $metric, $modificators);
  492. }
  493. }
  494. else if (isset($metrics[1]) && count($metrics[1]))
  495. {
  496. foreach ($metrics[1] as $metric)
  497. {
  498. $progressDate = self::createDateUsingMetrics(1, $metric, $modificators);
  499. $matchParams = self::getTextForReplace($originalText, $metric, $modificators);
  500. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_RELATIVE, $metric, $modificators);
  501. }
  502. }
  503. else if (isset($metrics[5]) && count($metrics[5]))
  504. {
  505. foreach ($metrics[5] as $metric)
  506. {
  507. $progressDate = self::createDateUsingMetrics(5, $metric, $modificators);
  508. $matchParams = self::getTextForReplace($originalText, $metric, $modificators);
  509. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_PARTOFDAY, $metric, $modificators);
  510. }
  511. }
  512. else if (isset($metrics[3]) && count($metrics[3]))
  513. {
  514. foreach ($metrics[3] as $metric)
  515. {
  516. $array = array();
  517. $progressDate = self::createDateUsingMetrics(3, $metric, $array);
  518. $matchParams = self::getTextForReplace($originalText, $metric, $modificators);
  519. $result[] = new DateConverterResult($progressDate, $matchParams, DateConverterResult::TYPE_MODIFIER, $metric);
  520. }
  521. }
  522. }
  523. return $result;
  524. }
  525. /**
  526. * Creates Date object from metrics (private method for self::decode)
  527. *
  528. * @param int $type Type of metric
  529. * @param array $metric Params of metric
  530. * @param array $metricModificator List of metric modificators
  531. * @param \Bitrix\Main\Type\DateTime $date If not specified, the modified date is got from the current date.
  532. * @return \Bitrix\Main\Type\DateTime
  533. *
  534. */
  535. private static function createDateUsingMetrics($type, $metric, &$metricModificator = array(), \Bitrix\Main\Type\DateTime $date = null)
  536. {
  537. $defaultDate = $date? $date: new \Bitrix\Main\Type\DateTime();
  538. $metricModificator = is_array($metricModificator)? $metricModificator: array();
  539. $workTimeStart = explode('.', \Bitrix\Main\Config\Option::get('calendar', 'work_time_start', '9'));
  540. $timeOfStartDate = str_pad(intval($workTimeStart[0]), 2, "0", STR_PAD_LEFT).':'.str_pad(intval($workTimeStart[1]), 2, "0", STR_PAD_LEFT);
  541. $workTimeEnd = explode('.', \Bitrix\Main\Config\Option::get('calendar', 'work_time_end', '18'));
  542. $timeOfEndDate = str_pad(intval($workTimeEnd[0]), 2, "0", STR_PAD_LEFT).':'.str_pad(intval($workTimeEnd[1]), 2, "0", STR_PAD_LEFT);
  543. $result = null;
  544. switch($type)
  545. {
  546. case 1:
  547. $metricDate = $defaultDate;
  548. switch($metric['TYPE'])
  549. {
  550. case 'DAYAFTERTOMORROW':
  551. $metricDate->add('TOMORROW')->add('TOMORROW');
  552. break;
  553. case 'TOMORROW':
  554. $metricDate->add('TOMORROW');
  555. break;
  556. case 'YESTERDAY':
  557. $metricDate->add('YESTERDAY');
  558. break;
  559. case 'DAYBEFOREYESTERDAY':
  560. $metricDate->add('YESTERDAY')->add('YESTERDAY');
  561. break;
  562. }
  563. $modificator = '';
  564. $metricTime = $timeOfEndDate;
  565. $metricModificator = self::checkModifierPosition($metric, $metricModificator);
  566. foreach ($metricModificator as $currentModificator)
  567. {
  568. if ($metric['TYPE'] == 'WEEK')
  569. {
  570. if (in_array($currentModificator['TYPE'], Array('BEFORE', 'PREVIOUS')))
  571. {
  572. $metricDate->add('-1 WEEK');
  573. }
  574. else if (in_array($currentModificator['TYPE'], Array('AFTER', 'NEXT')))
  575. {
  576. $metricDate->add('1 WEEK');
  577. }
  578. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  579. {
  580. $metricDate = self::getDateOfDayOfCurrentWeek('WEDNESDAY', $metricDate);
  581. $modificator = true;
  582. }
  583. else if ($currentModificator['TYPE'] == 'STARTOF')
  584. {
  585. $metricDate = self::getDateOfDayOfCurrentWeek('MONDAY', $metricDate);
  586. $modificator = true;
  587. }
  588. else if ($currentModificator['TYPE'] == 'ENDOF')
  589. {
  590. $metricDate = self::getDateOfDayOfCurrentWeek('FRIDAY', $metricDate);
  591. $modificator = true;
  592. }
  593. }
  594. else if ($metric['TYPE'] == 'WEEKEND')
  595. {
  596. if (in_array($currentModificator['TYPE'], Array('BEFORE')))
  597. {
  598. $metricDate = self::getDateOfDayOfCurrentWeek('SATURDAY', $metricDate);
  599. $metricDate->add('-1 DAY');
  600. $modificator = true;
  601. }
  602. else if (in_array($currentModificator['TYPE'], Array('PREVIOUS')))
  603. {
  604. $metricDate->add('-1 WEEK');
  605. $metricDate = self::getDateOfDayOfCurrentWeek('SATURDAY', $metricDate);
  606. $modificator = true;
  607. }
  608. else if (in_array($currentModificator['TYPE'], Array('NEXT')))
  609. {
  610. $metricDate->add('1 WEEK');
  611. $metricDate = self::getDateOfDayOfCurrentWeek('SATURDAY', $metricDate);
  612. $modificator = true;
  613. }
  614. else if (in_array($currentModificator['TYPE'], Array('AFTER')))
  615. {
  616. $metricDate = self::getDateOfDayOfCurrentWeek('SUNDAY', $metricDate);
  617. $metricDate->add('1 DAY');
  618. $modificator = true;
  619. }
  620. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  621. {
  622. $metricDate = self::getDateOfDayOfCurrentWeek('SATURDAY', $metricDate);
  623. $modificator = true;
  624. }
  625. else if ($currentModificator['TYPE'] == 'STARTOF')
  626. {
  627. $metricDate = self::getDateOfDayOfCurrentWeek('SATURDAY', $metricDate);
  628. $metricTime = $timeOfStartDate;
  629. $modificator = true;
  630. }
  631. else if ($currentModificator['TYPE'] == 'ENDOF')
  632. {
  633. $metricDate = self::getDateOfDayOfCurrentWeek('SUNDAY', $metricDate);
  634. $modificator = true;
  635. }
  636. }
  637. else if ($metric['TYPE'] == 'MONTH')
  638. {
  639. if (in_array($currentModificator['TYPE'], Array('BEFORE', 'PREVIOUS')))
  640. {
  641. $metricDate->add('-1 MONTH');
  642. $numberOfMonth = $metricDate->format('t');
  643. $metricDate = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m').'-'.$numberOfMonth.' '.$timeOfEndDate.':00', 'Y-m-d H:i:s');
  644. $modificator = true;
  645. }
  646. else if (in_array($currentModificator['TYPE'], Array('AFTER', 'NEXT')))
  647. {
  648. $metricDate->add('1 MONTH');
  649. $numberOfMonth = $metricDate->format('t');
  650. $metricDate = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m').'-'.$numberOfMonth.' '.$timeOfEndDate.':00', 'Y-m-d H:i:s');
  651. $modificator = true;
  652. }
  653. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  654. {
  655. $numberOfHalfMonth = ceil($metricDate->format('t')/2);
  656. $metricDate = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m').'-'.$numberOfHalfMonth.' '.$timeOfEndDate.':00', 'Y-m-d H:i:s');
  657. $modificator = true;
  658. }
  659. else if ($currentModificator['TYPE'] == 'STARTOF')
  660. {
  661. $metricDate = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m').'-01 '.$timeOfStartDate.':00', 'Y-m-d H:i:s');
  662. $modificator = true;
  663. }
  664. else if ($currentModificator['TYPE'] == 'ENDOF')
  665. {
  666. $numberOfMonth = $metricDate->format('t');
  667. $metricDate = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m').'-'.$numberOfMonth.' '.$timeOfEndDate.':00', 'Y-m-d H:i:s');
  668. $modificator = true;
  669. }
  670. }
  671. else
  672. {
  673. if (in_array($currentModificator['TYPE'], Array('BEFORE', 'PREVIOUS')))
  674. {
  675. $metricDate->add('-1 DAY');
  676. }
  677. else if (in_array($currentModificator['TYPE'], Array('AFTER', 'NEXT')))
  678. {
  679. $metricDate->add('1 DAY');
  680. }
  681. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  682. {
  683. $metricTime = '14:00';
  684. }
  685. else if ($currentModificator['TYPE'] == 'STARTOF')
  686. {
  687. $metricTime = $timeOfStartDate;
  688. }
  689. $modificator = true;
  690. }
  691. if (in_array($currentModificator['TYPE'], Array('MORNING', 'LUNCH', 'EVENING', 'HHMM')))
  692. {
  693. $metricTime = $currentModificator['VALUE'];
  694. $modificator = true;
  695. }
  696. }
  697. if (!$modificator)
  698. {
  699. if ($metric['TYPE'] == 'WEEK')
  700. {
  701. if ($metricDate->format('N') == 3)
  702. {
  703. $metricDate->add('FRIDAY');
  704. }
  705. else if ($metricDate->format('N') < 3)
  706. {
  707. $metricDate->add('THURSDAY');
  708. }
  709. else if ($metricDate->format('N') != 7)
  710. {
  711. $metricDate->add('SUNDAY');
  712. }
  713. }
  714. else if ($metric['TYPE'] == 'WEEKEND')
  715. {
  716. if (strtolower($metricDate->format('l')) == 'saturday')
  717. $metricDate->add('SUNDAY');
  718. else if (strtolower($metricDate->format('l')) != 'sunday')
  719. $metricDate->add('SATURDAY');
  720. }
  721. else if ($metric['TYPE'] == 'MONTH')
  722. {
  723. $numberOfMonth = $metricDate->format('j');
  724. if ($numberOfMonth > 15)
  725. {
  726. $plus = $metricDate->format('t')-$numberOfMonth;
  727. if ($plus >= 1)
  728. {
  729. $metricDate->add($plus.' DAY');
  730. }
  731. }
  732. else if ($numberOfMonth >= 10 && $numberOfMonth <= 15)
  733. {
  734. $plus = 20-$numberOfMonth;
  735. $metricDate->add($plus.' DAY');
  736. }
  737. else
  738. {
  739. $plus = 15-$numberOfMonth;
  740. $metricDate->add($plus.' DAY');
  741. }
  742. }
  743. }
  744. $result = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m-d').' '.$metricTime.':00', 'Y-m-d H:i:s');
  745. break;
  746. case 2:
  747. $metricDate = $defaultDate;
  748. $modificator = '';
  749. $metricTime = $timeOfEndDate;
  750. $metricModificator = self::checkModifierPosition($metric, $metricModificator);
  751. foreach ($metricModificator as $currentModificator)
  752. {
  753. $metricDate = self::getDateOfDayOfCurrentWeek($metric['TYPE'], $metricDate);
  754. if ($currentModificator['TYPE'] == 'BEFORE')
  755. {
  756. $metricDate->add('-1 DAY');
  757. }
  758. else if ($currentModificator['TYPE'] == 'AFTER')
  759. {
  760. $metricDate->add('1 DAY');
  761. }
  762. else if ($currentModificator['TYPE'] == 'THIS')
  763. {
  764. $metricDate->add('LAST '.$metric['TYPE']);
  765. }
  766. else if (in_array($currentModificator['TYPE'], Array('NEXT', 'PREVIOUS')))
  767. {
  768. if ($currentModificator['TYPE'] == 'NEXT')
  769. {
  770. $metricDate->add('NEXT '.$metric['TYPE']);
  771. }
  772. else
  773. {
  774. $metricDate->add('PREVIOUS '.$metric['TYPE']);
  775. }
  776. }
  777. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  778. {
  779. $metricTime = '14:00';
  780. }
  781. else if ($currentModificator['TYPE'] == 'STARTOF')
  782. {
  783. $metricTime = $timeOfStartDate;
  784. }
  785. else if (in_array($currentModificator['TYPE'], Array('MORNING', 'LUNCH', 'EVENING', 'HHMM')))
  786. {
  787. $metricTime = $currentModificator['VALUE'];
  788. }
  789. $modificator = true;
  790. }
  791. if (!$modificator)
  792. {
  793. $modificator = strtolower($metric['TYPE']) == strtolower($metricDate->format('l'))? 'NEXT ': '';
  794. $metricDate->add($modificator.$metric['TYPE']);
  795. }
  796. $result = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m-d').' '.$metricTime.':00', 'Y-m-d H:i:s');
  797. break;
  798. case 3:
  799. $metricDate = $defaultDate;
  800. $metricTime = $timeOfEndDate;
  801. switch($metric['TYPE'])
  802. {
  803. case 'BEFORE':
  804. $metricDate->add('TOMORROW')->add('TOMORROW');
  805. break;
  806. case 'AFTER':
  807. $metricDate->add('TOMORROW');
  808. break;
  809. case 'NEXT':
  810. $metricDate->add('YESTERDAY');
  811. break;
  812. case 'PREVIOUS':
  813. $metricDate->add('YESTERDAY')->add('YESTERDAY');
  814. break;
  815. case 'MIDDLEOF':
  816. $metricTime = '14:00';
  817. break;
  818. case 'STARTOF':
  819. $metricTime = $timeOfStartDate;
  820. break;
  821. case 'HHMM':
  822. $metricTime = $metric['VALUE'];
  823. break;
  824. }
  825. $result = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m-d').' '.$metricTime.':00', 'Y-m-d H:i:s');
  826. break;
  827. case 4:
  828. $modificator = '';
  829. $metricTime = $timeOfEndDate;
  830. $metricModificator = self::checkModifierPosition($metric, $metricModificator);
  831. foreach ($metricModificator as $currentModificator)
  832. {
  833. if (in_array($currentModificator['TYPE'], Array('MORNING', 'LUNCH', 'EVENING', 'HHMM')))
  834. {
  835. $metricTime = $currentModificator['VALUE'];
  836. }
  837. }
  838. $result = new \Bitrix\Main\Type\DateTime($metric['VALUE'].' '.$metricTime.':00', 'Y-m-d H:i:s');
  839. break;
  840. case 5:
  841. $metricDate = $defaultDate;
  842. $modificator = '';
  843. $metricTime = $metric['VALUE'];
  844. $metricModificator = self::checkModifierPosition($metric, $metricModificator);
  845. foreach ($metricModificator as $currentModificator)
  846. {
  847. if ($currentModificator['TYPE'] == 'BEFORE')
  848. {
  849. $metricDate->add('-1 DAY');
  850. }
  851. else if ($currentModificator['TYPE'] == 'AFTER')
  852. {
  853. $metricDate->add('1 DAY');
  854. }
  855. else if (in_array($currentModificator['TYPE'], Array('NEXT', 'PREVIOUS')))
  856. {
  857. if ($currentModificator['TYPE'] == 'NEXT')
  858. {
  859. $metricDate->add('NEXT '.$metric['TYPE']);
  860. }
  861. else
  862. {
  863. $metricDate->add('PREVIOUS '.$metric['TYPE']);
  864. }
  865. }
  866. else if ($currentModificator['TYPE'] == 'MIDDLEOF')
  867. {
  868. $metricTime = '14:00';
  869. }
  870. else if ($currentModificator['TYPE'] == 'STARTOF')
  871. {
  872. $metricTime = $timeOfStartDate;
  873. }
  874. else if (in_array($currentModificator['TYPE'], Array('MORNING', 'LUNCH', 'EVENING', 'HHMM')))
  875. {
  876. $metricTime = $currentModificator['VALUE'];
  877. }
  878. $modificator = true;
  879. }
  880. if (!$modificator)
  881. {
  882. $modificator = strtolower($metric['TYPE']) == strtolower($metricDate->format('l'))? 'NEXT ': '';
  883. $metricDate->add($modificator.$metric['TYPE']);
  884. }
  885. $result = new \Bitrix\Main\Type\DateTime($metricDate->format('Y-m-d').' '.$metricTime.':00', 'Y-m-d H:i:s');
  886. break;
  887. }
  888. return $result;
  889. }
  890. /**
  891. * Creates Date object of the day of the method week (private function for self::createDateUsingMetrics)
  892. *
  893. * @param string $nameOfday name of the day (monday, tuesday, etc)
  894. * @param \Bitrix\Main\Type\DateTime $date If not specified, the modified date is got from the current date.
  895. * @return \Bitrix\Main\Type\DateTime
  896. *
  897. */
  898. private static function getDateOfDayOfCurrentWeek($nameOfday, \Bitrix\Main\Type\DateTime $date = null)
  899. {
  900. $date = $date? $date: new \Bitrix\Main\Type\DateTime();
  901. $nameOfday = mb_strtoupper($nameOfday);
  902. if (!in_array($nameOfday, Array('MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY')))
  903. return false;
  904. $relation = '';
  905. $day = $date->format('N');
  906. if (
  907. $nameOfday == 'MONDAY' && $day == 1 ||
  908. $nameOfday == 'TUESDAY' && $day <= 2 ||
  909. $nameOfday == 'WEDNESDAY' && $day <= 3 ||
  910. $nameOfday == 'THURSDAY' && $day <= 4 ||
  911. $nameOfday == 'FRIDAY' && $day <= 5 ||
  912. $nameOfday == 'SATURDAY' && $day <= 6 ||
  913. $nameOfday == 'SUNDAY' && $day <= 7
  914. )
  915. {
  916. $relation = 'THIS';
  917. }
  918. $date->add($relation.' '.$nameOfday);
  919. return $date;
  920. }
  921. /**
  922. * @param $text
  923. * @param $position
  924. * @return string
  925. */
  926. private static function getMatchWord($text, $position)
  927. {
  928. $letters = array_merge(Array(" ", "\n", "\t", "\r"), str_split('"-.,?!#$%^&*();:<>\|{}-=^@[]`'));
  929. $spaceFound = self::findFirstOccurrence($text, $letters, $position);
  930. if ($spaceFound !== false)
  931. {
  932. $result = mb_substr($text, $position, $spaceFound - $position);
  933. }
  934. else
  935. {
  936. $result = mb_substr($text, $position);
  937. }
  938. return $result;
  939. }
  940. private static function getTextForReplace($text, $metrics, $metricModifier)
  941. {
  942. $found = false;
  943. $minStartPosition = null;
  944. $maxEndPosition = null;
  945. if (!empty($metrics))
  946. {
  947. $minStartPosition = $metrics['POSITION'];
  948. $maxEndPosition = $metrics['POSITION'] + $metrics['COUNT'];
  949. $found = true;
  950. }
  951. if (is_array($metricModifier))
  952. {
  953. foreach ($metricModifier as $metrics)
  954. {
  955. if (!$found)
  956. {
  957. $minStartPosition = $metrics['POSITION'];
  958. $maxEndPosition = $metrics['POSITION'] + $metrics['COUNT'];
  959. $found = true;
  960. }
  961. else
  962. {
  963. if ($minStartPosition > $metrics['POSITION'])
  964. {
  965. $minStartPosition = $metrics['POSITION'];
  966. }
  967. if ($maxEndPosition < $metrics['POSITION'] + $metrics['COUNT'])
  968. {
  969. $maxEndPosition = $metrics['POSITION'] + $metrics['COUNT'];
  970. }
  971. }
  972. }
  973. }
  974. if (is_null($minStartPosition) || is_null($maxEndPosition))
  975. {
  976. $result = false;
  977. }
  978. else
  979. {
  980. $result = mb_substr($text, $minStartPosition, $maxEndPosition - $minStartPosition);
  981. $result = Array('TEXT' => $result, 'POSITION' => $minStartPosition, 'LENGTH' => mb_strlen($result));
  982. }
  983. return $result;
  984. }
  985. private static function checkModifierPosition($metrics, $metricModifier)
  986. {
  987. $newMetrics = $metrics;
  988. $stackMetrics = Array();
  989. $while = true;
  990. $maxWhile = 100;
  991. while ($while && $maxWhile > 0)
  992. {
  993. $while = false;
  994. foreach ($metricModifier as $key => $currentModificator)
  995. {
  996. $diffResult = abs($newMetrics['POSITION'] - ($currentModificator['POSITION'] + $currentModificator['COUNT']));
  997. if ($diffResult < 5)
  998. {
  999. $while = true;
  1000. $newMetrics = $currentModificator;
  1001. $stackMetrics[] = $currentModificator;
  1002. unset($metricModifier[$key]);
  1003. }
  1004. else
  1005. {
  1006. $diffResult = abs($newMetrics['POSITION']+$newMetrics['COUNT'] - ($currentModificator['POSITION']));
  1007. if ($diffResult < 5)
  1008. {
  1009. $while = true;
  1010. $newMetrics = $currentModificator;
  1011. $stackMetrics[] = $currentModificator;
  1012. unset($metricModifier[$key]);
  1013. }
  1014. }
  1015. }
  1016. $maxWhile--;
  1017. }
  1018. return $stackMetrics;
  1019. }
  1020. private static function findFirstOccurrence($haystack, $needle, $offset=0)
  1021. {
  1022. $haystack = mb_substr($haystack, 0, 25 + $offset);
  1023. if(!is_array($needle))
  1024. {
  1025. $needle = array($needle);
  1026. }
  1027. $positions = array();
  1028. foreach($needle as $query)
  1029. {
  1030. $result = mb_strpos($haystack, $query, $offset);
  1031. if ($result !== false)
  1032. {
  1033. $positions[] = $result;
  1034. }
  1035. }
  1036. return empty($positions)? false: min($positions);
  1037. }
  1038. }