2678400) { throw new InvalidArgumentException('Cron interval must be between 30 seconds and 31 days.'); } return [ 'type' => 'interval', 'seconds' => $seconds, ]; } public static function nextRunAt(array $schedule, ?string $anchorDateTime, int $now): string { $normalized = self::normalize($schedule); $anchor = $anchorDateTime !== null && trim($anchorDateTime) !== '' ? strtotime($anchorDateTime) : false; $base = $anchor !== false ? (int)$anchor : $now; $next = $base + (int)$normalized['seconds']; if ($next <= $now) { $missed = (int)floor(($now - $next) / (int)$normalized['seconds']) + 1; $next += $missed * (int)$normalized['seconds']; } return date('Y-m-d H:i:s', $next); } public static function dueAt(array $schedule, ?string $lastRunAt, int $now, ?int $legacyLastRun = null): string { $normalized = self::normalize($schedule); if ($lastRunAt !== null && trim($lastRunAt) !== '') { return self::nextRunAt($normalized, $lastRunAt, $now); } if ($legacyLastRun !== null && $legacyLastRun > 0) { return date('Y-m-d H:i:s', $legacyLastRun + (int)$normalized['seconds']); } return date('Y-m-d H:i:s', $now); } }