Skip to content

Commit c68441b

Browse files
author
Dean Karn
authored
Merge pull request #575 from psampaz/datetime
Add datetime validation
2 parents b7430f8 + 912fcd1 commit c68441b

File tree

5 files changed

+89
-4
lines changed

5 files changed

+89
-4
lines changed

baked_in.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ var (
171171
"hostname_port": isHostnamePort,
172172
"lowercase": isLowercase,
173173
"uppercase": isUppercase,
174+
"datetime": isDatetime,
174175
}
175176
)
176177

@@ -2020,8 +2021,9 @@ func isJSON(fl FieldLevel) bool {
20202021
if field.Kind() == reflect.String {
20212022
val := field.String()
20222023
return json.Valid([]byte(val))
2023-
}
2024-
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2024+
}
2025+
2026+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
20252027
}
20262028

20272029
// isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address.
@@ -2070,3 +2072,20 @@ func isUppercase(fl FieldLevel) bool {
20702072

20712073
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
20722074
}
2075+
2076+
// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
2077+
func isDatetime(fl FieldLevel) bool {
2078+
field := fl.Field()
2079+
param := fl.Param()
2080+
2081+
if field.Kind() == reflect.String {
2082+
_, err := time.Parse(param, field.String())
2083+
if err != nil {
2084+
return false
2085+
}
2086+
2087+
return true
2088+
}
2089+
2090+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2091+
}

doc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,13 @@ can be used to valiate fields typically passed to sockets and connections.
10621062
10631063
Usage: hostname_port
10641064
1065+
Datetime
1066+
1067+
This validates that a string value is a valid datetime based on the supplied datetime format.
1068+
Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/
1069+
1070+
Usage: datetime=2006-01-02
1071+
10651072
Alias Validators and Tags
10661073
10671074
NOTE: When returning an error, the tag returned in "FieldError" will be

translations/en/en.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
13241324
{
13251325
tag: "json",
13261326
translation: "{0} must be a valid json string",
1327-
override: false,
1327+
override: false,
13281328
},
13291329
{
13301330
tag: "lowercase",
@@ -1336,6 +1336,21 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
13361336
translation: "{0} must be an uppercase string",
13371337
override: false,
13381338
},
1339+
{
1340+
tag: "datetime",
1341+
translation: "{0} does not match the {1} format",
1342+
override: false,
1343+
customTransFunc: func(ut ut.Translator, fe validator.FieldError) string {
1344+
1345+
t, err := ut.T(fe.Tag(), fe.Field(), fe.Param())
1346+
if err != nil {
1347+
log.Printf("warning: error translating FieldError: %#v", fe)
1348+
return fe.(error).Error()
1349+
}
1350+
1351+
return t
1352+
},
1353+
},
13391354
}
13401355

13411356
for _, t := range translations {

translations/en/en_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ func TestTranslations(t *testing.T) {
144144
JSONString string `validate:"json"`
145145
LowercaseString string `validate:"lowercase"`
146146
UppercaseString string `validate:"uppercase"`
147+
Datetime string `validate:"datetime=2006-01-02"`
147148
}
148149

149150
var test Test
@@ -195,6 +196,7 @@ func TestTranslations(t *testing.T) {
195196

196197
test.UniqueSlice = []string{"1234", "1234"}
197198
test.UniqueMap = map[string]string{"key1": "1234", "key2": "1234"}
199+
test.Datetime = "2008-Feb-01"
198200

199201
err = validate.Struct(test)
200202
NotEqual(t, err, nil)
@@ -650,6 +652,10 @@ func TestTranslations(t *testing.T) {
650652
ns: "Test.UppercaseString",
651653
expected: "UppercaseString must be an uppercase string",
652654
},
655+
{
656+
ns: "Test.Datetime",
657+
expected: "Datetime does not match the 2006-01-02 format",
658+
},
653659
}
654660

655661
for _, tt := range tests {

validator_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9155,4 +9155,42 @@ func TestUppercaseValidation(t *testing.T) {
91559155
PanicMatches(t, func() {
91569156
_ = validate.Var(2, "uppercase")
91579157
}, "Bad field type int")
9158-
}
9158+
9159+
}
9160+
9161+
func TestDatetimeValidation(t *testing.T) {
9162+
tests := []struct {
9163+
value string `validate:"datetime=2006-01-02"`
9164+
tag string
9165+
expected bool
9166+
}{
9167+
{"2008-02-01", `datetime=2006-01-02`, true},
9168+
{"2008-Feb-01", `datetime=2006-01-02`, false},
9169+
}
9170+
9171+
validate := New()
9172+
9173+
for i, test := range tests {
9174+
9175+
errs := validate.Var(test.value, test.tag)
9176+
9177+
if test.expected {
9178+
if !IsEqual(errs, nil) {
9179+
t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
9180+
}
9181+
} else {
9182+
if IsEqual(errs, nil) {
9183+
t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
9184+
} else {
9185+
val := getError(errs, "", "")
9186+
if val.Tag() != "datetime" {
9187+
t.Fatalf("Index: %d datetime failed Error: %s", i, errs)
9188+
}
9189+
}
9190+
}
9191+
}
9192+
9193+
PanicMatches(t, func() {
9194+
_ = validate.Var(2, "datetime")
9195+
}, "Bad field type int")
9196+
}

0 commit comments

Comments
 (0)