From dc1e1a2b88b71b8c179ff7d1b59d100f3b7dcd6f Mon Sep 17 00:00:00 2001 From: George Robinson Date: Mon, 22 Apr 2024 15:15:33 +0100 Subject: [PATCH] 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 --------- Signed-off-by: George Robinson --- docs/notifications.md | 2 ++ template/template.go | 12 ++++++++++++ template/template_test.go | 33 +++++++++++++++++++++++++++------ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/notifications.md b/docs/notifications.md index 2f60eb9cc6..1a70d4aa0d 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -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.) | | 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. | +| 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. | diff --git a/template/template.go b/template/template.go index 5172ba92a7..a34403d8f4 100644 --- a/template/template.go +++ b/template/template.go @@ -192,6 +192,18 @@ var DefaultFuncs = FuncMap{ "stringSlice": func(s ...string) []string { 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. diff --git a/template/template_test.go b/template/template_test.go index 7e45f25d27..103dd08430 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -473,10 +473,11 @@ func TestTemplateFuncs(t *testing.T) { require.NoError(t, err) for _, tc := range []struct { - title string - in string - data interface{} - exp string + title string + in string + data interface{} + exp string + expErr string }{{ title: "Template using toUpper", in: `{{ "abc" | toUpper }}`, @@ -506,6 +507,21 @@ func TestTemplateFuncs(t *testing.T) { title: "Template using reReplaceAll", in: `{{ reReplaceAll "ab" "AB" "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 : error calling tz: unknown time zone Invalid/Timezone", }} { tc := tc t.Run(tc.title, func(t *testing.T) { @@ -515,8 +531,13 @@ func TestTemplateFuncs(t *testing.T) { go func() { defer wg.Done() got, err := tmpl.ExecuteTextString(tc.in, tc.data) - require.NoError(t, err) - require.Equal(t, tc.exp, got) + if tc.expErr == "" { + require.NoError(t, err) + require.Equal(t, tc.exp, got) + } else { + require.EqualError(t, err, tc.expErr) + require.Empty(t, got) + } }() } wg.Wait()