Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
58 lines
2.0 KiB
PHP
58 lines
2.0 KiB
PHP
<?php
|
||
|
||
namespace App\Ingest;
|
||
|
||
use App\Scheduling\DueResolver;
|
||
use Carbon\CarbonImmutable;
|
||
use InvalidArgumentException;
|
||
|
||
final class DueAtParser
|
||
{
|
||
private const DATE = '/^(\d{4})-(\d{2})-(\d{2})$/';
|
||
private const LOCAL_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/';
|
||
private const FIXED_DATETIME = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(Z|[+-]\d{2}:\d{2})$/';
|
||
|
||
public static function parse(string $raw): ParsedDue
|
||
{
|
||
if (preg_match(self::DATE, $raw, $m)) {
|
||
self::assertDate((int) $m[1], (int) $m[2], (int) $m[3], $raw);
|
||
|
||
return ParsedDue::local($raw, DueResolver::WHOLE_DAY);
|
||
}
|
||
|
||
if (preg_match(self::LOCAL_DATETIME, $raw, $m)) {
|
||
self::assertDate((int) $m[1], (int) $m[2], (int) $m[3], $raw);
|
||
self::assertTime((int) $m[4], (int) $m[5], (int) ($m[6] ?? 0), $raw);
|
||
|
||
return ParsedDue::local(
|
||
sprintf('%s-%s-%s', $m[1], $m[2], $m[3]),
|
||
sprintf('%s:%s:%02d', $m[4], $m[5], (int) ($m[6] ?? 0)),
|
||
);
|
||
}
|
||
|
||
if (preg_match(self::FIXED_DATETIME, $raw)) {
|
||
try {
|
||
return ParsedDue::fixed(CarbonImmutable::parse($raw));
|
||
} catch (\Throwable $e) {
|
||
throw new InvalidArgumentException("due_at: невалидная дата-время «{$raw}»", 0, $e);
|
||
}
|
||
}
|
||
|
||
throw new InvalidArgumentException("due_at: ожидается YYYY-MM-DD, YYYY-MM-DDTHH:MM или дата-время со смещением, получено «{$raw}»");
|
||
}
|
||
|
||
private static function assertDate(int $y, int $m, int $d, string $raw): void
|
||
{
|
||
if (! checkdate($m, $d, $y)) {
|
||
throw new InvalidArgumentException("due_at: несуществующая дата «{$raw}»");
|
||
}
|
||
}
|
||
|
||
private static function assertTime(int $h, int $i, int $s, string $raw): void
|
||
{
|
||
if ($h > 23 || $i > 59 || $s > 59) {
|
||
throw new InvalidArgumentException("due_at: невалидное время «{$raw}»");
|
||
}
|
||
}
|
||
}
|