Skip to content

Commit

Permalink
feat: support datetime format variants
Browse files Browse the repository at this point in the history
  • Loading branch information
macrat committed Nov 26, 2022
1 parent 5703e93 commit d1e1cdd
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 14 deletions.
4 changes: 2 additions & 2 deletions internal/endpoint/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ func getTimeQuery(queries url.Values, name string, default_ time.Time) (time.Tim
return default_, nil
}

t, err := time.Parse(time.RFC3339, q)
t, err := api.ParseTime(q)
if err != nil {
return default_, fmt.Errorf("invalid %s format: %w", name, err)
return default_, fmt.Errorf("invalid %s format: %q", name, q)
}
return t, nil
}
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func ParseAlertPluginArgsFrom(args []string) (AlertPluginArgs, error) {
return AlertPluginArgs{}, ayderr.New(ErrInvalidArgumentValue, err, "invalid alert URL")
}

timestamp, err := time.Parse(time.RFC3339, args[2])
timestamp, err := ParseTime(args[2])
if err != nil {
return AlertPluginArgs{}, ayderr.New(ErrInvalidArgumentValue, err, "invalid timestamp")
}
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestParseAlertPluginArgs(t *testing.T) {
"",
"",
nil,
`invalid timestamp: parsing time "this is not a time" as "2006-01-02T15:04:05Z07:00": cannot parse "this is not a time" as "2006"`,
`invalid timestamp: invalid format: "this is not a time"`,
},
{
[]string{"./ayd-test-alert", "foo:bar", "2001-02-03T16:05:06Z", "HEALTHY", "not a latency", "bar:baz", "foo bar", "{}"},
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (ph *ProbeHistory) UnmarshalJSON(data []byte) error {
return err
}

updated, err := time.Parse(time.RFC3339, jh.Updated)
updated, err := ParseTime(jh.Updated)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions lib-ayd/incident.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func (i *Incident) UnmarshalJSON(data []byte) error {
return err
}

startsAt, err := time.Parse(time.RFC3339, ji.StartsAt)
startsAt, err := ParseTime(ji.StartsAt)
if err != nil {
return err
}

var endsAt time.Time
if ji.EndsAt != "" {
endsAt, err = time.Parse(time.RFC3339, ji.EndsAt)
endsAt, err = ParseTime(ji.EndsAt)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (r *Record) UnmarshalJSON(data []byte) error {
} else {
if s, ok := value.(string); !ok {
return ayderr.New(ErrInvalidRecord, nil, "invalid record: time: should be a string")
} else if r.Time, err = time.Parse(time.RFC3339, s); err != nil {
} else if r.Time, err = ParseTime(s); err != nil {
return ayderr.New(ErrInvalidRecord, err, "invalid record: time")
}
delete(raw, "time")
Expand Down
8 changes: 4 additions & 4 deletions lib-ayd/record_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (

func FuzzParseRecord(f *testing.F) {
f.Add(`{"time":"2021-01-02T15:04:05+09:00", "status":"HEALTHY", "latency":123.456, "target":"ping:example.com", "message":"hello world"}`)
f.Add(`{"time":"2021-01-02T15:04:05+09:00", "status":"FAILURE", "latency":123.456, "target":"exec:/path/to/file.sh", "message":"hello world"}`)
f.Add(`{"time":"2021-01-02T15:04:05+09:00", "status":"ABORTED", "latency":1234.567, "target":"dummy:#hello", "message":"hello world"}`)
f.Add(`{"time":"2021-01-02T15:04:05+09:00", "status":"DEGRADE", "latency":1.234, "target":"dummy:"}`)
f.Add(`{"time":"2021-01-02T15:04:05+09:00", "status":"DEGRADE", "latency":1.234, "target":"dummy:", "extra":123.456, "hello":"world"}`)
f.Add(`{"time":"2021-01-02_15:04:05+09:00", "status":"FAILURE", "latency":123.456, "target":"exec:/path/to/file.sh", "message":"hello world"}`)
f.Add(`{"time":"2021-01-02 15:04:05+09", "status":"ABORTED", "latency":1234.567, "target":"dummy:#hello", "message":"hello world"}`)
f.Add(`{"time":"2021-01-02T15:04:05+0900", "status":"DEGRADE", "latency":1.234, "target":"dummy:"}`)
f.Add(`{"time":"20210102T150405+09:00", "status":"DEGRADE", "latency":1.234, "target":"dummy:", "extra":123.456, "hello":"world"}`)
f.Add(`{"time":"2001-02-03T04:05:06-10:00", "status":"HEALTHY", "latency":1234.456, "target":"https://example.com/path/to/healthz", "message":"hello\tworld"}`)
f.Add(`{"time":"1234-10-30T22:33:44Z", "status":"FAILURE", "latency":0.123, "target":"source+http://example.com/hello/world", "message":"this is test\nhello"}`)
f.Add(`{"time":"2000-10-23T14:56:37Z", "status":"ABORTED", "latency":987654.321, "target":"alert:foobar:alert-url", "message":"cancelled"}`)
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestRecord(t *testing.T) {
},
{
String: `{"time":"2021/01/02 15:04:05", "status":"HEALTHY", "latency":123.456, "target":"ping:example.com", "message":"hello world"}`,
Error: `invalid record: time: parsing time "2021/01/02 15:04:05" as "2006-01-02T15:04:05Z07:00": cannot parse "/01/02 15:04:05" as "-"`,
Error: `invalid record: time: invalid format: "2021/01/02 15:04:05"`,
},
{
String: `{"time":"2021-01-02T15:04:05+09:00", "status":"HEALTHY", "latency":123.456, "target":"::invalid target::", "message":"hello world"}`,
Expand Down
2 changes: 1 addition & 1 deletion lib-ayd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (r *Report) UnmarshalJSON(data []byte) error {
return err
}

reportedAt, err := time.Parse(time.RFC3339, jr.ReportedAt)
reportedAt, err := ParseTime(jr.ReportedAt)
if err != nil {
return err
}
Expand Down
49 changes: 49 additions & 0 deletions lib-ayd/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ayd

import (
"errors"
"fmt"
"strings"
"time"
)

var (
dateformats = []string{
"2006-01-02T",
"2006-01-02_",
"2006-01-02 ",
"20060102 ",
"20060102T",
"20060102_",
}
timeformats = []string{
"15:04:05",
"15:04:05.999999999",
"150405",
"150405.999999999",
}
zoneformats = []string{
"Z07:00",
"Z0700",
"Z07",
}

ErrInvalidTime = errors.New("invalid format")
)

// ParseTime parses time string in Ayd way.
// This function supports RFC3339 and some variant formats.
func ParseTime(s string) (time.Time, error) {
x := strings.ToUpper(strings.TrimSpace(s))
for _, df := range dateformats {
for _, tf := range timeformats {
for _, zf := range zoneformats {
t, err := time.Parse(df+tf+zf, x)
if err == nil {
return t, nil
}
}
}
}
return time.Time{}, fmt.Errorf("%w: %q", ErrInvalidTime, s)
}
70 changes: 70 additions & 0 deletions lib-ayd/time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ayd_test

import (
"errors"
"testing"
"time"

"github.com/macrat/ayd/lib-ayd"
)

func TestParseTime_valid(t *testing.T) {
tests := []string{
"2000-01-02T00:03:04+00:00",
"2000-01-02T00:03:04+0000",
"2000-01-02T00:03:04-00:00",
"20000102T00:03:04-0000",
"20000102T00:03:04.897+00:00",
"20000102T00:03:04.897-00:00",
"20000102T00:03:04.897010Z",
"20000102T000304Z",
"20000102T00:03:04z",
"2000-01-02T08:48:04+08:45",
"2000-01-02T08:48:04+0845",
"2000-01-02T09:03:04+09:00",
"2000-01-02T09:03:04+0900",
"2000-01-02T090304.897+09:00",
"2000-01-02T090304.897010+09:00",
"2000-01-02T090304.897010+0900",
"2000-01-02t000304Z",
"2000-01-02t000304z",
"2000-01-02_00:03:04.897010Z",
"2000-01-02_00:03:04.897010z",
"2000-01-02_00:03:04.897z",
"20000102_00:03:04Z",
"20000102_00:03:04z",
"20000102 00:03:04+00:00",
"20000102 00:03:04-0000",
"2000-01-02 00:03:04.897-00:00",
"2000-01-02 00:03:04.897010z",
"2000-01-02 00:03:04.897Z",
"2000-01-02 00:03:04Z",
"2000-01-02 000304z",
"2000-01-02 090304+09",
"2000-01-02 09:03:04.897010+09",
}

want := time.Date(2000, 1, 2, 0, 3, 4, 0, time.UTC)

for _, tt := range tests {
actual, err := ayd.ParseTime(tt)
if err != nil {
t.Errorf("failed to parse %q: %s", tt, err)
} else if want.Unix() != actual.Truncate(time.Second).Unix() {
t.Errorf("unexpected result from %q: %s", tt, actual)
}
}
}

func TestParseTime_invalid(t *testing.T) {
tests := []string{
"2000/01/02 00:03:04",
}

for _, tt := range tests {
_, err := ayd.ParseTime(tt)
if !errors.Is(err, ayd.ErrInvalidTime) {
t.Errorf("unexpected error from %q: %s", tt, err)
}
}
}

0 comments on commit d1e1cdd

Please sign in to comment.