Add date and tz functions to templates (#3812)

* Add date and tz functions to templates

This commit adds the date and tz functions to templates. This means
users can now format time in a specified format and also change
the timezone to their specific locale.

An example of how these functions work, and can be composed together,
can be seen here:

	{{ .StartsAt | tz "Europe/Paris" | date "15:04:05 MST" }}

Signed-off-by: George Robinson <george.robinson@grafana.com>

---------

Signed-off-by: George Robinson <george.robinson@grafana.com>
This commit is contained in:
George Robinson 2024-04-22 15:15:33 +01:00 committed by GitHub
parent cb9724db47
commit dc1e1a2b88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 6 deletions

View File

@ -95,3 +95,5 @@ templating.
| join | sep string, s []string | [strings.Join](http://golang.org/pkg/strings/#Join), concatenates the elements of s to create a single string. The separator string sep is placed between elements in the resulting string. (note: argument order inverted for easier pipelining in templates.) | | join | sep string, s []string | [strings.Join](http://golang.org/pkg/strings/#Join), concatenates the elements of s to create a single string. The separator string sep is placed between elements in the resulting string. (note: argument order inverted for easier pipelining in templates.) |
| safeHtml | text string | [html/template.HTML](https://golang.org/pkg/html/template/#HTML), Marks string as HTML not requiring auto-escaping. | | safeHtml | text string | [html/template.HTML](https://golang.org/pkg/html/template/#HTML), Marks string as HTML not requiring auto-escaping. |
| stringSlice | ...string | Returns the passed strings as a slice of strings. | | stringSlice | ...string | Returns the passed strings as a slice of strings. |
| date | string, time.Time | Returns the text representation of the time in the specified format. For documentation on formats refer to [pkg.go.dev/time](https://pkg.go.dev/time#pkg-constants). |
| tz | string, time.Time | Returns the time in the timezone. For example, Europe/Paris. |

View File

@ -192,6 +192,18 @@ var DefaultFuncs = FuncMap{
"stringSlice": func(s ...string) []string { "stringSlice": func(s ...string) []string {
return s return s
}, },
// date returns the text representation of the time in the specified format.
"date": func(fmt string, t time.Time) string {
return t.Format(fmt)
},
// tz returns the time in the timezone.
"tz": func(name string, t time.Time) (time.Time, error) {
loc, err := time.LoadLocation(name)
if err != nil {
return time.Time{}, err
}
return t.In(loc), nil
},
} }
// Pair is a key/value string pair. // Pair is a key/value string pair.

View File

@ -473,10 +473,11 @@ func TestTemplateFuncs(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
for _, tc := range []struct { for _, tc := range []struct {
title string title string
in string in string
data interface{} data interface{}
exp string exp string
expErr string
}{{ }{{
title: "Template using toUpper", title: "Template using toUpper",
in: `{{ "abc" | toUpper }}`, in: `{{ "abc" | toUpper }}`,
@ -506,6 +507,21 @@ func TestTemplateFuncs(t *testing.T) {
title: "Template using reReplaceAll", title: "Template using reReplaceAll",
in: `{{ reReplaceAll "ab" "AB" "abc" }}`, in: `{{ reReplaceAll "ab" "AB" "abc" }}`,
exp: "ABc", exp: "ABc",
}, {
title: "Template using date",
in: `{{ . | date "2006-01-02" }}`,
data: time.Date(2024, 1, 1, 8, 15, 30, 0, time.UTC),
exp: "2024-01-01",
}, {
title: "Template using tz",
in: `{{ . | tz "Europe/Paris" }}`,
data: time.Date(2024, 1, 1, 8, 15, 30, 0, time.UTC),
exp: "2024-01-01 09:15:30 +0100 CET",
}, {
title: "Template using invalid tz",
in: `{{ . | tz "Invalid/Timezone" }}`,
data: time.Date(2024, 1, 1, 8, 15, 30, 0, time.UTC),
expErr: "template: :1:7: executing \"\" at <tz \"Invalid/Timezone\">: error calling tz: unknown time zone Invalid/Timezone",
}} { }} {
tc := tc tc := tc
t.Run(tc.title, func(t *testing.T) { t.Run(tc.title, func(t *testing.T) {
@ -515,8 +531,13 @@ func TestTemplateFuncs(t *testing.T) {
go func() { go func() {
defer wg.Done() defer wg.Done()
got, err := tmpl.ExecuteTextString(tc.in, tc.data) got, err := tmpl.ExecuteTextString(tc.in, tc.data)
require.NoError(t, err) if tc.expErr == "" {
require.Equal(t, tc.exp, got) require.NoError(t, err)
require.Equal(t, tc.exp, got)
} else {
require.EqualError(t, err, tc.expErr)
require.Empty(t, got)
}
}() }()
} }
wg.Wait() wg.Wait()