diff --git a/internal/skillscheck/sync.go b/internal/skillscheck/sync.go index 2f8adb3db..50620306f 100644 --- a/internal/skillscheck/sync.go +++ b/internal/skillscheck/sync.go @@ -14,6 +14,8 @@ import ( "github.com/larksuite/cli/internal/selfupdate" ) +const skillsStateUpdatedAtLayout = "2006-01-02T15:04:05" + var ( skillNamePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_:-]*(@[^\s]+)?$`) ansiPattern = regexp.MustCompile(`\x1b\[[0-?]*[ -/]*[@-~]`) @@ -335,7 +337,7 @@ func SyncSkills(opts SyncOptions) *SyncResult { UpdatedSkills: plan.ToUpdate, AddedOfficialSkills: plan.Added, SkippedDeletedSkills: plan.SkippedDeleted, - UpdatedAt: opts.Now().UTC().Format(time.RFC3339), + UpdatedAt: opts.Now().Format(skillsStateUpdatedAtLayout), } if err := WriteState(state); err != nil { result.Action = "failed" @@ -428,7 +430,7 @@ func fallbackFullInstall(opts SyncOptions, reason string, official []string) *Sy UpdatedSkills: official, AddedOfficialSkills: official, SkippedDeletedSkills: []string{}, - UpdatedAt: opts.Now().UTC().Format(time.RFC3339), + UpdatedAt: opts.Now().Format(skillsStateUpdatedAtLayout), } if writeErr := WriteState(state); writeErr != nil { return &SyncResult{ diff --git a/internal/skillscheck/sync_test.go b/internal/skillscheck/sync_test.go index fb8f117fc..2b4e90d8a 100644 --- a/internal/skillscheck/sync_test.go +++ b/internal/skillscheck/sync_test.go @@ -339,7 +339,9 @@ func TestSyncSkills_WritesStateAndDoesNotWriteStamp(t *testing.T) { result := SyncSkills(SyncOptions{ Version: "1.0.33", Runner: runner, - Now: func() time.Time { return time.Date(2026, 5, 18, 12, 0, 0, 0, time.UTC) }, + Now: func() time.Time { + return time.Date(2026, 5, 18, 12, 0, 0, 0, time.FixedZone("UTC+8", 8*60*60)) + }, }) if result.Err != nil { @@ -361,6 +363,9 @@ func TestSyncSkills_WritesStateAndDoesNotWriteStamp(t *testing.T) { assertStrings(t, state.UpdatedSkills, []string{"lark-calendar", "lark-new"}) assertStrings(t, state.AddedOfficialSkills, []string{"lark-new"}) assertStrings(t, state.SkippedDeletedSkills, []string{"lark-mail"}) + if state.UpdatedAt != "2026-05-18T12:00:00" { + t.Errorf("state.UpdatedAt = %q, want local wall-clock timestamp without timezone", state.UpdatedAt) + } if _, err := os.Stat(filepath.Join(dir, "skills.stamp")); !os.IsNotExist(err) { t.Fatalf("skills.stamp exists or stat failed with unexpected err: %v", err) } @@ -584,7 +589,13 @@ func TestSyncSkills_ParseEmptyLocalListsFallBackToFullInstall(t *testing.T) { globalOut: "Some unrecognized output format\n", } - result := SyncSkills(SyncOptions{Version: "1.0.33", Runner: runner, Now: time.Now}) + result := SyncSkills(SyncOptions{ + Version: "1.0.33", + Runner: runner, + Now: func() time.Time { + return time.Date(2026, 5, 18, 12, 0, 0, 0, time.FixedZone("UTC-7", -7*60*60)) + }, + }) if result.Action != "fallback_synced" { t.Fatalf("SyncSkills() action = %q, want fallback_synced", result.Action) } @@ -594,6 +605,13 @@ func TestSyncSkills_ParseEmptyLocalListsFallBackToFullInstall(t *testing.T) { if runner.installedAll != 1 { t.Fatalf("installedAll = %d, want 1", runner.installedAll) } + state, readable, err := ReadState() + if err != nil || !readable { + t.Fatalf("ReadState() = (_, %v, %v), want readable", readable, err) + } + if state.UpdatedAt != "2026-05-18T12:00:00" { + t.Errorf("state.UpdatedAt = %q, want local wall-clock timestamp without timezone", state.UpdatedAt) + } } func TestSyncSkills_EmptyToUpdateFallsBackToFullInstall(t *testing.T) {