diff --git a/funcs/time.go b/funcs/time.go index 61c984d7d..604d6a6ab 100644 --- a/funcs/time.go +++ b/funcs/time.go @@ -1,7 +1,11 @@ package funcs import ( + "fmt" + "strconv" + "strings" "sync" + gotime "time" "github.com/hairyhenderson/gomplate/time" ) @@ -34,3 +38,61 @@ func (f *TimeFuncs) ZoneName() string { func (f *TimeFuncs) ZoneOffset() int { return time.ZoneOffset() } + +// Parse - +func (f *TimeFuncs) Parse(layout, value string) (gotime.Time, error) { + return gotime.Parse(layout, value) +} + +// Now - +func (f *TimeFuncs) Now() gotime.Time { + return gotime.Now() +} + +// Unix - convert UNIX time (in seconds since the UNIX epoch) into a time.Time for further processing +// Takes a string or number (int or float) +func (f *TimeFuncs) Unix(in interface{}) (gotime.Time, error) { + sec, nsec, err := parseNum(in) + if err != nil { + return gotime.Time{}, err + } + return gotime.Unix(sec, nsec), nil +} + +// convert a number input to a pair of int64s, representing the integer portion and the decimal remainder +// this can handle a string as well as any integer or float type +// precision is at the "nano" level (i.e. 1e+9) +func parseNum(in interface{}) (integral int64, fractional int64, err error) { + if s, ok := in.(string); ok { + ss := strings.Split(s, ".") + if len(ss) > 2 { + return 0, 0, fmt.Errorf("can not parse '%s' as a number - too many decimal points", s) + } + if len(ss) == 1 { + integral, err := strconv.ParseInt(s, 0, 64) + return integral, 0, err + } + integral, err := strconv.ParseInt(ss[0], 0, 64) + if err != nil { + return integral, 0, err + } + fractional, err = strconv.ParseInt(ss[1], 0, 64) + return integral, fractional, err + } + if s, ok := in.(fmt.Stringer); ok { + return parseNum(s.String()) + } + if i, ok := in.(int); ok { + return int64(i), 0, nil + } + if u, ok := in.(uint64); ok { + return int64(u), 0, nil + } + if f, ok := in.(float64); ok { + return 0, 0, fmt.Errorf("can not parse floating point number (%f) - use a string instead", f) + } + if in == nil { + return 0, 0, nil + } + return 0, 0, nil +} diff --git a/funcs/time_test.go b/funcs/time_test.go new file mode 100644 index 000000000..7d6674006 --- /dev/null +++ b/funcs/time_test.go @@ -0,0 +1,52 @@ +package funcs + +import ( + "math" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseNum(t *testing.T) { + i, f, _ := parseNum("42") + assert.Equal(t, int64(42), i) + assert.Equal(t, int64(0), f) + + i, f, _ = parseNum(42) + assert.Equal(t, int64(42), i) + assert.Equal(t, int64(0), f) + + i, f, _ = parseNum(big.NewInt(42)) + assert.Equal(t, int64(42), i) + assert.Equal(t, int64(0), f) + + i, f, _ = parseNum(big.NewFloat(42.0)) + assert.Equal(t, int64(42), i) + assert.Equal(t, int64(0), f) + + i, f, _ = parseNum(uint64(math.MaxInt64)) + assert.Equal(t, int64(uint64(math.MaxInt64)), i) + assert.Equal(t, int64(0), f) + + i, f, _ = parseNum("9223372036854775807.9223372036854775807") + assert.Equal(t, int64(9223372036854775807), i) + assert.Equal(t, int64(9223372036854775807), f) + + _, _, err := parseNum("bogus.9223372036854775807") + assert.Error(t, err) + + _, _, err = parseNum("bogus") + assert.Error(t, err) + + _, _, err = parseNum("1.2.3") + assert.Error(t, err) + + _, _, err = parseNum(1.1) + assert.Error(t, err) + + i, f, err = parseNum(nil) + assert.Zero(t, i) + assert.Zero(t, f) + assert.NoError(t, err) +}