Read Veeam next run from the live schedule

On VBR 13 a job's ScheduleOptions property is a stale snapshot: NextRun
is empty (and its LatestRunLocal years old), so every Veeam job showed
no next run and could never be flagged Overdue. GetScheduleOptions() /
Get-VBRJobScheduleOptions return the live schedule ("2026-10-09
12:15:00 AM"). Use GetScheduleOptions(), falling back to the property.

Immediate-mode backup copy jobs (IsContinuous) have a live NextRun too,
but it's a stale date months in the past, which would show as Overdue;
keep reporting no next run for them.

Parsing tries the current culture, then invariant; VBR 13's format
parsed correctly under en-CA, en-US, fr-CA and en-GB. Mock updated to
VBR 13's schedule shape.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-08 09:53:55 -06:00
co-authored by Claude Opus 5.5
parent 51b97c4556
commit dd3e2fe885
3 changed files with 35 additions and 14 deletions
+17 -6
View File
@@ -85,14 +85,25 @@ function Import-Veeam {
function Get-NextRun($Job) {
try {
if (-not $Job.IsScheduleEnabled) { return $null }
$raw = $Job.ScheduleOptions.NextRun
# Immediate-mode backup copy jobs run whenever their source job does. Their schedule still
# carries a NextRun, but it's a stale date (months old on VBR 13) that would show as Overdue.
if ($Job.IsContinuous) { return $null }
# The ScheduleOptions property is a stale snapshot on VBR 13 (NextRun empty, last run years
# old); GetScheduleOptions() reads the live schedule. Fall back for versions without it.
$options = $null
try { $options = $Job.GetScheduleOptions() } catch { }
if (-not $options) { $options = $Job.ScheduleOptions }
$raw = $options.NextRun
if (-not $raw) { return $null } # e.g. chained jobs that run "after" another job
if ($raw -is [datetime]) { return ConvertTo-IsoTime $raw }
# Older versions return a string formatted with the server's culture, so parse with it
$parsed = [datetime]::MinValue
if ([datetime]::TryParse([string]$raw, [Globalization.CultureInfo]::CurrentCulture,
[Globalization.DateTimeStyles]::None, [ref]$parsed)) {
return ConvertTo-IsoTime $parsed
# A string formatted by VBR ("2026-10-09 12:15:00 AM" on VBR 13, en-CA). Try the server's
# culture first, then the invariant one
foreach ($culture in [Globalization.CultureInfo]::CurrentCulture, [Globalization.CultureInfo]::InvariantCulture) {
$parsed = [datetime]::MinValue
if ([datetime]::TryParse([string]$raw, $culture, [Globalization.DateTimeStyles]::None, [ref]$parsed)) {
return ConvertTo-IsoTime $parsed
}
}
Write-Log "Couldn't parse next run '$raw' for job '$($Job.Name)'" 'WARN'
} catch {