package schedule import ( "fmt" "strings" "time" cron "github.com/robfig/cron/v3" ) var cronParser = cron.NewParser( cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor, ) func Parse(cronExpr string) (cron.Schedule, error) { trimmed := strings.TrimSpace(cronExpr) if trimmed == "" { return nil, fmt.Errorf("cron expression is required") } schedule, err := cronParser.Parse(trimmed) if err != nil { return nil, fmt.Errorf("parse cron expression %q: %w", trimmed, err) } return schedule, nil } func ComputeNextRun(cronExpr string, startAt, endAt *time.Time, now time.Time) (*time.Time, error) { schedule, err := Parse(cronExpr) if err != nil { return nil, err } notBefore := now.In(time.Local) if startAt != nil { start := startAt.In(time.Local) if start.After(notBefore) { notBefore = start } } next := schedule.Next(notBefore.Add(-time.Nanosecond)).Round(0) if next.IsZero() { return nil, nil } if endAt != nil && next.After(endAt.In(time.Local)) { return nil, nil } return &next, nil } func IsRunAllowed(runAt time.Time, startAt, endAt *time.Time) bool { runAt = runAt.In(time.Local) if startAt != nil && runAt.Before(startAt.In(time.Local)) { return false } if endAt != nil && runAt.After(endAt.In(time.Local)) { return false } return true }