fix(cron): honor schedule time zones (#1044)

This commit is contained in:
XYSK-lilong007 2026-03-13 02:26:18 +08:00
parent 4a8a2e9c23
commit 83becfc2c9
2 changed files with 52 additions and 0 deletions

View file

@ -7,6 +7,7 @@ import (
"fmt"
"log"
"os"
"strings"
"sync"
"time"
@ -286,6 +287,14 @@ func (cs *CronService) computeNextRun(schedule *CronSchedule, nowMS int64) *int6
// Use gronx to calculate next run time
now := time.UnixMilli(nowMS)
if tz := strings.TrimSpace(schedule.TZ); tz != "" {
loc, err := time.LoadLocation(tz)
if err != nil {
log.Printf("[cron] failed to load timezone %q: %v", tz, err)
} else {
now = now.In(loc)
}
}
nextTime, err := gronx.NextTickAfter(schedule.Expr, now, false)
if err != nil {
log.Printf("[cron] failed to compute next run for expr '%s': %v", schedule.Expr, err)

View file

@ -5,6 +5,7 @@ import (
"path/filepath"
"runtime"
"testing"
"time"
)
func TestSaveStore_FilePermissions(t *testing.T) {
@ -33,6 +34,48 @@ func TestSaveStore_FilePermissions(t *testing.T) {
}
}
func TestComputeNextRun_UsesScheduleTimeZone(t *testing.T) {
cs := NewCronService(filepath.Join(t.TempDir(), "cron", "jobs.json"), nil)
now := time.Date(2026, time.March, 13, 12, 30, 0, 0, time.UTC).UnixMilli()
baseline := cs.computeNextRun(&CronSchedule{Kind: "cron", Expr: "0 9 * * *"}, now)
if baseline == nil {
t.Fatal("baseline computeNextRun() returned nil")
}
t.Run("uses explicit timezone", func(t *testing.T) {
next := cs.computeNextRun(&CronSchedule{
Kind: "cron",
Expr: "0 9 * * *",
TZ: "America/New_York",
}, now)
if next == nil {
t.Fatal("computeNextRun() returned nil")
}
wantNext := time.Date(2026, time.March, 13, 13, 0, 0, 0, time.UTC).UnixMilli()
if *next != wantNext {
t.Fatalf("computeNextRun() = %d, want %d", *next, wantNext)
}
if *next == *baseline {
t.Fatal("explicit timezone should change the computed next run for this fixture")
}
})
t.Run("falls back to baseline on invalid timezone", func(t *testing.T) {
next := cs.computeNextRun(&CronSchedule{
Kind: "cron",
Expr: "0 9 * * *",
TZ: "Mars/OlympusMons",
}, now)
if next == nil {
t.Fatal("computeNextRun() returned nil")
}
if *next != *baseline {
t.Fatalf("computeNextRun() = %d, want baseline %d", *next, *baseline)
}
})
}
func int64Ptr(v int64) *int64 {
return &v
}