fix(v0.5.1): use local time for workspace directory prefix and info display

Workspace directory names (YYYY-MM-DD_slug) and the YYYYMMDD-HHMMSS ID
field now use local time so users see their wall-clock date rather
than UTC — the prior behavior caused evening-EST creations to appear
under tomorrow's date for several hours every day. ctask info's
Created/Updated/Archived lines also convert to local for display.

Stored timestamps in task.yaml, session logs, the lease, the manifest,
and the session summary all continue to use UTC. Only user-facing
surfaces change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 21:33:52 -04:00
parent 8130a689d4
commit a162aec0b2
4 changed files with 101 additions and 6 deletions
+6 -3
View File
@@ -35,8 +35,11 @@ func runInfo(cmd *cobra.Command, args []string) error {
fmt.Printf("Status: %s\n", m.Status)
fmt.Printf("Mode: %s\n", m.Mode)
fmt.Printf("Agent: %s\n", m.Agent)
fmt.Printf("Created: %s\n", m.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf("Updated: %s\n", m.UpdatedAt.Format("2006-01-02 15:04:05"))
// v0.5.1: display timestamps in local time. task.yaml stores UTC;
// info converts for friendliness so the shown time matches the user's
// wall clock.
fmt.Printf("Created: %s\n", m.CreatedAt.Local().Format("2006-01-02 15:04:05"))
fmt.Printf("Updated: %s\n", m.UpdatedAt.Local().Format("2006-01-02 15:04:05"))
fmt.Printf("Path: %s\n", ws.Path)
if m.LaunchDir != "" {
@@ -56,7 +59,7 @@ func runInfo(cmd *cobra.Command, args []string) error {
}
if m.ArchivedAt != nil {
fmt.Printf("Archived: %s\n", m.ArchivedAt.Format("2006-01-02 15:04:05"))
fmt.Printf("Archived: %s\n", m.ArchivedAt.Local().Format("2006-01-02 15:04:05"))
}
// List contents
+50
View File
@@ -88,6 +88,56 @@ func TestInfoOmitsLaunchFieldsForTask(t *testing.T) {
}
}
func TestInfoFormatsTimestampsInLocalZone(t *testing.T) {
// v0.5.1: info must display Created/Updated/Archived in the local zone
// so the displayed time matches the user's wall clock. The stored
// timestamp stays UTC in task.yaml (unambiguous), but the display
// converts for friendliness.
root := t.TempDir()
wsDir := filepath.Join(root, "general", "2026-04-22_tz-test")
os.MkdirAll(wsDir, 0755)
// Pick a UTC moment that crosses a date boundary in most western zones.
fixedUTC := time.Date(2026, 4, 23, 1, 22, 50, 0, time.UTC)
archived := fixedUTC.Add(time.Hour)
meta := &workspace.TaskMeta{
ID: "t", Slug: "tz-test", Title: "tz-test",
CreatedAt: fixedUTC, UpdatedAt: fixedUTC,
ArchivedAt: &archived,
Status: "archived", Category: "general", Type: "task",
Mode: "local", Agent: "claude",
}
workspace.WriteMeta(filepath.Join(wsDir, "task.yaml"), meta)
// infoAll must be true because the workspace is archived.
prevAll := infoAll
infoAll = true
defer func() { infoAll = prevAll }()
out, err := runInfoCapture(t, root, "tz-test")
if err != nil {
t.Fatalf("runInfo: %v", err)
}
localCreated := fixedUTC.Local().Format("2006-01-02 15:04:05")
utcCreated := fixedUTC.Format("2006-01-02 15:04:05")
localArchived := archived.Local().Format("2006-01-02 15:04:05")
if !strings.Contains(out, "Created: "+localCreated) {
t.Errorf("expected Created line to show local time %q:\n%s", localCreated, out)
}
if !strings.Contains(out, "Updated: "+localCreated) {
t.Errorf("expected Updated line to show local time %q:\n%s", localCreated, out)
}
if !strings.Contains(out, "Archived: "+localArchived) {
t.Errorf("expected Archived line to show local time %q:\n%s", localArchived, out)
}
// Only meaningful when local != UTC (skips when the test host is UTC).
if localCreated != utcCreated && strings.Contains(out, "Created: "+utcCreated) {
t.Errorf("Created line must not show UTC time %q when local differs (%q):\n%s", utcCreated, localCreated, out)
}
}
func TestInfoShowsDirExistsNoWhenLaunchDirMissing(t *testing.T) {
root := t.TempDir()
wsDir := filepath.Join(root, "projects", "2026-04-22_renamed")