package ionet import ( "encoding/json" "strings" "time" "github.com/samber/lo" ) // decodeWithFlexibleTimes unmarshals API responses while tolerating timestamp strings // that omit timezone information by normalizing them to RFC3339Nano. func decodeWithFlexibleTimes(data []byte, target interface{}) error { var intermediate interface{} if err := json.Unmarshal(data, &intermediate); err != nil { return err } normalized := normalizeTimeValues(intermediate) reencoded, err := json.Marshal(normalized) if err != nil { return err } return json.Unmarshal(reencoded, target) } func decodeData[T any](data []byte, target *T) error { var wrapper struct { Data T `json:"data"` } if err := json.Unmarshal(data, &wrapper); err != nil { return err } *target = wrapper.Data return nil } func decodeDataWithFlexibleTimes[T any](data []byte, target *T) error { var wrapper struct { Data T `json:"data"` } if err := decodeWithFlexibleTimes(data, &wrapper); err != nil { return err } *target = wrapper.Data return nil } func normalizeTimeValues(value interface{}) interface{} { switch v := value.(type) { case map[string]interface{}: return lo.MapValues(v, func(val interface{}, _ string) interface{} { return normalizeTimeValues(val) }) case []interface{}: return lo.Map(v, func(item interface{}, _ int) interface{} { return normalizeTimeValues(item) }) case string: if normalized, changed := normalizeTimeString(v); changed { return normalized } return v default: return value } } func normalizeTimeString(input string) (string, bool) { trimmed := strings.TrimSpace(input) if trimmed == "" { return input, false } if _, err := time.Parse(time.RFC3339Nano, trimmed); err == nil { return trimmed, trimmed != input } if _, err := time.Parse(time.RFC3339, trimmed); err == nil { return trimmed, trimmed != input } layouts := []string{ "2006-01-02T15:04:05.999999999", "2006-01-02T15:04:05.999999", "2006-01-02T15:04:05", } for _, layout := range layouts { if parsed, err := time.Parse(layout, trimmed); err == nil { return parsed.UTC().Format(time.RFC3339Nano), true } } return input, false }