From 1bc46c9eff4c8d78f5cda741bd8fff2ffee012aa Mon Sep 17 00:00:00 2001
From: DTTerastar
Date: Mon, 20 Apr 2026 22:00:10 -0400
Subject: [PATCH] fix: resolve --today against local calendar date
--today, --days N, and the default 7-day window previously used
time.Now().UTC(), so in timezones west of UTC, "today" flipped to the
next day shortly after local evening. At 20:xx ET (~00:xx UTC next
day), `crono-export nutrition --today` queried tomorrow's UTC date and
returned an empty result even though the user had logged food "today".
Resolve the reference day from the user's local zone instead, and
construct midnight with time.Date rather than Truncate(24*time.Hour)
(which only aligns to midnight in UTC). Explicit --start/--end string
parsing is unchanged; the fix only affects the three relative modes.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
internal/cronoclient/daterange.go | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/internal/cronoclient/daterange.go b/internal/cronoclient/daterange.go
index 627b3ff..7e5941d 100644
--- a/internal/cronoclient/daterange.go
+++ b/internal/cronoclient/daterange.go
@@ -12,7 +12,11 @@ import (
const dateLayout = "2006-01-02"
-// DateRange is an inclusive [Start, End] window in UTC.
+// DateRange is an inclusive [Start, End] window. Only the calendar date
+// (YYYY-MM-DD) of each endpoint is sent to Cronometer's export endpoints,
+// so the time-of-day and zone on these values don't round-trip — but the
+// calendar date is resolved in the user's local zone so that --today
+// matches the day the user sees in the Cronometer UI.
type DateRange struct {
Start time.Time
End time.Time
@@ -29,17 +33,18 @@ func AddDateRangeFlags(cmd *cobra.Command) {
// ParseDateRangeFromFlags reads the date-range flags off cmd and resolves
// them into a concrete DateRange. Default when no flags are passed: the
-// last 7 days ending today (UTC).
+// last 7 days ending today. "Today" is the user's local calendar day.
func ParseDateRangeFromFlags(cmd *cobra.Command) (DateRange, error) {
startStr, _ := cmd.Flags().GetString("start")
endStr, _ := cmd.Flags().GetString("end")
days, _ := cmd.Flags().GetInt("days")
today, _ := cmd.Flags().GetBool("today")
- return resolveDateRange(startStr, endStr, days, today, time.Now().UTC())
+ return resolveDateRange(startStr, endStr, days, today, time.Now())
}
func resolveDateRange(startStr, endStr string, days int, today bool, ref time.Time) (DateRange, error) {
- now := ref.Truncate(24 * time.Hour)
+ y, m, d := ref.Date()
+ now := time.Date(y, m, d, 0, 0, 0, 0, ref.Location())
var start, end time.Time
switch {