Files
myep-team/src/RequiredTeamerCheck/RecurringDeadlines.php
T

105 lines
2.8 KiB
PHP

<?php
namespace App\RequiredTeamerCheck;
use Carbon\CarbonImmutable;
/**
* A set of yearly recurring dates after which something must be confirmed again.
*
* Belongs to the individual check that composes it, never to the application as a
* whole: a deadline expresses "this particular confirmation has expired", not
* "portal access has expired".
*/
final class RecurringDeadlines
{
/**
* @param string[] $deadlines dates as MM-DD, e.g. ['04-01', '10-01']; an empty
* list is never due
*/
public function __construct(private readonly array $deadlines)
{
}
/**
* Whether a confirmation is outstanding: never confirmed, or last confirmed
* before the most recent deadline that has passed.
*/
public function isDue(?\DateTimeInterface $confirmedAt, ?\DateTimeInterface $registeredAt, CarbonImmutable $now): bool
{
$deadline = $this->getCurrentDeadline($now);
if (null === $deadline) {
return false;
}
if (true === $this->isRegisteredAfterAnyDeadline($registeredAt, $now)) {
return false;
}
if (null === $confirmedAt) {
return true;
}
return CarbonImmutable::instance($confirmedAt) < $deadline;
}
/**
* The most recent deadline that has already passed, or null when none has.
*/
public function getCurrentDeadline(CarbonImmutable $now): ?CarbonImmutable
{
$deadlines = $this->getDeadlinesForYear($now);
rsort($deadlines);
foreach ($deadlines as $deadline) {
if ($deadline <= $now) {
return $deadline;
}
}
return null;
}
/**
* Someone who registered after a deadline has effectively confirmed their data
* by registering, so they are exempt until the next one.
*/
private function isRegisteredAfterAnyDeadline(?\DateTimeInterface $registeredAt, CarbonImmutable $now): bool
{
if (null === $registeredAt) {
return false;
}
$registeredAt = CarbonImmutable::instance($registeredAt)->startOfDay();
foreach ($this->getDeadlinesForYear($now) as $deadline) {
if ($registeredAt > $deadline) {
return true;
}
}
return false;
}
/**
* @return CarbonImmutable[]
*/
private function getDeadlinesForYear(CarbonImmutable $now): array
{
$deadlines = [];
foreach (array_filter(array_map('trim', $this->deadlines)) as $date) {
$deadline = CarbonImmutable::createFromFormat('Y-m-d', sprintf('%s-%s', $now->format('Y'), $date));
if (false === $deadline instanceof CarbonImmutable) {
continue;
}
$deadlines[] = $deadline->startOfDay();
}
return $deadlines;
}
}