From 9ffa9004bc96c27781a7cc1155b7b9475744f9ae Mon Sep 17 00:00:00 2001 From: Wu Qing <3184394176@qq.com> Date: Fri, 1 May 2026 14:39:16 +0800 Subject: [PATCH] fix: respect local timezone for scheduler (#54) --- server/internal/scheduler/service.go | 2 +- server/internal/scheduler/service_test.go | 34 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/server/internal/scheduler/service.go b/server/internal/scheduler/service.go index 13d67dc..f24717a 100644 --- a/server/internal/scheduler/service.go +++ b/server/internal/scheduler/service.go @@ -45,7 +45,7 @@ type Service struct { func NewService(tasks repository.BackupTaskRepository, runner TaskRunner, logger *zap.Logger) *Service { parser := cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) return &Service{ - cron: cron.New(cron.WithParser(parser), cron.WithLocation(time.UTC)), + cron: cron.New(cron.WithParser(parser), cron.WithLocation(time.Local)), tasks: tasks, runner: runner, logger: logger, diff --git a/server/internal/scheduler/service_test.go b/server/internal/scheduler/service_test.go index 36251a9..72b58de 100644 --- a/server/internal/scheduler/service_test.go +++ b/server/internal/scheduler/service_test.go @@ -68,3 +68,37 @@ func TestServiceSyncTaskAndTrigger(t *testing.T) { t.Fatalf("expected scheduled runner to be triggered") } } + +func TestServiceSchedulesTasksInLocalTimezone(t *testing.T) { + location, err := time.LoadLocation("Asia/Shanghai") + if err != nil { + t.Fatalf("LoadLocation returned error: %v", err) + } + originalLocal := time.Local + time.Local = location + t.Cleanup(func() { + time.Local = originalLocal + }) + + service := NewService(&fakeTaskRepository{}, &fakeRunner{}, nil) + if got := service.cron.Location(); got != location { + t.Fatalf("cron location = %v, want %v", got, location) + } + + task := &model.BackupTask{ID: 1, Enabled: true, CronExpr: "0 5 * * *"} + if err := service.SyncTask(context.Background(), task); err != nil { + t.Fatalf("SyncTask returned error: %v", err) + } + entryID, ok := service.entries[task.ID] + if !ok { + t.Fatalf("expected cron entry for task %d", task.ID) + } + + entry := service.cron.Entry(entryID) + now := time.Date(2026, 4, 30, 4, 0, 0, 0, location) + got := entry.Schedule.Next(now) + want := time.Date(2026, 4, 30, 5, 0, 0, 0, location) + if !got.Equal(want) { + t.Fatalf("next run = %s, want %s", got, want) + } +}