Templates: add duration funcs (#29368)

This commit is contained in:
andig 2026-04-23 16:46:28 +02:00 • committed by GitHub
parent a1634e25bb
commit 289bcdb185
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 3 deletions

View file

@ -78,9 +78,11 @@ func FuncMap(tmpl *template.Template) *template.Template {
}
return res
},
"urlEncode": url.QueryEscape,
"unquote": unquote,
"quote": yamlQuote,
"urlEncode": url.QueryEscape,
"unquote": unquote,
"quote": yamlQuote,
"asDuration": asDuration,
"durationSeconds": durationSeconds,
}
return tmpl.Funcs(sprig.FuncMap()).Funcs(funcMap)

View file

@ -0,0 +1,72 @@
package templates
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
)
// -----------------------------------------------------------------------------
// Duration helpers (numeric-only returns)
// Source: https://github.com/Masterminds/sprig/pull/467
// -----------------------------------------------------------------------------
// asDuration converts common template values into a time.Duration.
//
// Supported inputs:
// - time.Duration
// - string duration values parsed by time.ParseDuration (e.g. "1h2m3s")
// - numeric strings treated as seconds (e.g. "2.5")
// - ints and uints treated as seconds
// - floats treated as seconds
func asDuration(v any) (time.Duration, error) {
switch x := v.(type) {
case time.Duration:
return x, nil
case string:
s := strings.TrimSpace(x)
if s == "" {
return 0, fmt.Errorf("empty duration")
}
if d, err := time.ParseDuration(s); err == nil {
return d, nil
}
if f, err := strconv.ParseFloat(s, 64); err == nil {
return time.Duration(f * float64(time.Second)), nil
}
return 0, fmt.Errorf("could not parse duration %q", x)
case nil:
return 0, fmt.Errorf("invalid duration")
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return time.Duration(rv.Int()) * time.Second, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
u := rv.Uint()
if u > uint64(math.MaxInt64) {
return 0, fmt.Errorf("duration seconds overflow: %d", u)
}
return time.Duration(int64(u)) * time.Second, nil
case reflect.Float32, reflect.Float64:
return time.Duration(rv.Float() * float64(time.Second)), nil
default:
return 0, fmt.Errorf("unsupported duration type %T", v)
}
}
// durationSeconds converts a duration to seconds (float64).
// On error it returns 0.
func durationSeconds(v any) float64 {
d, err := asDuration(v)
if err != nil {
return 0
}
return d.Seconds()
}