From aa78aee1ffa4917dd1ddb491ed296d76d7f1a12a Mon Sep 17 00:00:00 2001
From: DTTerastar
Date: Tue, 21 Apr 2026 20:05:26 -0400
Subject: [PATCH] fix: resolve dates and bucket timestamps in local time
Two bug classes were mixing UTC and local time across the CLI:
1. Absolute date flags (--since 2025-01-01, show 2025-03-08) used
time.Parse which defaults to UTC. In EDT that's Jan 1 midnight UTC =
Dec 31 8pm local, so --since silently included yesterday evening's
data and show returned workouts from the wrong calendar day.
Relative flags (--since 7d, today, yesterday) already anchored to
local time, so the two forms of the same flag had different
semantics.
2. API timestamps (StartedAt, RFC3339 with the server's zone) were
formatted to YYYY-MM-DD without converting to local first. A 11pm
local workout (4am UTC next day) bucketed under the wrong date in
stats and monthly bodyweight averages, and failed to match in
'workouts show '.
Fix is mechanical: ParseInLocation(..., time.Local) for user date
parsing, and .Local() before every Format("2006-01-02") used as a
bucket key or display string.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
cmd/bodyweights.go | 1 +
cmd/stats.go | 2 +-
cmd/workouts.go | 8 ++++----
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/cmd/bodyweights.go b/cmd/bodyweights.go
index 09bf707..99f5e1c 100644
--- a/cmd/bodyweights.go
+++ b/cmd/bodyweights.go
@@ -105,6 +105,7 @@ func loadBodyweightEntries(sinceFlag string) ([]bodyweightEntry, error) {
if err != nil {
continue
}
+ date = date.Local()
if !since.IsZero() && date.Before(since) {
continue
}
diff --git a/cmd/stats.go b/cmd/stats.go
index 5a03841..0895f71 100644
--- a/cmd/stats.go
+++ b/cmd/stats.go
@@ -103,7 +103,7 @@ func buildSummaries(posts []Post) []ExerciseSummary {
for _, p := range posts {
bw, _ := strconv.ParseFloat(strings.TrimSpace(p.Bodyweight), 64)
t, _ := time.Parse(time.RFC3339Nano, p.StartedAt)
- date := t.Format("2006-01-02")
+ date := t.Local().Format("2006-01-02")
for _, e := range p.ExerciseData {
ss := sessionStats(e, bw)
diff --git a/cmd/workouts.go b/cmd/workouts.go
index 6c32658..45cd804 100644
--- a/cmd/workouts.go
+++ b/cmd/workouts.go
@@ -87,7 +87,7 @@ var listCmd = &cobra.Command{
func parseSince(s string) (time.Time, error) {
// Try absolute date first
- if t, err := time.Parse("2006-01-02", s); err == nil {
+ if t, err := time.ParseInLocation("2006-01-02", s, time.Local); err == nil {
return t, nil
}
// Try relative: e.g. 30d, 4w, 6m, 1y
@@ -137,7 +137,7 @@ var showCmd = &cobra.Command{
if err != nil {
continue
}
- if t.Format("2006-01-02") == target.Format("2006-01-02") {
+ if t.Local().Format("2006-01-02") == target.Format("2006-01-02") {
matched = append(matched, p)
}
}
@@ -162,7 +162,7 @@ func parseDate(s string) (time.Time, error) {
case "yesterday":
return now.AddDate(0, 0, -1), nil
}
- if t, err := time.Parse("2006-01-02", s); err == nil {
+ if t, err := time.ParseInLocation("2006-01-02", s, time.Local); err == nil {
return t, nil
}
return time.Time{}, fmt.Errorf("invalid date: %q (use YYYY-MM-DD, today, or yesterday)", s)
@@ -223,7 +223,7 @@ func printFitdown(posts []Post) error {
if err != nil {
fmt.Printf("Workout %s\n", post.StartedAt)
} else {
- fmt.Printf("Workout %s\n", t.Format("January 2, 2006"))
+ fmt.Printf("Workout %s\n", t.Local().Format("January 2, 2006"))
}
if post.SessionNotes != "" {