diff --git a/go/vehicle-gateway/internal/history/query.go b/go/vehicle-gateway/internal/history/query.go index da35b069..12dfaeb3 100644 --- a/go/vehicle-gateway/internal/history/query.go +++ b/go/vehicle-gateway/internal/history/query.go @@ -366,8 +366,8 @@ func normalizeRawFrameQuery(query RawFrameQuery) RawFrameQuery { query.Phone = strings.TrimSpace(query.Phone) query.DeviceID = strings.TrimSpace(query.DeviceID) query.MessageID = strings.TrimSpace(query.MessageID) - query.DateFrom = strings.TrimSpace(query.DateFrom) - query.DateTo = strings.TrimSpace(query.DateTo) + query.DateFrom = normalizeDateTimeLiteral(query.DateFrom) + query.DateTo = normalizeDateTimeLiteral(query.DateTo) if query.Limit <= 0 { query.Limit = 20 } @@ -380,8 +380,8 @@ func normalizeLocationQuery(query LocationQuery) LocationQuery { query.VIN = strings.TrimSpace(query.VIN) query.Phone = strings.TrimSpace(query.Phone) query.DeviceID = strings.TrimSpace(query.DeviceID) - query.DateFrom = strings.TrimSpace(query.DateFrom) - query.DateTo = strings.TrimSpace(query.DateTo) + query.DateFrom = normalizeDateTimeLiteral(query.DateFrom) + query.DateTo = normalizeDateTimeLiteral(query.DateTo) if query.Limit <= 0 { query.Limit = 20 } @@ -394,8 +394,8 @@ func normalizeMileagePointQuery(query MileagePointQuery) MileagePointQuery { query.VIN = strings.TrimSpace(query.VIN) query.Phone = strings.TrimSpace(query.Phone) query.DeviceID = strings.TrimSpace(query.DeviceID) - query.DateFrom = strings.TrimSpace(query.DateFrom) - query.DateTo = strings.TrimSpace(query.DateTo) + query.DateFrom = normalizeDateTimeLiteral(query.DateFrom) + query.DateTo = normalizeDateTimeLiteral(query.DateTo) if query.Limit <= 0 { query.Limit = 20 } @@ -482,10 +482,10 @@ func rawFrameWhere(query RawFrameQuery) []string { } } if query.DateFrom != "" { - add("ts >= '" + quote(query.DateFrom) + "'") + add("ts >= '" + quote(normalizeDateTimeLiteral(query.DateFrom)) + "'") } if query.DateTo != "" { - add("ts <= '" + quote(query.DateTo) + "'") + add("ts <= '" + quote(normalizeDateTimeLiteral(query.DateTo)) + "'") } return where } @@ -511,10 +511,10 @@ func locationWhere(query LocationQuery) []string { add("device_id = '" + quote(query.DeviceID) + "'") } if query.DateFrom != "" { - add("ts >= '" + quote(query.DateFrom) + "'") + add("ts >= '" + quote(normalizeDateTimeLiteral(query.DateFrom)) + "'") } if query.DateTo != "" { - add("ts <= '" + quote(query.DateTo) + "'") + add("ts <= '" + quote(normalizeDateTimeLiteral(query.DateTo)) + "'") } return where } @@ -540,10 +540,10 @@ func mileagePointWhere(query MileagePointQuery) []string { add("device_id = '" + quote(query.DeviceID) + "'") } if query.DateFrom != "" { - add("ts >= '" + quote(query.DateFrom) + "'") + add("ts >= '" + quote(normalizeDateTimeLiteral(query.DateFrom)) + "'") } if query.DateTo != "" { - add("ts <= '" + quote(query.DateTo) + "'") + add("ts <= '" + quote(normalizeDateTimeLiteral(query.DateTo)) + "'") } return where } @@ -793,7 +793,7 @@ func parseMessageID(value string) (int64, bool) { } func validDateTime(value string) bool { - value = strings.TrimSpace(value) + value = normalizeDateTimeLiteral(value) if value == "" { return true } @@ -805,6 +805,14 @@ func validDateTime(value string) bool { return false } +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:] + } + return value +} + func safeIdentifier(value string) bool { if value == "" { return false diff --git a/go/vehicle-gateway/internal/history/query_test.go b/go/vehicle-gateway/internal/history/query_test.go index 1c206078..bd657c0d 100644 --- a/go/vehicle-gateway/internal/history/query_test.go +++ b/go/vehicle-gateway/internal/history/query_test.go @@ -256,6 +256,18 @@ func TestRawFrameHandlerRejectsInvalidLimit(t *testing.T) { } } +func TestParseRawFrameQueryAcceptsDatetimeLocalValues(t *testing.T) { + request := httptest.NewRequest(http.MethodGet, "/api/history/raw-frames?dateFrom=2026-07-01T00:00:00&dateTo=2026-07-02T00:00:00", nil) + + query, err := parseRawFrameQuery(request) + 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" { + t.Fatalf("date range = %q -> %q", query.DateFrom, query.DateTo) + } +} + func TestParseMessageIDSupportsDecimalAndHex(t *testing.T) { for raw, want := range map[string]int64{ "512": 512, @@ -323,8 +335,8 @@ func TestBuildRawFrameSQLUsesLiteralsForTDengine(t *testing.T) { VehicleKey: "JT808:013307811350", VIN: "VIN'1", MessageID: "0x0200", - DateFrom: "2026-07-01 00:00:00", - DateTo: "2026-07-01 23:59:59", + DateFrom: "2026-07-01T00:00:00", + DateTo: "2026-07-01T23:59:59", Limit: 20, Offset: 5, }) @@ -336,6 +348,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'", "LIMIT 20 OFFSET 5", } { if !strings.Contains(sqlText, want) {