293
// Returns: Nominal is the time that matches, pre-jitter. Next is the nominal time with
294
// jitter applied. If there is no matching time, Nominal and Next will be the zero time.
295
>
func (cs *CompiledSpec) GetNextTime(jitterSeed string, after time.Time) (GetNextTimeResult, error) {
spec.go
296
>
// If we're starting before the schedule's allowed time range, jump up to right before
297
>
// it (so that we can still return the first second of the range if it happens to match).
298
>
// note: AsTime returns unix epoch on nil StartTime
299
>
after = util.MaxTime(after, cs.spec.StartTime.AsTime().Add(-time.Second))
300
>
301
>
pastEndTime := func(t time.Time) bool {
302
return cs.spec.EndTime != nil && t.After(cs.spec.EndTime.AsTime()) || t.Year() > maxCalendarYear
303
}
304
>
warnIterations := cs.warnIterations()
spec.go
305
>
if warnIterations <= 0 {
306
warnIterations = DefaultWarnIterations
307
}
308
>
maxIterations := cs.maxIterations()
spec.go
309
>
if maxIterations <= 0 {
310
maxIterations = math.MaxInt // disabled: effectively unlimited
311
}
312
314
>
var nominal time.Time
315
>
for iterations := 0; nominal.IsZero() || cs.excluded(nominal); iterations++ {
316
>
// Hard bound: stop an over-excluded / adversarial spec from spinning toward
317
>
// maxCalendarYear. Disabled by default (maxIterations == math.MaxInt); an operator can
318
>
// lower it to re-enable enforcement. Well-formed specs resolve in a handful of iterations.
319
>
if iterations >= maxIterations {
320
return GetNextTimeResult{ComputeLimitWarning: true}, ErrComputeLimitExceeded
321
}
322
>
if iterations >= warnIterations {
spec.go
323
warned = true
324
}
325
>
nominal = cs.rawNextTime(after)
spec.go
326
>
after = nominal
327
>
328
>
if nominal.IsZero() || pastEndTime(nominal) {
329
return GetNextTimeResult{ComputeLimitWarning: warned}, nil
330
}