Initial commit

This commit is contained in:
root
2026-01-19 17:44:46 +01:00
commit 823af8b11d
8721 changed files with 1130846 additions and 0 deletions

View File

@@ -0,0 +1,410 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM Open Source CRM application.
* Copyright (C) 2014-2025 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program 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.
*
* This program 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 <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Tools\WorkingTime;
use DateTimeZone;
use Espo\Tools\WorkingTime\Calendar\WorkingWeekday;
use Espo\Tools\WorkingTime\Calendar\WorkingDate;
use Espo\Tools\WorkingTime\Calendar\HavingRanges;
use Espo\Core\Field\DateTime;
use Espo\Core\Field\Date;
class Extractor
{
/**
* @return array{DateTime,DateTime}[]
*/
public function extract(Calendar $calendar, DateTime $from, DateTime $to): array
{
$pointer = $from->withTimezone($calendar->getTimezone());
$fromDate = Date::fromDateTime($from->modify('-1 day')->toDateTime());
$toDate = Date::fromDateTime($from->modify('+1 day')->toDateTime());
$workingDates = $calendar->getWorkingDates($fromDate, $toDate);
$nonWorkingDates = $calendar->getNonWorkingDates($fromDate, $toDate);
$workingWeekdays = $calendar->getWorkingWeekdays();
$list = [];
$end = $to->withTimezone($calendar->getTimezone())->withTime(23, 59, 59);
while ($pointer->toTimestamp() < $end->toTimestamp()) {
$list = array_merge(
$list,
$this->extractIteration(
$pointer,
$workingDates,
$nonWorkingDates,
$workingWeekdays,
),
);
$pointer = $pointer->modify('+1 day');
}
return $this->trim($list, $from, $to, $calendar);
}
/**
* @param array{DateTime,DateTime}[] $list
* @return array{DateTime,DateTime}[]
*/
private function trim(array $list, DateTime $from, DateTime $to, Calendar $calendar): array
{
$wasUnset = false;
foreach ($list as $i => $pair) {
if ($from->isLessThan($pair[0]) || $from->isEqualTo($pair[0])) {
break;
}
if ($from->isGreaterThan($pair[1]) || $from->isEqualTo($pair[1])) {
unset($list[$i]);
$wasUnset = true;
continue;
}
if ($from->isLessThan($pair[1])) {
$list[$i][0] = $from->withTimezone($calendar->getTimezone());
}
break;
}
if ($wasUnset) {
$list = array_values($list);
$wasUnset = false;
}
for ($i = count($list) - 1; $i >= 0; $i--) {
$pair = $list[$i];
if ($to->isGreaterThan($pair[1]) || $to->isEqualTo($pair[1])) {
break;
}
if ($to->isLessThan($pair[0]) || $to->isEqualTo($pair[0])) {
unset($list[$i]);
$wasUnset = true;
continue;
}
if ($to->isGreaterThan($pair[0])) {
$list[$i][1] = $to->withTimezone($calendar->getTimezone());
}
break;
}
if ($wasUnset) {
$list = array_values($list);
}
return $list;
}
/**
* @return array{DateTime,DateTime}[]
*/
public function extractAllDay(Calendar $calendar,
DateTime $from,
DateTime $to,
?DateTimeZone $timezone = null
): array {
$timezone ??= $calendar->getTimezone();
$pointer = $from
->withTimezone($timezone)
->withTime(0, 0, 0);
$fromDate = Date::fromDateTime($from->modify('-1 day')->toDateTime());
$toDate = Date::fromDateTime($from->modify('+1 day')->toDateTime());
$workingDates = $calendar->getWorkingDates($fromDate, $toDate);
$nonWorkingDates = $calendar->getNonWorkingDates($fromDate, $toDate);
$workingWeekdays = $calendar->getWorkingWeekdays();
$list = [];
$end = $to->withTimezone($timezone)->withTime(23, 59, 59);
while ($pointer->toTimestamp() < $end->toTimestamp()) {
$isWorkingDay = $this->isWorkingDay(
$pointer,
$workingDates,
$nonWorkingDates,
$workingWeekdays,
);
$nextPointer = $pointer->modify('+1 day');
if ($isWorkingDay) {
$list[] = [
$pointer,
$nextPointer
];
}
$pointer = $nextPointer;
}
return $list;
}
/**
* @return array{DateTime,DateTime}[]
*/
public function extractInversion(Calendar $calendar, DateTime $from, DateTime $to): array
{
$list = $this->extract($calendar, $from, $to);
$timezone = $calendar->getTimezone();
if ($list === []) {
return [
[
$from->withTimezone($timezone),
$to->withTimezone($timezone),
]
];
}
$listInverted = [];
$count = count($list);
$listInverted[] = [
$from->withTimezone($calendar->getTimezone()),
$list[0][0]
];
for ($i = 0; $i < $count - 1; $i++) {
$item1 = $list[$i];
$item2 = $list[$i + 1];
$listInverted[] = [
$item1[1],
$item2[0]
];
}
$listInverted[] = [
$list[$count - 1][1],
$to->withTimezone($timezone)
];
return $listInverted;
}
/**
* @return array{DateTime,DateTime}[]
*/
public function extractAllDayInversion(
Calendar $calendar,
DateTime $from,
DateTime $to,
?DateTimeZone $timezone = null
): array {
$list = $this->extractAllDay($calendar, $from, $to, $timezone);
if ($list === []) {
return [[$from, $to]];
}
$count = count($list);
$listInverted = [];
$listInverted[] = [
$from,
$list[0][0]
];
for ($i = 0; $i < $count - 1; $i++) {
$item1 = $list[$i];
$item2 = $list[$i + 1];
$listInverted[] = [
$item1[1],
$item2[0]
];
}
$listInverted[] = [
$list[$count - 1][1],
$to
];
return $listInverted;
}
/**
* @param WorkingDate[] $workingDates
* @param WorkingDate[] $nonWorkingDates
* @param WorkingWeekday[] $workingWeekdays
*/
private function isWorkingDay(
DateTime $pointer,
array $workingDates,
array $nonWorkingDates,
array $workingWeekdays
): bool {
if ($this->findInDateList($pointer, $nonWorkingDates)) {
return false;
}
$day1 = $this->findInDateList($pointer, $workingDates);
if ($day1) {
return true;
}
$day2 = $this->findInWeekdayList($pointer, $workingWeekdays);
if ($day2) {
return true;
}
return false;
}
/**
* @param WorkingDate[] $workingDates
* @param WorkingDate[] $nonWorkingDates
* @param WorkingWeekday[] $workingWeekdays
* @return array{DateTime,DateTime}[]
*/
private function extractIteration(
DateTime $pointer,
array $workingDates,
array $nonWorkingDates,
array $workingWeekdays
): array {
if ($this->findInDateList($pointer, $nonWorkingDates)) {
return [];
}
$day1 = $this->findInDateList($pointer, $workingDates);
if ($day1) {
return $this->extractFromDay($pointer, $day1);
}
$day2 = $this->findInWeekdayList($pointer, $workingWeekdays);
if ($day2) {
return $this->extractFromDay($pointer, $day2);
}
return [];
}
/**
* @param DateTime $pointer
* @param WorkingDate[] $dateList
*/
private function findInDateList(DateTime $pointer, array $dateList): ?WorkingDate
{
$day = $pointer->getDay();
$month = $pointer->getMonth();
$year = $pointer->getYear();
foreach ($dateList as $item) {
$date = $item->getDate();
if (
$date->getDay() === $day &&
$date->getMonth() === $month &&
$date->getYear() === $year
) {
return $item;
}
}
return null;
}
/**
* @param DateTime $pointer
* @param WorkingWeekday[] $dayList
*/
private function findInWeekdayList(DateTime $pointer, array $dayList): ?WorkingWeekday
{
$dow = $pointer->getDayOfWeek();
foreach ($dayList as $item) {
if ($item->getWeekday() === $dow) {
return $item;
}
}
return null;
}
/**
* @return array{DateTime,DateTime}[]
*/
private function extractFromDay(DateTime $dateTime, HavingRanges $day): array
{
$pointer = $dateTime->toDateTime();
$list = [];
foreach ($day->getRanges() as $range) {
$start = $range->getStart();
$end = $range->getEnd();
$startDateTime = DateTime::fromDateTime(
$pointer->setTime($start->getHour(), $start->getMinute())
);
$endDateTime = DateTime::fromDateTime(
$pointer->setTime($end->getHour(), $end->getMinute())
);
if ($end->getHour() === 0 && $end->getMinute() === 0) {
$endDateTime = $endDateTime->addDays(1);
}
$list[] = [$startDateTime, $endDateTime];
}
return $list;
}
}