mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-22 23:31:33 +00:00
fix(ingest): accept space-separated and colonless-offset timestamps
This commit is contained in:
1 parent
c9b7010082
commit
fd57c8b00c
2 files changed
+51
-13
No files matched your search
@@ -730,11 +730,7 @@ func (w *Worker) handlePacket(ctx context.Context, iata, pubkeyHex string, raw [
|
||||
w.log.Error(fmt.Sprintf("db: upsert packet failed from %s/%s", iata, pubkeyHex), "error", err)
|
||||
return
|
||||
}
|
||||
// Prefer an explicit RFC3339 offset; legacy timezone-less observers use UTC.
|
||||
heardAt, err := time.Parse(time.RFC3339Nano, envelope.Timestamp)
|
||||
if err != nil {
|
||||
heardAt, err = time.Parse("2006-01-02T15:04:05", envelope.Timestamp)
|
||||
}
|
||||
heardAt, err := parseHeardAt(envelope.Timestamp)
|
||||
if err != nil {
|
||||
w.log.Warn(fmt.Sprintf("failed to parse timestamp %q", envelope.Timestamp), "error", err)
|
||||
heardAt = time.Now().UTC()
|
||||
@@ -959,3 +955,25 @@ func computeTransportCode(key []byte, payloadType uint8, payload []byte) uint16
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// heardAtLayouts lists offset forms first; timezone-less observers are read as UTC.
|
||||
// Fractional seconds are optional in every layout.
|
||||
var heardAtLayouts = []string{
|
||||
time.RFC3339Nano,
|
||||
"2006-01-02 15:04:05.999999999Z07:00",
|
||||
"2006-01-02T15:04:05.999999999Z0700",
|
||||
"2006-01-02 15:04:05.999999999Z0700",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04:05",
|
||||
}
|
||||
|
||||
func parseHeardAt(s string) (time.Time, error) {
|
||||
var err error
|
||||
for _, layout := range heardAtLayouts {
|
||||
var t time.Time
|
||||
if t, err = time.Parse(layout, s); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, err
|
||||
}
|
||||
@@ -49,16 +49,36 @@ func TestPacketTimestampFormatsAndSkew(t *testing.T) {
|
||||
}
|
||||
for _, precision := range []time.Duration{time.Second, time.Microsecond, time.Nanosecond} {
|
||||
expected := want.Truncate(precision)
|
||||
stamp := expected.Format("2006-01-02T15:04:05.999999999")
|
||||
if got := capture(t, stamp); !got.Equal(expected) {
|
||||
t.Fatalf("timezone-less UTC changed: got %s want %s", got, expected)
|
||||
for _, layout := range []string{"2006-01-02T15:04:05.999999999", "2006-01-02 15:04:05.999999999"} {
|
||||
stamp := expected.Format(layout)
|
||||
t.Run("naive "+stamp, func(t *testing.T) {
|
||||
if got := capture(t, stamp); !got.Equal(expected) {
|
||||
t.Fatalf("timezone-less UTC changed: got %s want %s", got, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
west := time.FixedZone("west", -4*60*60)
|
||||
for _, layout := range []string{
|
||||
"2006-01-02 15:04:05.999999-07:00", // Python str(datetime)
|
||||
"2006-01-02T15:04:05.999-0700",
|
||||
"2006-01-02 15:04:05-0700",
|
||||
} {
|
||||
stamp := want.Truncate(time.Microsecond).In(west).Format(layout)
|
||||
expected, _ := time.Parse(layout, stamp)
|
||||
t.Run(stamp, func(t *testing.T) {
|
||||
if got := capture(t, stamp); !got.Equal(expected) {
|
||||
t.Fatalf("reported time changed: got %s want %s", got, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, stamp := range []string{"", "invalid", "2026-09-29T25:00:00+00:00", time.Now().Add(31 * time.Minute).Format(time.RFC3339), time.Now().Add(-31 * time.Minute).Format(time.RFC3339)} {
|
||||
before := time.Now().UTC()
|
||||
got := capture(t, stamp)
|
||||
if got.Before(before) || got.After(time.Now().UTC()) {
|
||||
t.Fatalf("invalid/skewed time escaped server-time guard: %s => %s", stamp, got)
|
||||
}
|
||||
t.Run("guard "+stamp, func(t *testing.T) {
|
||||
before := time.Now().UTC()
|
||||
got := capture(t, stamp)
|
||||
if got.Before(before) || got.After(time.Now().UTC()) {
|
||||
t.Fatalf("invalid/skewed time escaped server-time guard: %s => %s", stamp, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user