fix: format daily metric query timestamps
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Queryer interface {
|
||||
@@ -63,11 +64,14 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
|
||||
var out []MetricRow
|
||||
for rows.Next() {
|
||||
var row MetricRow
|
||||
var statDate scanDate
|
||||
var createdAt scanDateTime
|
||||
var updatedAt scanDateTime
|
||||
var first sql.NullFloat64
|
||||
var latest sql.NullFloat64
|
||||
if err := rows.Scan(
|
||||
&row.VIN,
|
||||
&row.StatDate,
|
||||
&statDate,
|
||||
&row.Protocol,
|
||||
&row.MetricKey,
|
||||
&row.MetricValue,
|
||||
@@ -76,11 +80,14 @@ func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]Metr
|
||||
&latest,
|
||||
&row.SampleCount,
|
||||
&row.CalculationMethod,
|
||||
&row.CreatedAt,
|
||||
&row.UpdatedAt,
|
||||
&createdAt,
|
||||
&updatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.StatDate = statDate.String
|
||||
row.CreatedAt = createdAt.String
|
||||
row.UpdatedAt = updatedAt.String
|
||||
if first.Valid {
|
||||
row.FirstTotalMileageKM = &first.Float64
|
||||
}
|
||||
@@ -239,3 +246,36 @@ func writeMetricError(w http.ResponseWriter, status int, message string) {
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"error": message})
|
||||
}
|
||||
|
||||
type scanDate struct {
|
||||
String string
|
||||
}
|
||||
|
||||
func (s *scanDate) Scan(value any) error {
|
||||
s.String = formatSQLTime(value, "2006-01-02")
|
||||
return nil
|
||||
}
|
||||
|
||||
type scanDateTime struct {
|
||||
String string
|
||||
}
|
||||
|
||||
func (s *scanDateTime) Scan(value any) error {
|
||||
s.String = formatSQLTime(value, "2006-01-02 15:04:05")
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatSQLTime(value any, layout string) string {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return ""
|
||||
case time.Time:
|
||||
return typed.Format(layout)
|
||||
case []byte:
|
||||
return strings.TrimSpace(string(typed))
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
default:
|
||||
return strings.TrimSpace(fmt.Sprint(typed))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
@@ -24,8 +25,8 @@ func TestMetricRepositoryQueriesDailyMetricsWithFilters(t *testing.T) {
|
||||
"first_total_mileage_km", "latest_total_mileage_km", "sample_count", "calculation_method",
|
||||
"created_at", "updated_at",
|
||||
}).AddRow(
|
||||
"LKLG7C4E3NA774736", "2026-07-01", "JT808", MetricDailyTotalMileageKM, 12345.6, "km",
|
||||
12345.6, 12345.6, 13, "TOTAL_MILEAGE_DIFF", "2026-07-01 22:49:11", "2026-07-01 23:09:36",
|
||||
"LKLG7C4E3NA774736", time.Date(2026, 7, 1, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)), "JT808", MetricDailyTotalMileageKM, 12345.6, "km",
|
||||
12345.6, 12345.6, 13, "TOTAL_MILEAGE_DIFF", time.Date(2026, 7, 1, 22, 49, 11, 0, time.FixedZone("Asia/Shanghai", 8*3600)), time.Date(2026, 7, 1, 23, 9, 36, 0, time.FixedZone("Asia/Shanghai", 8*3600)),
|
||||
))
|
||||
|
||||
repository := NewMetricRepository(db)
|
||||
@@ -45,6 +46,9 @@ func TestMetricRepositoryQueriesDailyMetricsWithFilters(t *testing.T) {
|
||||
if rows[0].MetricKey != MetricDailyTotalMileageKM || rows[0].MetricValue != 12345.6 {
|
||||
t.Fatalf("unexpected row: %#v", rows[0])
|
||||
}
|
||||
if rows[0].StatDate != "2026-07-01" || rows[0].UpdatedAt != "2026-07-01 23:09:36" {
|
||||
t.Fatalf("unexpected time formatting: %#v", rows[0])
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatalf("sql expectations: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user