diff --git a/internal/logic/monitor/api_metrics.go b/internal/logic/monitor/api_metrics.go index 6759a47..9b233ef 100644 --- a/internal/logic/monitor/api_metrics.go +++ b/internal/logic/monitor/api_metrics.go @@ -64,13 +64,22 @@ type trafficFlowRow struct { // normalizeMonitorDateRange 规范化监控类接口的日期范围,返回 YYYY-MM-DD 的 [start, end](含)。 // 缺省近 30 天;end 不超过今天;跨度上限 trafficFlowMaxDays。流量桑基图与模型性能共用。 func normalizeMonitorDateRange(startDate, endDate string) (string, string) { + return normalizeMonitorDateRangeAt(startDate, endDate, time.Now()) +} + +// normalizeMonitorDateRangeAt is separated for deterministic tests. Date-only +// filters must be compared as calendar dates in the server's location, rather +// than against the current instant. Otherwise, on a non-UTC server before its +// UTC offset, today's parsed midnight can incorrectly fall after time.Now() +// and the range silently falls back to the default 30 days. +func normalizeMonitorDateRangeAt(startDate, endDate string, now time.Time) (string, string) { const layout = "2006-01-02" - today := time.Now() - end, err := time.Parse(layout, endDate) + today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + end, err := time.ParseInLocation(layout, endDate, now.Location()) if err != nil || end.After(today) { end = today } - start, err := time.Parse(layout, startDate) + start, err := time.ParseInLocation(layout, startDate, now.Location()) if err != nil || start.After(end) { start = end.AddDate(0, 0, -29) } diff --git a/internal/logic/monitor/api_metrics_test.go b/internal/logic/monitor/api_metrics_test.go index 8307a2d..9ec4a9f 100644 --- a/internal/logic/monitor/api_metrics_test.go +++ b/internal/logic/monitor/api_metrics_test.go @@ -5,11 +5,22 @@ import ( "reflect" "strings" "testing" + "time" v1 "github.com/qianfree/team-api/api/admin/v1" "github.com/qianfree/team-api/internal/logic/common" ) +func TestNormalizeMonitorDateRangeKeepsTodayInNonUTCTimezone(t *testing.T) { + location := time.FixedZone("UTC+8", 8*60*60) + now := time.Date(2026, time.August, 29, 0, 10, 0, 0, location) + + start, end := normalizeMonitorDateRangeAt("2026-08-29", "2026-08-29", now) + if start != "2026-08-29" || end != "2026-08-29" { + t.Fatalf("same-day range = [%s, %s], want [2026-08-29, 2026-08-29]", start, end) + } +} + // perfFieldValue 按 JSON 字段名(snake_case)从结构体取值,整数统一归一为 int64、 // 浮点统一为 float64,与旧的 []map[string]any 断言口径保持一致。 func perfFieldValue(item v1.ModelPerformanceSummary, field string) any {