From 1eec6cd5f55b26f5f14bc0e397c6bbdedd498d0a Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Mon, 23 Oct 2023 22:24:41 +0200 Subject: [PATCH] Add full support for iso8601 parsing Signed-off-by: Lorenzo --- go.mod | 1 + go.sum | 2 ++ ocpp1.6/types/datetime.go | 46 +++++++++++++++++++++---------------- ocpp2.0.1/types/datetime.go | 46 ++++++++++++++++++++----------------- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 38b2fd2a..7dba0b0b 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/gorilla/websocket v1.4.1 github.com/kr/pretty v0.1.0 // indirect github.com/leodido/go-urn v1.1.0 // indirect + github.com/relvacode/iso8601 v1.3.0 // indirect github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.8.0 golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect diff --git a/go.sum b/go.sum index 502662eb..b1223cf9 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/relvacode/iso8601 v1.3.0 h1:HguUjsGpIMh/zsTczGN3DVJFxTU/GX+MMmzcKoMO7ko= +github.com/relvacode/iso8601 v1.3.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/ocpp1.6/types/datetime.go b/ocpp1.6/types/datetime.go index a75101f0..498ae265 100644 --- a/ocpp1.6/types/datetime.go +++ b/ocpp1.6/types/datetime.go @@ -2,14 +2,13 @@ package types import ( "encoding/json" - "strings" + "errors" "time" -) -// ISO8601 time format, assuming Zulu timestamp. -const ISO8601 = "2006-01-02T15:04:05Z" + "github.com/relvacode/iso8601" +) -// DateTimeFormat to be used for all OCPP messages. +// DateTimeFormat to be used when serializing all OCPP messages. // // The default dateTime format is RFC3339. // Change this if another format is desired. @@ -25,24 +24,31 @@ func NewDateTime(time time.Time) *DateTime { return &DateTime{Time: time} } +func null(b []byte) bool { + if len(b) != 4 { + return false + } + if b[0] != 'n' && b[1] != 'u' && b[2] != 'l' && b[3] != 'l' { + return false + } + return true +} + func (dt *DateTime) UnmarshalJSON(input []byte) error { - strInput := string(input) - strInput = strings.Trim(strInput, `"`) - if DateTimeFormat == "" { - var defaultTime time.Time - err := json.Unmarshal(input, &defaultTime) - if err != nil { - return err - } - dt.Time = defaultTime.Local() + // Do not parse null timestamps + if null(input) { + return nil + } + // Assert that timestamp is a string + if len(input) > 0 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] } else { - newTime, err := time.Parse(DateTimeFormat, strInput) - if err != nil { - return err - } - dt.Time = newTime.Local() + return errors.New("timestamp not enclosed in double quotes") } - return nil + // Parse ISO8601 + var err error + dt.Time, err = iso8601.Parse(input) + return err } func (dt *DateTime) MarshalJSON() ([]byte, error) { diff --git a/ocpp2.0.1/types/datetime.go b/ocpp2.0.1/types/datetime.go index b9ba6f9e..498ae265 100644 --- a/ocpp2.0.1/types/datetime.go +++ b/ocpp2.0.1/types/datetime.go @@ -2,11 +2,13 @@ package types import ( "encoding/json" - "strings" + "errors" "time" + + "github.com/relvacode/iso8601" ) -// DateTimeFormat to be used for all OCPP messages. +// DateTimeFormat to be used when serializing all OCPP messages. // // The default dateTime format is RFC3339. // Change this if another format is desired. @@ -22,36 +24,38 @@ func NewDateTime(time time.Time) *DateTime { return &DateTime{Time: time} } -// Creates a new DateTime struct, embedding a struct generated using time.Now(). -func Now() *DateTime { - return &DateTime{Time: time.Now()} +func null(b []byte) bool { + if len(b) != 4 { + return false + } + if b[0] != 'n' && b[1] != 'u' && b[2] != 'l' && b[3] != 'l' { + return false + } + return true } func (dt *DateTime) UnmarshalJSON(input []byte) error { - strInput := string(input) - strInput = strings.Trim(strInput, `"`) - if DateTimeFormat == "" { - var defaultTime time.Time - err := json.Unmarshal(input, &defaultTime) - if err != nil { - return err - } - dt.Time = defaultTime.Local() + // Do not parse null timestamps + if null(input) { + return nil + } + // Assert that timestamp is a string + if len(input) > 0 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] } else { - newTime, err := time.Parse(DateTimeFormat, strInput) - if err != nil { - return err - } - dt.Time = newTime.Local() + return errors.New("timestamp not enclosed in double quotes") } - return nil + // Parse ISO8601 + var err error + dt.Time, err = iso8601.Parse(input) + return err } func (dt *DateTime) MarshalJSON() ([]byte, error) { if DateTimeFormat == "" { return json.Marshal(dt.Time) } - timeStr := FormatTimestamp(dt.Time) + timeStr := dt.FormatTimestamp() return json.Marshal(timeStr) }