From 44f3347f13195a5cb9624b35d4574ebeac7eb5af Mon Sep 17 00:00:00 2001 From: math-nao Date: Sat, 25 Jul 2020 19:39:27 +0200 Subject: [PATCH 1/2] Add support for time zone validation --- baked_in.go | 27 +++++++++++++++++++++++++++ doc.go | 8 ++++++++ validator_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/baked_in.go b/baked_in.go index 36e8057..4d7d917 100644 --- a/baked_in.go +++ b/baked_in.go @@ -174,6 +174,7 @@ var ( "lowercase": isLowercase, "uppercase": isUppercase, "datetime": isDatetime, + "timeZone": isTimeZone, } ) @@ -2096,3 +2097,29 @@ func isDatetime(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } + +// isTimeZone is the validation function for validating if the current field's value is a valid time zone string. +func isTimeZone(fl FieldLevel) bool { + field := fl.Field() + + if field.Kind() == reflect.String { + // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name + if field.String() == "" { + return false + } + + // Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name + if strings.ToLower(field.String()) == "local" { + return false + } + + _, err := time.LoadLocation(field.String()) + if err != nil { + return false + } + + return true + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} diff --git a/doc.go b/doc.go index 4aba75f..814a6cf 100644 --- a/doc.go +++ b/doc.go @@ -1088,6 +1088,14 @@ Supplied format must match the official Go time format layout as documented in h Usage: datetime=2006-01-02 +TimeZone + +This validates that a string value is a valid time zone based on the time zone database present on the system. +Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator. +More information on https://golang.org/pkg/time/#LoadLocation + + Usage: timeZone + Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be diff --git a/validator_test.go b/validator_test.go index e76a3cd..519a2b2 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9201,3 +9201,44 @@ func TestDatetimeValidation(t *testing.T) { _ = validate.Var(2, "datetime") }, "Bad field type int") } + +func TestTimeZoneValidation(t *testing.T) { + tests := []struct { + value string `validate:"timeZone"` + tag string + expected bool + }{ + // systems may have different time zone database, some systems time zone are case insensitive + {"America/New_York", `timeZone`, true}, + {"UTC", `timeZone`, true}, + {"", `timeZone`, false}, + {"Local", `timeZone`, false}, + {"Unknown", `timeZone`, false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.value, test.tag) + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d time zone failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d time zone failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "timeZone" { + t.Fatalf("Index: %d time zone failed Error: %s", i, errs) + } + } + } + } + + PanicMatches(t, func() { + _ = validate.Var(2, "timeZone") + }, "Bad field type int") +} From 7dbe685a0221ea3b620dd70a3f250a4826880134 Mon Sep 17 00:00:00 2001 From: math-nao Date: Sun, 2 Aug 2020 13:59:46 +0200 Subject: [PATCH 2/2] Update timeZone tag name to timezone --- baked_in.go | 2 +- doc.go | 2 +- validator_test.go | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/baked_in.go b/baked_in.go index 4d7d917..9708165 100644 --- a/baked_in.go +++ b/baked_in.go @@ -174,7 +174,7 @@ var ( "lowercase": isLowercase, "uppercase": isUppercase, "datetime": isDatetime, - "timeZone": isTimeZone, + "timezone": isTimeZone, } ) diff --git a/doc.go b/doc.go index 814a6cf..afdb2ab 100644 --- a/doc.go +++ b/doc.go @@ -1094,7 +1094,7 @@ This validates that a string value is a valid time zone based on the time zone d Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator. More information on https://golang.org/pkg/time/#LoadLocation - Usage: timeZone + Usage: timezone Alias Validators and Tags diff --git a/validator_test.go b/validator_test.go index 519a2b2..8e39e0c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -9204,16 +9204,16 @@ func TestDatetimeValidation(t *testing.T) { func TestTimeZoneValidation(t *testing.T) { tests := []struct { - value string `validate:"timeZone"` + value string `validate:"timezone"` tag string expected bool }{ // systems may have different time zone database, some systems time zone are case insensitive - {"America/New_York", `timeZone`, true}, - {"UTC", `timeZone`, true}, - {"", `timeZone`, false}, - {"Local", `timeZone`, false}, - {"Unknown", `timeZone`, false}, + {"America/New_York", `timezone`, true}, + {"UTC", `timezone`, true}, + {"", `timezone`, false}, + {"Local", `timezone`, false}, + {"Unknown", `timezone`, false}, } validate := New() @@ -9231,7 +9231,7 @@ func TestTimeZoneValidation(t *testing.T) { t.Fatalf("Index: %d time zone failed Error: %s", i, errs) } else { val := getError(errs, "", "") - if val.Tag() != "timeZone" { + if val.Tag() != "timezone" { t.Fatalf("Index: %d time zone failed Error: %s", i, errs) } } @@ -9239,6 +9239,6 @@ func TestTimeZoneValidation(t *testing.T) { } PanicMatches(t, func() { - _ = validate.Var(2, "timeZone") + _ = validate.Var(2, "timezone") }, "Bad field type int") }