fix(monitor/collectors/coinbase): parse Coinbase's Go-style time format in heartbeat current_time

Coinbase Advanced Trade WS leaks Go's time.Time.String() format in
heartbeat events (e.g. "2026-05-02 07:55:50.784... +0000 UTC m=+102632...")
which is not RFC 3339 — UnmarshalJSON into time.Time fails and the entire
envelope errors. Decode current_time as a string and parse it with a
fallback that handles both RFC 3339 and the Go-string variant (with the
m=+monotonic suffix stripped).
This commit is contained in:
CarterPerez-dev 2026-05-02 04:02:49 -04:00
parent 2ccc379255
commit 989e53c958
1 changed files with 19 additions and 2 deletions

View File

@ -8,6 +8,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/coder/websocket"
@ -137,7 +138,23 @@ type rawEnvelope struct {
type rawEnvelopeEv struct {
Type string `json:"type"`
Tickers []TickerEntry `json:"tickers,omitempty"`
CurrentTime time.Time `json:"current_time,omitempty"`
CurrentTime string `json:"current_time,omitempty"`
}
func parseCoinbaseTime(s string) time.Time {
if s == "" {
return time.Time{}
}
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
return t
}
if idx := strings.Index(s, " m=+"); idx > 0 {
s = s[:idx]
}
if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil {
return t
}
return time.Time{}
}
var ErrFrameMalformed = errors.New("coinbase: malformed frame")
@ -169,7 +186,7 @@ func decodeFrame(msg []byte) (Frame, error) {
case channelHeartbeats:
frame.Kind = FrameTypeHeartbeats
if len(env.Events) > 0 {
frame.HeartbeatTime = env.Events[0].CurrentTime
frame.HeartbeatTime = parseCoinbaseTime(env.Events[0].CurrentTime)
}
return frame, nil