Files
kupshop/bundles/KupShop/KupShopBundle/Util/DateUtil.php
2025-08-02 16:30:27 +02:00

182 lines
5.3 KiB
PHP

<?php
namespace KupShop\KupShopBundle\Util;
use Yasumi\ProviderInterface;
use Yasumi\Yasumi;
class DateUtil
{
public static function calcWorkingDaysWithHours($days, $hours, $seconds, $fromDay = null, $workdayFunction = null)
{
$secondsTotal = $days * 24 * 60 + $hours * 60 + $seconds;
$days = $secondsTotal > 0 ? floor($secondsTotal / 24 / 60) : ceil($secondsTotal / 24 / 60);
$seconds = $secondsTotal - $days * 24 * 60;
$fromDay = self::calcWorkingDays($days, $fromDay, $workdayFunction);
if (!$seconds) {
return $fromDay;
}
$timeDiff = new \DateInterval('PT'.abs($seconds).'M');
$timeDiff->invert = $seconds < 0;
$fromDay->add($timeDiff);
if (!is_callable($workdayFunction)) {
$workdayFunction = [self::class, 'isWorkday'];
}
$timeDiffDay = new \DateInterval('P1D');
$timeDiffDay->invert = $seconds < 0;
$i = 0;
while (!$workdayFunction($fromDay)) {
if ($i++ > 20) {
getRaven()->captureMessage('Calculate working days from minutes error');
break;
}
$fromDay->add($timeDiffDay);
}
return $fromDay;
}
/**
* @param null $fromDay
* @param null $workdayFunction
*
* @return \DateTime|null
*
* @throws \ReflectionException
*/
public static function calcWorkingDays($days, $fromDay = null, $workdayFunction = null, ?array $countries = null)
{
if (empty($fromDay)) {
$fromDay = new \DateTime();
}
if ($days > 20) {
// if more than 20 days, don't calculate working days, just add days
// see https://sentry.wpj.cz/organizations/wpj/issues/467352/ - Calculate working days error, 101 loops reached
$fromDay->add(new \DateInterval('P'.abs($days).'D'));
return $fromDay;
}
$dayDiff = new \DateInterval('P1D');
if ($days < 0) {
$dayDiff->invert = 1;
}
$days = abs($days);
if (!is_callable($workdayFunction)) {
$workdayFunction = [self::class, 'isWorkday'];
}
$i = 0;
while ($days > 0) {
if ($i++ > 100) {
getRaven()->captureMessage('Calculate working days error, 101 loops reached');
break;
}
$fromDay->add($dayDiff);
if ($workdayFunction($fromDay, $countries)) {
$days--;
}
}
return $fromDay;
}
/**
* @param $date \DateTime
*
* @return bool
*
* @throws \ReflectionException
*/
public static function isWorkday($date, ?array $countries = null)
{
if (empty($countries)) {
$countries = ['CZ'];
}
$result = true;
foreach ($countries as $country) {
$result = $result && static::createHolidays($country, (int) $date->format('Y'))->isWorkingDay($date);
}
return $result;
}
public static function compareTimes($first, $second = null): bool
{
$first = new \DateTime('2000-01-01 '.$first);
if (!$second) {
$second = date('H:i:s');
}
$second = new \DateTime('2000-01-01 '.$second);
return $first < $second;
}
public static function isOpen(array $opening_hours, $dayOfWeek = null, $compareTime = null): array
{
if ($dayOfWeek === null) {
$dayOfWeek = (new \DateTime())->format('N');
}
if ($compareTime === null) {
$compareTime = date('H:i:s');
}
$result = [
'is_open' => false,
'is_open_today' => false,
];
if (($from = ($opening_hours[$dayOfWeek][0] ?? false))
&& ($to = ($opening_hours[$dayOfWeek][1] ?? false))) {
$breakFrom = ($opening_hours[$dayOfWeek][2] ?? false);
$breakTo = ($opening_hours[$dayOfWeek][3] ?? false);
if (DateUtil::compareTimes($from, $compareTime) && !DateUtil::compareTimes($to, $compareTime)) {
if (!$breakFrom || !$breakTo || DateUtil::compareTimes($compareTime, $breakFrom) || DateUtil::compareTimes($breakTo,
$compareTime)) {
$result['is_open'] = true;
}
}
$result['is_open_today'] = ['from' => $from, 'to' => $to, 'break_from' => $breakFrom, 'break_to' => $breakTo];
}
return $result;
}
private static function createHolidays(string $country, int $year): ProviderInterface
{
static $holidaysCache = [];
$cacheKey = implode('-', [$country, $year]);
if (!($holidaysCache[$cacheKey] ?? false)) {
try {
// method `createByISO3166_2` does not exists in older versions of Yasumi lib (< 1.7.0)
$holidaysCache[$cacheKey] = Yasumi::createByISO3166_2($country, $year);
} catch (\Throwable $e) {
// fallback
$holidaysCache[$cacheKey] = Yasumi::create('CzechRepublic', $year);
}
}
return $holidaysCache[$cacheKey];
}
public static function getNowWithOffset(int $offset)
{
return (new \DateTime())->modify(($offset > 0 ? '+' : '-').abs($offset).' days');
}
}