- admin/quickTasks: list, create, save, delete - admin/scheduledTasks: list, save, delete, toggle, run, runDue - cron: expireStaleChats, expireTabs - receipt: public order receipt page (no auth, UUID-secured) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
59 lines
2.2 KiB
PHP
59 lines
2.2 KiB
PHP
<?php
|
|
/**
|
|
* Shared cron expression utilities for scheduled tasks.
|
|
*/
|
|
|
|
/**
|
|
* Parse a 5-part cron expression and calculate the next run time from now.
|
|
* Supports: minute hour day month weekday
|
|
* Weekday ranges like 1-5 (Mon-Fri) are supported.
|
|
* Returns a DateTime in UTC.
|
|
*/
|
|
function calculateNextRun(string $cronExpression): DateTime {
|
|
$parts = preg_split('/\s+/', trim($cronExpression));
|
|
if (count($parts) !== 5) {
|
|
return new DateTime('+1 day', new DateTimeZone('UTC'));
|
|
}
|
|
|
|
[$cronMinute, $cronHour, $cronDay, $cronMonth, $cronWeekday] = $parts;
|
|
|
|
// Start from next minute
|
|
$check = new DateTime('now', new DateTimeZone('UTC'));
|
|
$check->modify('+1 minute');
|
|
$check->setTime((int) $check->format('H'), (int) $check->format('i'), 0);
|
|
|
|
$maxIterations = 400 * 24 * 60;
|
|
for ($i = 0; $i < $maxIterations; $i++) {
|
|
$m = (int) $check->format('i');
|
|
$h = (int) $check->format('G');
|
|
$d = (int) $check->format('j');
|
|
$mo = (int) $check->format('n');
|
|
$dow = (int) $check->format('w'); // 0=Sunday
|
|
|
|
$matchMinute = ($cronMinute === '*' || (is_numeric($cronMinute) && $m === (int) $cronMinute));
|
|
$matchHour = ($cronHour === '*' || (is_numeric($cronHour) && $h === (int) $cronHour));
|
|
$matchDay = ($cronDay === '*' || (is_numeric($cronDay) && $d === (int) $cronDay));
|
|
$matchMonth = ($cronMonth === '*' || (is_numeric($cronMonth) && $mo === (int) $cronMonth));
|
|
|
|
$matchWeekday = ($cronWeekday === '*');
|
|
if (!$matchWeekday) {
|
|
if (str_contains($cronWeekday, '-')) {
|
|
$range = explode('-', $cronWeekday);
|
|
if (count($range) === 2 && is_numeric($range[0]) && is_numeric($range[1])) {
|
|
$matchWeekday = ($dow >= (int) $range[0] && $dow <= (int) $range[1]);
|
|
}
|
|
} elseif (is_numeric($cronWeekday)) {
|
|
$matchWeekday = ($dow === (int) $cronWeekday);
|
|
}
|
|
}
|
|
|
|
if ($matchMinute && $matchHour && $matchDay && $matchMonth && $matchWeekday) {
|
|
return $check;
|
|
}
|
|
|
|
$check->modify('+1 minute');
|
|
}
|
|
|
|
// Fallback
|
|
return new DateTime('+1 day', new DateTimeZone('UTC'));
|
|
}
|