diff --git a/go/vehicle-gateway/internal/history/query.go b/go/vehicle-gateway/internal/history/query.go index a78628c4..e68e75e2 100644 --- a/go/vehicle-gateway/internal/history/query.go +++ b/go/vehicle-gateway/internal/history/query.go @@ -807,11 +807,17 @@ func validDateTime(value string) bool { func normalizeDateTimeLiteral(value string) string { value = strings.TrimSpace(value) - if len(value) == len("2006-01-02T15:04:05") && value[10] == 'T' { - return value[:10] + " " + value[11:] + if value == "" { + return "" + } + shanghai := time.FixedZone("Asia/Shanghai", 8*3600) + for _, layout := range []string{"2006-01-02T15:04:05", "2006-01-02 15:04:05"} { + if parsed, err := time.ParseInLocation(layout, value, shanghai); err == nil { + return parsed.UTC().Format("2006-01-02 15:04:05") + } } if parsed, err := time.Parse(time.RFC3339, value); err == nil { - return parsed.In(time.FixedZone("Asia/Shanghai", 8*3600)).Format("2006-01-02 15:04:05") + return parsed.UTC().Format("2006-01-02 15:04:05") } return value } diff --git a/go/vehicle-gateway/internal/history/query_test.go b/go/vehicle-gateway/internal/history/query_test.go index ed4b0f12..e59b98ff 100644 --- a/go/vehicle-gateway/internal/history/query_test.go +++ b/go/vehicle-gateway/internal/history/query_test.go @@ -263,15 +263,17 @@ func TestParseRawFrameQueryAcceptsDatetimeLocalValues(t *testing.T) { if err != nil { t.Fatalf("parseRawFrameQuery() error = %v", err) } - if query.DateFrom != "2026-07-01 00:00:00" || query.DateTo != "2026-07-02 00:00:00" { + if query.DateFrom != "2026-06-30 16:00:00" || query.DateTo != "2026-07-01 16:00:00" { t.Fatalf("date range = %q -> %q", query.DateFrom, query.DateTo) } } -func TestNormalizeDateTimeLiteralConvertsRFC3339ToShanghaiTime(t *testing.T) { +func TestNormalizeDateTimeLiteralConvertsInputToTDengineUTCTime(t *testing.T) { for raw, want := range map[string]string{ - "2026-07-01T00:00:00+08:00": "2026-07-01 00:00:00", - "2026-06-30T16:00:00Z": "2026-07-01 00:00:00", + "2026-07-01T00:00:00": "2026-06-30 16:00:00", + "2026-07-01 00:00:00": "2026-06-30 16:00:00", + "2026-07-01T00:00:00+08:00": "2026-06-30 16:00:00", + "2026-06-30T16:00:00Z": "2026-06-30 16:00:00", } { if got := normalizeDateTimeLiteral(raw); got != want { t.Fatalf("normalizeDateTimeLiteral(%q) = %q, want %q", raw, got, want) @@ -307,7 +309,7 @@ func TestBuildMileagePointSQLUsesLiteralsForTDengine(t *testing.T) { for _, want := range []string{ "protocol = 'JT808'", "vehicle_key = 'JT808:013307811350'", - "ts >= '2026-07-02 00:00:00'", + "ts >= '2026-07-01 16:00:00'", "LIMIT 20 OFFSET 5", } { if !strings.Contains(sqlText, want) { @@ -331,7 +333,7 @@ func TestBuildLocationSQLUsesLiteralsForTDengine(t *testing.T) { for _, want := range []string{ "protocol = 'JT808'", "vehicle_key = 'JT808:013307811350'", - "ts >= '2026-07-02 00:00:00'", + "ts >= '2026-07-01 16:00:00'", "LIMIT 20 OFFSET 5", } { if !strings.Contains(sqlText, want) { @@ -359,8 +361,8 @@ func TestBuildRawFrameSQLUsesLiteralsForTDengine(t *testing.T) { "vehicle_key = 'JT808:013307811350'", "vin = 'VIN''1'", "message_id = 512", - "ts >= '2026-07-01 00:00:00'", - "ts <= '2026-07-01 23:59:59'", + "ts >= '2026-06-30 16:00:00'", + "ts <= '2026-07-01 15:59:59'", "LIMIT 20 OFFSET 5", } { if !strings.Contains(sqlText, want) {