cmd/uplink: better usability for date specification
I use `uplink share` command but I always fail to set the --not-before parameter. * Usually I try +2d when I see in the help that +2h is possible --> fail * When it fails, I try to set explicit date, like 2012-12-23 --> fail This patch makes it possible to use: * day duration (like +3d) * shorter date definition (like `2023-12-12` or `2023-12-12T12:40`) Change-Id: I2243b36f59c8929eb0473c4bb4fed19220890c71
This commit is contained in:
parent
63fa386b0a
commit
6737d427e4
@ -60,10 +60,10 @@ func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) {
|
||||
|
||||
ap.notBefore = params.Flag("not-before",
|
||||
"Disallow access before this time (e.g. '+2h', 'now', '2020-01-02T15:04:05Z0700', 'none')",
|
||||
nil, clingy.Transform(parseHumanDate), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
|
||||
nil, clingy.Transform(parseHumanDateNotBefore), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
|
||||
ap.notAfter = params.Flag("not-after",
|
||||
"Disallow access after this time (e.g. '+2h', 'now', '2020-01-02T15:04:05Z0700', 'none')",
|
||||
nil, clingy.Transform(parseHumanDate), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
|
||||
nil, clingy.Transform(parseHumanDateNotAfter), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
|
||||
|
||||
if !prefixFlags {
|
||||
ap.prefixes = params.Arg("prefix", "Key prefix access will be restricted to",
|
||||
|
@ -6,6 +6,8 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/clingy"
|
||||
@ -41,6 +43,24 @@ func (s *stdlibFlags) Setup(f clingy.Flags) {
|
||||
// parseHumanDate parses command-line flags which accept relative and absolute datetimes.
|
||||
// It can be passed to clingy.Transform to create a clingy.Option.
|
||||
func parseHumanDate(date string) (time.Time, error) {
|
||||
return parseHumanDateInLocation(date, time.Now().Location(), false)
|
||||
}
|
||||
|
||||
// parseHumanDateNotBefore parses command-line flags which accept relative and absolute datetimes.
|
||||
func parseHumanDateNotBefore(date string) (time.Time, error) {
|
||||
return parseHumanDateInLocation(date, time.Now().Location(), false)
|
||||
}
|
||||
|
||||
// parseHumanDateNotAfter parses relative/short date times. But it rounds up the period.
|
||||
// For example parseHumanDateNotAfter('2022-01-23') will return with '2022-01-23T23:59...' (end of day),
|
||||
// and parseHumanDateNotAfter('2022-01-23T15:04') will return with '2022-01-23T15:04:59...' (end of minute)...
|
||||
func parseHumanDateNotAfter(date string) (time.Time, error) {
|
||||
return parseHumanDateInLocation(date, time.Now().Location(), true)
|
||||
}
|
||||
|
||||
var durationWithDay = regexp.MustCompile(`(\+|-)(\d+)d`)
|
||||
|
||||
func parseHumanDateInLocation(date string, loc *time.Location, ceil bool) (time.Time, error) {
|
||||
switch {
|
||||
case date == "none":
|
||||
return time.Time{}, nil
|
||||
@ -49,17 +69,53 @@ func parseHumanDate(date string) (time.Time, error) {
|
||||
case date == "now":
|
||||
return time.Now(), nil
|
||||
case date[0] == '+' || date[0] == '-':
|
||||
dayDuration := durationWithDay.FindStringSubmatch(date)
|
||||
if len(dayDuration) > 0 {
|
||||
days, _ := strconv.Atoi(dayDuration[2])
|
||||
if dayDuration[1] == "-" {
|
||||
days *= -1
|
||||
}
|
||||
return time.Now().Add(time.Hour * time.Duration(days*24)), nil
|
||||
}
|
||||
|
||||
d, err := time.ParseDuration(date)
|
||||
return time.Now().Add(d), errs.Wrap(err)
|
||||
default:
|
||||
t, err := time.Parse(time.RFC3339, date)
|
||||
if err != nil {
|
||||
d, err := time.ParseDuration(date)
|
||||
if err == nil {
|
||||
return time.Now().Add(d), nil
|
||||
}
|
||||
t, err := time.ParseInLocation(time.RFC3339, date, time.Now().Location())
|
||||
if err == nil {
|
||||
return t, nil
|
||||
}
|
||||
return t, errs.Wrap(err)
|
||||
|
||||
// shorter version of RFC3339
|
||||
t, err = time.ParseInLocation("2006-01-02T15:04:05", date, loc)
|
||||
if err == nil {
|
||||
if ceil {
|
||||
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()+1, -1, loc)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
t, err = time.ParseInLocation("2006-01-02T15:04", date, loc)
|
||||
if err == nil {
|
||||
if ceil {
|
||||
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()+1, 0, -1, loc)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
t, err = time.ParseInLocation("2006-01-02", date, loc)
|
||||
if err == nil {
|
||||
if ceil {
|
||||
t = time.Date(t.Year(), t.Month(), t.Day()+1, 0, 0, 0, -1, loc)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
d, err := time.ParseDuration(date)
|
||||
if err == nil {
|
||||
return time.Now().Add(d), nil
|
||||
}
|
||||
return time.Time{}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,61 @@ import (
|
||||
)
|
||||
|
||||
func TestParseHumanDate(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Asia/Tbilisi")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("parse relative date", func(t *testing.T) {
|
||||
parsed, err := parseHumanDate("+24h")
|
||||
parsed, err := parseHumanDateNotBefore("+24h")
|
||||
require.NoError(t, err)
|
||||
require.Less(t, parsed.Unix(), time.Now().Add(25*time.Hour).Unix())
|
||||
require.Greater(t, parsed.Unix(), time.Now().Add(23*time.Hour).Unix())
|
||||
})
|
||||
|
||||
t.Run("parse absolute date", func(t *testing.T) {
|
||||
parsed, err := parseHumanDate("2030-02-03T12:13:14+01:00")
|
||||
t.Run("parse relative date with day", func(t *testing.T) {
|
||||
parsed, err := parseHumanDateNotBefore("+13d")
|
||||
require.NoError(t, err)
|
||||
require.Less(t, parsed.Unix(), time.Now().Add((13*24+1)*time.Hour).Unix())
|
||||
require.Greater(t, parsed.Unix(), time.Now().Add((13*24-1)*time.Hour).Unix())
|
||||
})
|
||||
|
||||
t.Run("parse absolute full date", func(t *testing.T) {
|
||||
parsed, err := parseHumanDateNotBefore("2030-02-03T12:13:14+01:00")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-02-03T12:13:14+01:00", parsed.Format(time.RFC3339))
|
||||
})
|
||||
|
||||
t.Run("parse absolute date without TZ", func(t *testing.T) {
|
||||
parsed, err := parseHumanDateInLocation("2030-02-03T12:13:14", loc, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-02-03T12:13:14+04:00", parsed.Format(time.RFC3339))
|
||||
|
||||
parsed, err = parseHumanDateInLocation("2030-02-03T12:13:14", loc, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-02-03T12:13:14.999999999+04:00", parsed.Format(time.RFC3339Nano))
|
||||
})
|
||||
|
||||
t.Run("parse absolute date without sec", func(t *testing.T) {
|
||||
parsed, err := parseHumanDateInLocation("2030-02-03T12:13", loc, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-02-03T12:13:00+04:00", parsed.Format(time.RFC3339))
|
||||
|
||||
parsed, err = parseHumanDateInLocation("2030-02-03T12:13", loc, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-02-03T12:13:59.999999999+04:00", parsed.Format(time.RFC3339Nano))
|
||||
})
|
||||
|
||||
t.Run("parse absolute date without hour", func(t *testing.T) {
|
||||
parsed, err := parseHumanDateInLocation("2030-03-31", loc, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-03-31T00:00:00+04:00", parsed.Format(time.RFC3339))
|
||||
|
||||
parsed, err = parseHumanDateInLocation("2030-03-31", loc, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "2030-03-31T23:59:59.999999999+04:00", parsed.Format(time.RFC3339Nano))
|
||||
})
|
||||
|
||||
t.Run("parse nonsense", func(t *testing.T) {
|
||||
parsed, err := parseHumanDate("999999")
|
||||
parsed, err := parseHumanDateNotBefore("999999")
|
||||
require.Equal(t, time.Time{}, parsed)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user