Also: StoresUtc trait normalizes datetime casts to UTC (controller ruling). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
63 lines
2.0 KiB
PHP
63 lines
2.0 KiB
PHP
<?php
|
|
|
|
namespace App\Scheduling;
|
|
|
|
use App\Enums\DueMode;
|
|
use App\Models\Event;
|
|
use App\Models\User;
|
|
use Carbon\CarbonImmutable;
|
|
|
|
final class DueResolver
|
|
{
|
|
public const WHOLE_DAY = '24:00:00';
|
|
|
|
public function resolve(Event $event, User $user): Due
|
|
{
|
|
return match ($event->due_mode) {
|
|
DueMode::Fixed => $this->fixed($event, $user),
|
|
DueMode::Local => $this->local($event, $user),
|
|
};
|
|
}
|
|
|
|
public function fireStart(Event $event, User $user): CarbonImmutable
|
|
{
|
|
return self::localMidnight($event->fire_on->format('Y-m-d'), $user->tz);
|
|
}
|
|
|
|
/**
|
|
* Старт дня срока. Обычно локальная полночь дня срока. Если момент срока не
|
|
* позже начала активных часов, первое окно дня уже опоздало бы — тогда старт
|
|
* за два часа до тихих часов накануне (два последних часовых окна вечера).
|
|
*/
|
|
public function todayStart(Due $due, User $user): CarbonImmutable
|
|
{
|
|
if ($due->moment->lte($user->activeStart($due->day))) {
|
|
return $user->activeEnd($due->day->subDay())->subHours(2);
|
|
}
|
|
|
|
return $due->day;
|
|
}
|
|
|
|
public static function localMidnight(string $ymd, string $tz): CarbonImmutable
|
|
{
|
|
return CarbonImmutable::createFromFormat('Y-m-d', $ymd, $tz)->startOfDay();
|
|
}
|
|
|
|
private function local(Event $event, User $user): Due
|
|
{
|
|
$day = self::localMidnight($event->due_date->format('Y-m-d'), $user->tz);
|
|
$moment = $event->due_time === self::WHOLE_DAY
|
|
? $day->addDay()
|
|
: $day->setTimeFromTimeString($event->due_time);
|
|
|
|
return new Due($day, $moment);
|
|
}
|
|
|
|
private function fixed(Event $event, User $user): Due
|
|
{
|
|
$moment = $event->due_instant->setTimezone($user->tz);
|
|
|
|
return new Due($moment->startOfDay(), $moment);
|
|
}
|
|
}
|