Skip to content

Commit e875d30

Browse files
shanselmanJanDeDobbeleer
authored andcommitted
fix(nightscout): handle float date values via custom UnmarshalJSON
Some Nightscout API providers (e.g. T1Pal) return the date field as a float (e.g. 1770512410938.386). Go's json.Unmarshal cannot decode a float into an int64 field, causing the segment to silently fail. Add a custom UnmarshalJSON that accepts both integer and floating-point JSON numbers, truncating to int64. This preserves the existing Date int64 type and is fully backward compatible. Fixes #7284
1 parent 7500a46 commit e875d30

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/segments/nightscout.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package segments
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
67
http2 "net/http"
78
"time"
89

@@ -46,6 +47,38 @@ type NightscoutData struct {
4647
Mills int64 `json:"mills"`
4748
}
4849

50+
// UnmarshalJSON handles both integer and floating-point JSON numbers for the date field.
51+
// Some Nightscout API providers (e.g. T1Pal) return the date as a float.
52+
func (n *NightscoutData) UnmarshalJSON(data []byte) error {
53+
type Alias NightscoutData
54+
aux := &struct {
55+
*Alias
56+
Date json.Number `json:"date"`
57+
}{
58+
Alias: (*Alias)(n),
59+
}
60+
61+
if err := json.Unmarshal(data, aux); err != nil {
62+
return err
63+
}
64+
65+
if aux.Date == "" {
66+
return nil
67+
}
68+
69+
if i, err := aux.Date.Int64(); err == nil {
70+
n.Date = i
71+
return nil
72+
}
73+
74+
if f, err := aux.Date.Float64(); err == nil {
75+
n.Date = int64(f)
76+
return nil
77+
}
78+
79+
return fmt.Errorf("date field must be a valid number, got: %s", aux.Date)
80+
}
81+
4982
func (ns *Nightscout) Template() string {
5083
return " {{ .Sgv }} "
5184
}

src/segments/nightscout_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package segments
22

33
import (
4+
"encoding/json"
45
"errors"
56
"testing"
67

@@ -82,6 +83,14 @@ func TestNSSegment(t *testing.T) {
8283
ExpectedString: "\ue2a1 50↓↓",
8384
ExpectedEnabled: true,
8485
},
86+
{
87+
Case: "Float date value",
88+
JSONResponse: `
89+
[{"_id":"619d6fa819696e8ded5b2206","sgv":124,"date":1770512410938.386,"dateString":"2026-02-08T01:00:10.000Z","trend":4,"direction":"Flat","device":"share2","type":"sgv","utcOffset":0,"sysTime":"2026-02-08T01:00:10.000Z","mills":1770512410000}]`, //nolint:lll
90+
Template: "\ue2a1 {{.Sgv}}{{.TrendIcon}}",
91+
ExpectedString: "\ue2a1 124→",
92+
ExpectedEnabled: true,
93+
},
8594
{
8695
Case: "Error in retrieving data",
8796
JSONResponse: "nonsense",
@@ -135,3 +144,50 @@ func TestNSSegment(t *testing.T) {
135144
assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, ns), tc.Case)
136145
}
137146
}
147+
148+
func TestNightscoutDataUnmarshalJSON(t *testing.T) {
149+
cases := []struct {
150+
Case string
151+
JSONInput string
152+
ExpectedDate int64
153+
ExpectError bool
154+
}{
155+
{
156+
Case: "Integer date value",
157+
JSONInput: `{"date": 1637707537000}`,
158+
ExpectedDate: 1637707537000,
159+
},
160+
{
161+
Case: "Floating-point date value",
162+
JSONInput: `{"date": 1637707537000.5}`,
163+
ExpectedDate: 1637707537000,
164+
},
165+
{
166+
Case: "Floating-point date with larger decimal",
167+
JSONInput: `{"date": 1637707537123.789}`,
168+
ExpectedDate: 1637707537123,
169+
},
170+
{
171+
Case: "Invalid date value",
172+
JSONInput: `{"date": "not-a-number"}`,
173+
ExpectError: true,
174+
},
175+
{
176+
Case: "Missing date field",
177+
JSONInput: `{"sgv": 150}`,
178+
},
179+
}
180+
181+
for _, tc := range cases {
182+
var data NightscoutData
183+
err := json.Unmarshal([]byte(tc.JSONInput), &data)
184+
185+
if tc.ExpectError {
186+
assert.Error(t, err, tc.Case)
187+
continue
188+
}
189+
190+
assert.NoError(t, err, tc.Case)
191+
assert.Equal(t, tc.ExpectedDate, data.Date, tc.Case)
192+
}
193+
}

0 commit comments

Comments
 (0)