<?php declare(strict_types=1); namespace WerkraumMedia\Events\Service\DestinationDataImportService; use DateInterval; use DatePeriod; use DateTimeImmutable; use Generator; use Psr\Log\LoggerInterface; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Log\LogManager; use WerkraumMedia\Events\Domain\DestinationData\Date as DestinationDataDate; use WerkraumMedia\Events\Domain\Model\Date; use WerkraumMedia\Events\Domain\Model\Import; final class DatesFactory { private readonly LoggerInterface $logger; public function __construct( private readonly Context $context, LogManager $logManager ) { $this->logger = $logManager->getLogger(self::class); } /** * @return Generator<Date> */ public function createDates( Import $import, array $timeIntervals, bool $canceled ): Generator { foreach ($timeIntervals as $date) { $dates = $this->createDate($import, $date, $canceled); if (!$dates instanceof Generator) { return null; } foreach ($dates as $createdDate) { yield $createdDate; } } } /** * @return Generator<Date>|null */ private function createDate( Import $import, array $date, bool $canceled ): ?Generator { $date = new DestinationDataDate($date); if ($date->isSingle()) { $this->logger->info('Is single date', ['date' => $date]); return $this->createSingleDate($date, $canceled); } if ($date->isInterval()) { $this->logger->info('Is interval date', ['date' => $date]); return $this->createDateFromInterval($import, $date, $canceled); } return null; } /** * @return Generator<Date> */ private function createSingleDate( DestinationDataDate $date, bool $canceled ): Generator { if ($date->isAfter($this->getToday())) { yield Date::createFromDestinationDataDate($date, $canceled); } } /** * @return Generator<Date>|null */ private function createDateFromInterval( Import $import, DestinationDataDate $date, bool $canceled ): ?Generator { $date = $this->ensureRepeatUntil($import, $date); if ($date->isDaily()) { return $this->createDailyDates($date, $canceled); } if ($date->isWeekly()) { return $this->createWeeklyDates($date, $canceled); } return null; } private function ensureRepeatUntil( Import $import, DestinationDataDate $date ): DestinationDataDate { if ($date->hasKnownRepeat()) { return $date; } $repeatUntil = $this->getToday()->modify($import->getRepeatUntil())->format('c'); $this->logger->info('Interval did not provide repeatUntil.', ['newRepeat' => $repeatUntil]); return $date->withRepeatUntil($repeatUntil); } /** * @return Generator<Date> */ private function createDailyDates( DestinationDataDate $date, bool $canceled ): Generator { $today = $this->getToday(); $timeZone = $date->getTimeZone(); $start = $date->getStart(); $end = $date->getEnd(); $period = new DatePeriod($start, new DateInterval('P1D'), $date->getRepeatUntil()); foreach ($period as $day) { $day = $day->setTimezone($timeZone); if ($day < $today) { $this->logger->debug('Date was in the past.', ['day' => $day]); continue; } yield $this->createDateFromStartAndEnd( $day, $start, $end, $canceled ); } } /** * @return Generator<Date> */ private function createWeeklyDates( DestinationDataDate $date, bool $canceled ): Generator { $today = $this->getToday(); $timeZone = $date->getTimeZone(); $start = $date->getStart(); $end = $date->getEnd(); $until = $date->getRepeatUntil(); foreach ($date->getWeekdays() as $day) { $dateToUse = $start->modify($day); $dateToUse = $dateToUse->setTime((int)$start->format('H'), (int)$start->format('i')); $period = new DatePeriod($dateToUse, new DateInterval('P1W'), $until); foreach ($period as $day) { $day = $day->setTimezone($timeZone); if ($day < $today) { $this->logger->debug('Date was in the past.', ['day' => $day]); continue; } yield $this->createDateFromStartAndEnd( $day, $start, $end, $canceled ); } } } private function createDateFromStartAndEnd( DateTimeImmutable $dateToUse, DateTimeImmutable $start, DateTimeImmutable $end, bool $canceled ): Date { return Date::createFromDestinationData( $dateToUse->setTime((int)$start->format('H'), (int)$start->format('i')), $dateToUse->setTime((int)$end->format('H'), (int)$end->format('i')), $canceled ); } private function getToday(): DateTimeImmutable { $today = $this->context->getPropertyFromAspect('date', 'full', new DateTimeImmutable()); if (!$today instanceof DateTimeImmutable) { $today = new DateTimeImmutable(); } return $today->modify('midnight'); } }