Skip to content

Commit 2789b73

Browse files
committed
feat(core): add support for relative date parsing
1 parent 39cc477 commit 2789b73

File tree

9 files changed

+113
-6
lines changed

9 files changed

+113
-6
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require (
1717
github.com/getsentry/raven-go v0.2.0
1818
github.com/gorilla/websocket v1.4.2
1919
github.com/hashicorp/go-version v1.2.0
20+
github.com/karrick/tparse v2.4.2+incompatible
2021
github.com/kr/pretty v0.1.0 // indirect
2122
github.com/mattn/go-colorable v0.1.4
2223
github.com/mattn/go-isatty v0.0.11

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
3939
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
4040
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
4141
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
42+
github.com/karrick/tparse v1.0.0 h1:qVJoscl1sG/UodDmNjjY6cSIun7s541PNNE42dqstfg=
43+
github.com/karrick/tparse v2.4.2+incompatible h1:+cW306qKAzrASC5XieHkgN7/vPaGKIuK62Q7nI7DIRc=
44+
github.com/karrick/tparse v2.4.2+incompatible/go.mod h1:ASPA+vrIcN1uEW6BZg8vfWbzm69ODPSYZPU6qJyfdK0=
4245
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
4346
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
4447
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

internal/args/errors.go

+16
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,19 @@ func missingIndices(index, length int) string {
187187
}
188188
return strings.Join(s, ",")
189189
}
190+
191+
type CannotParseDateError struct {
192+
ArgValue string
193+
AbsoluteTimeParseError error
194+
RelativeTimeParseError error
195+
}
196+
197+
func (e *CannotParseDateError) Error() string {
198+
return fmt.Sprintf(`date parsing error: could not parse %s as either an absolute time (RFC3339) nor a relative time (now(+/-)RFC3339)
199+
200+
Try using RFC3339 for absolute time '2006-01-02T15:04:05Z07:00'. You can also have relative time with 'now +/- a duration' ex: 'now+1d-3w4mo+7y6h4m'
201+
202+
AbsoluteTimeParseError: %s
203+
RelativeTimeParseError: %s
204+
`, e.ArgValue, e.AbsoluteTimeParseError, e.RelativeTimeParseError)
205+
}

internal/args/unmarshal.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"time"
1515

1616
"github.com/dustin/go-humanize"
17+
"github.com/karrick/tparse"
1718
"github.com/scaleway/scaleway-sdk-go/scw"
1819
"github.com/scaleway/scaleway-sdk-go/strcase"
1920
"github.com/scaleway/scaleway-sdk-go/validation"
@@ -61,13 +62,24 @@ var unmarshalFuncs = map[reflect.Type]UnmarshalFunc{
6162

6263
reflect.TypeOf((*time.Time)(nil)).Elem(): func(value string, dest interface{}) error {
6364
// Handle absolute time
64-
t, err := time.Parse(time.RFC3339, value)
65-
if err != nil {
66-
return err
65+
absoluteTimeParsed, absoluteErr := time.Parse(time.RFC3339, value)
66+
// Handle relative time
67+
relativeTimeParsed, relativeErr := tparse.ParseNow(time.RFC3339, value)
68+
69+
if absoluteErr == nil {
70+
*(dest.(*time.Time)) = absoluteTimeParsed
71+
return nil
72+
}
73+
if relativeErr == nil {
74+
*(dest.(*time.Time)) = relativeTimeParsed
75+
return nil
6776
}
6877

69-
*(dest.(*time.Time)) = t
70-
return nil
78+
return &CannotParseDateError{
79+
ArgValue: value,
80+
AbsoluteTimeParseError: absoluteErr,
81+
RelativeTimeParseError: relativeErr,
82+
}
7183
},
7284
}
7385

internal/args/unmarshal_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,33 @@ func TestUnmarshalStruct(t *testing.T) {
247247
},
248248
}))
249249

250+
t.Run("Relative date positive", run(TestCase{
251+
args: []string{
252+
"time=now+1m1s",
253+
},
254+
expected: &WellKnownTypes{
255+
Time: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
256+
},
257+
}))
258+
259+
t.Run("Relative date negative", run(TestCase{
260+
args: []string{
261+
"time=now-1m1s",
262+
},
263+
expected: &WellKnownTypes{
264+
Time: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
265+
},
266+
}))
267+
268+
t.Run("Unknown relative date markers", run(TestCase{
269+
args: []string{
270+
"time=-1R",
271+
},
272+
expected: &WellKnownTypes{
273+
Time: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
274+
},
275+
}))
276+
250277
t.Run("nested-basic", run(TestCase{
251278
args: []string{
252279
"basic.string=test",

internal/core/bootstrap.go

+6
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
144144
}
145145
}
146146

147+
// We use this map for relative time parsing.
148+
// Using a tparse.ParseWithMap instead of tparse.ParseNow allows us to mock it in testing
149+
timeMap := make(map[string]time.Time)
150+
timeMap["now"] = time.Now()
151+
147152
// Meta store globally available variables like SDK client.
148153
// Meta is injected in a context object that will be passed to all commands.
149154
meta := &meta{
@@ -156,6 +161,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
156161
OverrideExec: config.OverrideExec,
157162
ConfigPathFlag: configPathFlag,
158163
Logger: log,
164+
TimeMap: timeMap,
159165

160166
stdout: config.Stdout,
161167
stderr: config.Stderr,

internal/core/context.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"os"
88
"path"
9+
"time"
910

1011
"github.com/scaleway/scaleway-sdk-go/scw"
1112
)
@@ -31,6 +32,7 @@ type meta struct {
3132
result interface{}
3233
httpClient *http.Client
3334
isClientFromBootstrapConfig bool
35+
TimeMap map[string]time.Time
3436
}
3537

3638
type contextKey int
@@ -166,7 +168,11 @@ func ExtractProfileFlag(ctx context.Context) string {
166168
return extractMeta(ctx).ProfileFlag
167169
}
168170

169-
// GetDocGenContext reuturn a minimal context that can be used by scw-doc-gen
171+
func ExtractTimeMap(ctx context.Context) map[string]time.Time {
172+
return extractMeta(ctx).TimeMap
173+
}
174+
175+
// GetDocGenContext return a minimal context that can be used by scw-doc-gen
170176
func GetDocGenContext() context.Context {
171177
ctx := context.Background()
172178
client, _ := scw.NewClient(

internal/namespaces/help/custom.go

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ func GetCommands() *core.Commands {
1111
return core.NewCommands(
1212
helpRoot(),
1313
newHelpCommand("output", shortOutput, longOutput),
14+
newHelpCommand("date", shortDate, longDate),
1415
)
1516
}
1617

internal/namespaces/help/date.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package help
2+
3+
const (
4+
shortDate = "Get help about how date parsing works in the CLI"
5+
longDate = `Date parsing
6+
7+
You have two ways for managing date in the CLI: Absolute and Relative
8+
9+
- Absolute time
10+
11+
Absolute time refers to a specific and absolute point in time.
12+
CLI uses RFC3339 to parse those time and pass a time.Time go structure to the underlying functions.
13+
14+
Example: "2006-01-02T15:04:05Z07:00"
15+
16+
- Relative time
17+
18+
Relative time refers to a time calculated from adding a given duration to the time when a command is launched.
19+
20+
Example: now+1d4m => current time plus 1 day and 4 minutes
21+
22+
- Units of time
23+
24+
Nanosecond: ns
25+
Microsecond: us, µs (U+00B5 = micro symbol), μs (U+03BC = Greek letter mu)
26+
Millisecond: ms
27+
Second: s, sec, second, seconds
28+
Minute: m, min, minute, minutes
29+
Hour: h, hr, hour, hours
30+
Day: d, day, days
31+
Week: w, wk, week, weeks
32+
Month: mo, mon, month, months
33+
Year: y, yr, year, years
34+
`
35+
)

0 commit comments

Comments
 (0)