Skip to content

fix: resolve dates and bucket timestamps in local time - #14

Merged
DTTerastar merged 1 commit into
mainfrom
fix/timezone-local
Apr 22, 2026
Merged

fix: resolve dates and bucket timestamps in local time#14
DTTerastar merged 1 commit into
mainfrom
fix/timezone-local

Conversation

@DTTerastar

Copy link
Copy Markdown
Collaborator

Summary

Two bug classes were mixing UTC and local time, producing off-by-one-day results for users not in UTC:

  1. Absolute date flags used UTC. `--since 2025-01-01` and `show 2025-03-08` went through `time.Parse("2006-01-02", …)`, which defaults to UTC. Relative flags (`--since 7d`, `today`, `yesterday`) already anchored to local, so the two forms of the same flag had different semantics.
  2. Day-bucketing used the API's zone. `StartedAt` (RFC3339Nano from the server) was `.Format("2006-01-02")`'d without converting to local first. A workout logged at 11pm local (≈4am UTC next day) bucketed under the wrong date in stats, monthly bodyweight averages, and `workouts show ` comparisons.

Fix is mechanical:

  • `cmd/workouts.go`: `ParseInLocation("2006-01-02", s, time.Local)` in both `parseSince` and `parseDate`; `.Local()` on the timestamp before comparing in `show` and before formatting the fitdown header.
  • `cmd/stats.go`: `.Local()` on the timestamp before formatting the per-session date key.
  • `cmd/bodyweights.go`: normalize `date = date.Local()` once on ingest; every downstream format (list, monthly key, stats display) inherits local zone.

Test plan

  • `go build ./...` and `go vet ./...` pass
  • `workouts show today` shows today's workout (was failing after 8pm EDT before, because "today" resolved to local Apr 21 but workouts logged earlier today had their RFC3339 timestamps formatted in UTC → Apr 22, mismatch)
  • `workouts list --since 3d` and `workouts list --since 2026-04-20` now return consistent data
  • Reviewer: confirm your stats monthly graph doesn't shift workouts across month boundaries near midnight

🤖 Generated with Claude Code

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 <date> 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 <date>'.

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) <noreply@anthropic.com>
@DTTerastar
DTTerastar merged commit f0b52ff into main Apr 22, 2026
1 check passed
DTTerastar pushed a commit that referenced this pull request May 19, 2026
…#48)

Wires liftoff-export-cli into the cross-CLI compat suite for §4
(--format) of CONTRACT.md, mirroring crono's machine-attestation
pattern.

- compat_contract_test.go (build tag: compat) runs
  compat/formats.RunContract against the built binary via
  LIFTOFF_EXPORT_BIN.
- New CI job builds the binary, runs `go mod tidy`, then
  `go test -tags=compat -run TestContractFormats ./...`.
- Existing `go vet + test` job kept untouched.
- Subcommands list targets the four data-producing leaves that take
  --format without a required positional: `workouts list`,
  `workouts stats`, `bodyweights list`, `bodyweights stats`. Excludes
  `workouts show` because its mandatory <date> positional would
  conflate "missing arg" with "bad format" in parse-level subtests.
- SkipDataPath: true — liftoff's data path needs a stored OAuth
  token at ~/.config/liftoff-export/auth.json which CI does not
  provision; parse-level subtests still attest the §4 surface.
- SupportedFormats: [markdown, json] — CSV is not yet wired, so
  CSVHasHeader skips with a named reason rather than failing.
- go.mod pinned to merged-main pseudo-version v0.0.0-…-de9219e03369
  (PR #12 nested-subcommand support + PR #14 SkipDataPath).

Closes liftoff side of [QUA-16](/QUA/issues/QUA-16). Follows the
crono pattern landed in [QUA-14](/QUA/issues/QUA-14).

Co-authored-by: Terastar-Paperclip <noreply@paperclip.ing>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant