shithub: hugo

Download patch

ref: f4bf214137ebd24a0d12f16d3a98d9038e6eabd3
parent: 0462c96a5a9da3e8adc78d96acd39575a8b46c40
author: Bjørn Erik Pedersen <[email protected]>
date: Fri Sep 8 13:58:39 EDT 2017

tpl/time: Add time.Duration and time.ParseDuration template funcs

And with time.Duration with the convenient alias `duration`:

```
{{ mul 60 60 | duration "second" }}
```

Fixes #3828

--- a/tpl/time/init.go
+++ b/tpl/time/init.go
@@ -65,6 +65,20 @@
 			},
 		)
 
+		ns.AddMethodMapping(ctx.Duration,
+			[]string{"duration"},
+			[][2]string{
+				{`{{ mul 60 60 | duration "second" }}`, `1h0m0s`},
+			},
+		)
+
+		ns.AddMethodMapping(ctx.ParseDuration,
+			nil,
+			[][2]string{
+				{`{{ "1h12m10s" | time.ParseDuration }}`, `1h12m10s`},
+			},
+		)
+
 		return ns
 
 	}
--- a/tpl/time/time.go
+++ b/tpl/time/time.go
@@ -14,6 +14,7 @@
 package time
 
 import (
+	"fmt"
 	_time "time"
 
 	"github.com/spf13/cast"
@@ -53,4 +54,53 @@
 // Now returns the current local time.
 func (ns *Namespace) Now() _time.Time {
 	return _time.Now()
+}
+
+// ParseDuration parses a duration string.
+// A duration string is a possibly signed sequence of
+// decimal numbers, each with optional fraction and a unit suffix,
+// such as "300ms", "-1.5h" or "2h45m".
+// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+// See https://golang.org/pkg/time/#ParseDuration
+func (ns *Namespace) ParseDuration(in interface{}) (_time.Duration, error) {
+	s, err := cast.ToStringE(in)
+	if err != nil {
+		return 0, err
+	}
+
+	return _time.ParseDuration(s)
+}
+
+var durationUnits = map[string]_time.Duration{
+	"nanosecond":  _time.Nanosecond,
+	"ns":          _time.Nanosecond,
+	"microsecond": _time.Microsecond,
+	"us":          _time.Microsecond,
+	"µs":          _time.Microsecond,
+	"millisecond": _time.Millisecond,
+	"ms":          _time.Millisecond,
+	"second":      _time.Second,
+	"s":           _time.Second,
+	"minute":      _time.Minute,
+	"m":           _time.Minute,
+	"hour":        _time.Hour,
+	"h":           _time.Hour,
+}
+
+// Duration converts the given number to a time.Duration.
+// Unit is one of nanosecond/ns, microsecond/us/µs, millisecond/ms, second/s, minute/m or hour/h.
+func (ns *Namespace) Duration(unit interface{}, number interface{}) (_time.Duration, error) {
+	unitStr, err := cast.ToStringE(unit)
+	if err != nil {
+		return 0, err
+	}
+	unitDuration, found := durationUnits[unitStr]
+	if !found {
+		return 0, fmt.Errorf("%q is not a valid duration unit", unit)
+	}
+	n, err := cast.ToInt64E(number)
+	if err != nil {
+		return 0, err
+	}
+	return _time.Duration(n) * unitDuration, nil
 }
--- a/tpl/time/time_test.go
+++ b/tpl/time/time_test.go
@@ -55,3 +55,46 @@
 		}
 	}
 }
+
+func TestDuration(t *testing.T) {
+	t.Parallel()
+
+	ns := New()
+
+	for i, test := range []struct {
+		unit   interface{}
+		num    interface{}
+		expect interface{}
+	}{
+		{"nanosecond", 10, 10 * time.Nanosecond},
+		{"ns", 10, 10 * time.Nanosecond},
+		{"microsecond", 20, 20 * time.Microsecond},
+		{"us", 20, 20 * time.Microsecond},
+		{"µs", 20, 20 * time.Microsecond},
+		{"millisecond", 20, 20 * time.Millisecond},
+		{"ms", 20, 20 * time.Millisecond},
+		{"second", 30, 30 * time.Second},
+		{"s", 30, 30 * time.Second},
+		{"minute", 20, 20 * time.Minute},
+		{"m", 20, 20 * time.Minute},
+		{"hour", 20, 20 * time.Hour},
+		{"h", 20, 20 * time.Hour},
+		{"hours", 20, false},
+		{"hour", "30", 30 * time.Hour},
+	} {
+		result, err := ns.Duration(test.unit, test.num)
+		if b, ok := test.expect.(bool); ok && !b {
+			if err == nil {
+				t.Errorf("[%d] Duration didn't return an expected error, got %v", i, result)
+			}
+		} else {
+			if err != nil {
+				t.Errorf("[%d] Duration failed: %s", i, err)
+				continue
+			}
+			if result != test.expect {
+				t.Errorf("[%d] Duration got %v but expected %v", i, result, test.expect)
+			}
+		}
+	}
+}