Files
geo/server/internal/shared/schedule/runtime.go
T
root 3f5b5c1e01 feat(infra): add scheduler and worker-generate standalone processes
Extract monitoring recovery/inspection workers and schedule dispatch
into a dedicated scheduler process. Add worker-generate process for
article generation and template-assist queue consumption. Introduce
shared/schedule runtime for cron-based worker lifecycle management.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:19:21 +08:00

69 lines
1.3 KiB
Go

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
}