diff --git a/date.go b/date.go index ed022dda..8c0d6f8a 100644 --- a/date.go +++ b/date.go @@ -143,10 +143,27 @@ func toDate(fmt, str string) time.Time { return t } +func toDateInZone(fmt, str string, timezone string) time.Time { + z, err := time.LoadLocation(timezone) + if err != nil { + z, _ = time.LoadLocation("UTC") + } + t, _ := time.ParseInLocation(fmt, str, z) + return t +} + func mustToDate(fmt, str string) (time.Time, error) { return time.ParseInLocation(fmt, str, time.Local) } +func mustToDateInZone(fmt, str string, timezone string) (time.Time, error) { + z, err := time.LoadLocation(timezone) + if err != nil { + return time.Time{}, err + } + return time.ParseInLocation(fmt, str, z) +} + func unixEpoch(date time.Time) string { return strconv.FormatInt(date.Unix(), 10) } diff --git a/date_test.go b/date_test.go index cf4d4a88..dbf982a5 100644 --- a/date_test.go +++ b/date_test.go @@ -95,6 +95,41 @@ func TestDateInZone(t *testing.T) { } } +func TestToDateInZone(t *testing.T) { + tpl := `{{ date_in_zone "02 Jan 06 15:04 MST" (toDateInZone "2006-01-02 15:04" "2025-10-20 19:30" "Europe/Paris") "UTC" }}` + if err := runt(tpl, "20 Oct 25 17:30 UTC"); err != nil { + t.Error(err) + } + + tpl = `{{ date_in_zone "02 Jan 06 15:04 MST" (toDateInZone "2006-01-02 15:04" "2025-11-20 19:30" "Europe/Paris") "UTC" }}` + if err := runt(tpl, "20 Nov 25 18:30 UTC"); err != nil { + t.Error(err) + } + + tpl = `{{ date_in_zone "02 Jan 06 15:04 MST" (toDateInZone "2006-01-02 15:04" "2025-10-20 19:30" "Europe/Paris") "Europe/London" }}` + if err := runt(tpl, "20 Oct 25 18:30 BST"); err != nil { + t.Error(err) + } + + // Test case of invalid timezone + tpl = `{{ date_in_zone "02 Jan 06 15:04 MST" (toDateInZone "2006-01-02 15:04" "2025-11-20 19:30" "Europe/Paris") "foobar" }}` + if err := runt(tpl, "20 Nov 25 18:30 UTC"); err != nil { + t.Error(err) + } +} + +func TestMustToDateInZone(t *testing.T) { + tpl := `{{ date_in_zone "02 Jan 06 15:04 MST" (mustToDateInZone "2006-01-02 15:04" "2025-10-20 19:30" "Europe/Paris") "UTC" }}` + if err := runt(tpl, "20 Oct 25 17:30 UTC"); err != nil { + t.Error(err) + } + + tpl = `{{ date_in_zone "02 Jan 06 15:04 MST" (mustToDateInZone "2006-01-02 15:04" "2025-11-20 19:30" "Europe/Paris") "UTC" }}` + if err := runt(tpl, "20 Nov 25 18:30 UTC"); err != nil { + t.Error(err) + } +} + func TestDuration(t *testing.T) { tpl := "{{ duration .Secs }}" if err := runtv(tpl, "1m1s", map[string]interface{}{"Secs": "61"}); err != nil { diff --git a/docs/date.md b/docs/date.md index 9f02651e..98f40bf9 100644 --- a/docs/date.md +++ b/docs/date.md @@ -124,3 +124,18 @@ This is useful when you want to convert a string date to another format ``` toDate "2006-01-02" "2017-12-31" | date "02/01/2006" ``` + +## toDateInZone, mustToDateInZone + +`toDateInZone` converts a string to a date in the given timezone. The first +argument is the date layout, the second the date string and the third the timezone. +If the string can't be converted it returns the zero value, if the timezone can't be +converted it returns the date in UTC. +`mustToDateInZone` will return an error in case the string or timezone cannot be converted. + +The example below converts "2025-10-20 19:30" in "CET" to "20 Oct 25 17:30 UTC". + +``` +dateInZone "02 Jan 06 15:04 MST" (toDateInZone "2006-01-02 15:04" "2025-10-20 19:30" "Europe/Paris") "UTC" }} +``` + diff --git a/functions.go b/functions.go index cda47d26..3534135a 100644 --- a/functions.go +++ b/functions.go @@ -110,8 +110,10 @@ var genericMap = map[string]interface{}{ "must_date_modify": mustDateModify, "mustDateModify": mustDateModify, "mustToDate": mustToDate, + "mustToDateInZone": mustToDateInZone, "now": time.Now, "toDate": toDate, + "toDateInZone": toDateInZone, "unixEpoch": unixEpoch, // Strings