Repository: go-playground/validator
Pull request #630 (2020-07-03 19:02:46)

Add checksum validation for Ethereum address
Fixes Or Enhances https://github.com/go-playground/validator/issues/631

**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular change.

Change Details:

- Add checksum validation for Ethereum address

@go-playground/admins

deankarn: @elias19r made one suggested change, other than that looks great and can merge next time I merge a group of PR's
deankarn: @elias19r I'll do it during the merge no worries

Patch:
diff --git a/.gitignore b/.gitignore
index 792ca00..6e43fac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 # Folders
 _obj
 _test
+bin
 
 # Architecture specific extensions/prefixes
 *.[568vq]
@@ -26,4 +27,4 @@ _testmain.go
 *.out
 *.txt
 cover.html
-README.html
\ No newline at end of file
+README.html
diff --git a/.travis.yml b/.travis.yml
index f6484f3..85a7be3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
 language: go
 go:
-  - 1.13.7
+  - 1.15.2
   - tip
 matrix:
   allow_failures:
@@ -25,5 +25,5 @@ script:
   - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
 
 after_success: |
-  [ $TRAVIS_GO_VERSION = 1.13.7 ] &&
-  goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
\ No newline at end of file
+  [ $TRAVIS_GO_VERSION = 1.15.2 ] &&
+  goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
diff --git a/README.md b/README.md
index f552826..054f294 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,7 @@ Baked-in Validations
 | contains | Contains |
 | containsany | Contains Any |
 | containsrune | Contains Rune |
+| endswith | Ends With |
 | lowercase | Lowercase |
 | multibyte | Multi-Byte Characters |
 | number | NOT DOCUMENTED IN doc.go |
@@ -145,6 +146,7 @@ Baked-in Validations
 | btc_addr | Bitcoin Address |
 | btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
 | datetime | Datetime |
+| e164 | e164 formatted phone number |
 | email | E-mail String
 | eth_addr | Ethereum Address |
 | hexadecimal | Hexadecimal String |
@@ -185,7 +187,6 @@ Baked-in Validations
 | Tag | Description |
 | - | - |
 | dir | Directory |
-| e164 | NOT DOCUMENTED IN doc.go |
 | endswith | Ends With |
 | excludes | Excludes |
 | excludesall | Excludes All |
@@ -197,10 +198,16 @@ Baked-in Validations
 | min | Minimum |
 | oneof | One Of |
 | required | Required |
+| required_if | Required If |
+| required_unless | Required Unless |
 | required_with | Required With |
 | required_with_all | Required With All |
 | required_without | Required Without |
 | required_without_all | Required Without All |
+| excluded_with | Excluded With |
+| excluded_with_all | Excluded With All |
+| excluded_without | Excluded Without |
+| excluded_without_all | Excluded Without All |
 | unique | Unique |
 
 Benchmarks
diff --git a/baked_in.go b/baked_in.go
index 36e8057..c5729a7 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"context"
 	"crypto/sha256"
+	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -16,6 +17,8 @@ import (
 	"time"
 	"unicode/utf8"
 
+	"golang.org/x/crypto/sha3"
+
 	urn "github.com/leodido/go-urn"
 )
 
@@ -64,10 +67,16 @@ var (
 	// or even disregard and use your own map if so desired.
 	bakedInValidators = map[string]Func{
 		"required":             hasValue,
+		"required_if":          requiredIf,
+		"required_unless":      requiredUnless,
 		"required_with":        requiredWith,
 		"required_with_all":    requiredWithAll,
 		"required_without":     requiredWithout,
 		"required_without_all": requiredWithoutAll,
+		"excluded_with":        excludedWith,
+		"excluded_with_all":    excludedWithAll,
+		"excluded_without":     excludedWithout,
+		"excluded_without_all": excludedWithoutAll,
 		"isdefault":            isDefault,
 		"len":                  hasLengthOf,
 		"min":                  hasMinOf,
@@ -174,6 +183,7 @@ var (
 		"lowercase":            isLowercase,
 		"uppercase":            isUppercase,
 		"datetime":             isDatetime,
+		"timezone":             isTimeZone,
 	}
 )
 
@@ -241,23 +251,33 @@ func isUnique(fl FieldLevel) bool {
 
 	switch field.Kind() {
 	case reflect.Slice, reflect.Array:
+		elem := field.Type().Elem()
+		if elem.Kind() == reflect.Ptr {
+			elem = elem.Elem()
+		}
+
 		if param == "" {
-			m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
+			m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
 
 			for i := 0; i < field.Len(); i++ {
-				m.SetMapIndex(field.Index(i), v)
+				m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
 			}
 			return field.Len() == m.Len()
 		}
 
-		sf, ok := field.Type().Elem().FieldByName(param)
+		sf, ok := elem.FieldByName(param)
 		if !ok {
 			panic(fmt.Sprintf("Bad field name %s", param))
 		}
 
-		m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type()))
+		sfTyp := sf.Type
+		if sfTyp.Kind() == reflect.Ptr {
+			sfTyp = sfTyp.Elem()
+		}
+
+		m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
 		for i := 0; i < field.Len(); i++ {
-			m.SetMapIndex(field.Index(i).FieldByName(param), v)
+			m.SetMapIndex(reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)), v)
 		}
 		return field.Len() == m.Len()
 	case reflect.Map:
@@ -515,7 +535,7 @@ func isISBN10(fl FieldLevel) bool {
 	return checksum%11 == 0
 }
 
-// IsEthereumAddress is the validation function for validating if the field's value is a valid ethereum address based currently only on the format
+// IsEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
 func isEthereumAddress(fl FieldLevel) bool {
 	address := fl.Field().String()
 
@@ -527,7 +547,21 @@ func isEthereumAddress(fl FieldLevel) bool {
 		return true
 	}
 
-	// checksum validation is blocked by https://github.com/golang/crypto/pull/28
+	// Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
+	address = address[2:] // Skip "0x" prefix.
+	h := sha3.NewLegacyKeccak256()
+	// hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash
+	_, _ = h.Write([]byte(strings.ToLower(address)))
+	hash := hex.EncodeToString(h.Sum(nil))
+
+	for i := 0; i < len(address); i++ {
+		if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case.
+			continue
+		}
+		if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' {
+			return false
+		}
+	}
 
 	return true
 }
@@ -1129,7 +1163,7 @@ func isEq(fl FieldLevel) bool {
 		return int64(field.Len()) == p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() == p
 
@@ -1383,6 +1417,75 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
 	}
 }
 
+// requireCheckFieldValue is a func for check field value
+func requireCheckFieldValue(fl FieldLevel, param string, value string, defaultNotFoundValue bool) bool {
+	field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
+	if !found {
+		return defaultNotFoundValue
+	}
+
+	switch kind {
+
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return field.Int() == asInt(value)
+
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return field.Uint() == asUint(value)
+
+	case reflect.Float32, reflect.Float64:
+		return field.Float() == asFloat(value)
+
+	case reflect.Slice, reflect.Map, reflect.Array:
+		return int64(field.Len()) == asInt(value)
+	}
+
+	// default reflect.String:
+	return field.String() == value
+}
+
+// requiredIf is the validation function
+// The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
+func requiredIf(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	if len(params)%2 != 0 {
+		panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
+	}
+	for i := 0; i < len(params); i += 2 {
+		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
+			return true
+		}
+	}
+	return hasValue(fl)
+}
+
+// requiredUnless is the validation function
+// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
+func requiredUnless(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	if len(params)%2 != 0 {
+		panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName()))
+	}
+
+	for i := 0; i < len(params); i += 2 {
+		if requireCheckFieldValue(fl, params[i], params[i+1], false) {
+			return true
+		}
+	}
+	return hasValue(fl)
+}
+
+// ExcludedWith is the validation function
+// The field under validation must not be present or is empty if any of the other specified fields are present.
+func excludedWith(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	for _, param := range params {
+		if !requireCheckFieldKind(fl, param, true) {
+			return !hasValue(fl)
+		}
+	}
+	return true
+}
+
 // RequiredWith is the validation function
 // The field under validation must be present and not empty only if any of the other specified fields are present.
 func requiredWith(fl FieldLevel) bool {
@@ -1395,6 +1498,18 @@ func requiredWith(fl FieldLevel) bool {
 	return true
 }
 
+// ExcludedWithAll is the validation function
+// The field under validation must not be present or is empty if all of the other specified fields are present.
+func excludedWithAll(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	for _, param := range params {
+		if requireCheckFieldKind(fl, param, true) {
+			return true
+		}
+	}
+	return !hasValue(fl)
+}
+
 // RequiredWithAll is the validation function
 // The field under validation must be present and not empty only if all of the other specified fields are present.
 func requiredWithAll(fl FieldLevel) bool {
@@ -1407,6 +1522,15 @@ func requiredWithAll(fl FieldLevel) bool {
 	return hasValue(fl)
 }
 
+// ExcludedWithout is the validation function
+// The field under validation must not be present or is empty when any of the other specified fields are not present.
+func excludedWithout(fl FieldLevel) bool {
+	if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
+		return !hasValue(fl)
+	}
+	return true
+}
+
 // RequiredWithout is the validation function
 // The field under validation must be present and not empty only when any of the other specified fields are not present.
 func requiredWithout(fl FieldLevel) bool {
@@ -1416,6 +1540,18 @@ func requiredWithout(fl FieldLevel) bool {
 	return true
 }
 
+// RequiredWithoutAll is the validation function
+// The field under validation must not be present or is empty when all of the other specified fields are not present.
+func excludedWithoutAll(fl FieldLevel) bool {
+	params := parseOneOfParam2(fl.Param())
+	for _, param := range params {
+		if !requireCheckFieldKind(fl, param, true) {
+			return true
+		}
+	}
+	return !hasValue(fl)
+}
+
 // RequiredWithoutAll is the validation function
 // The field under validation must be present and not empty only when all of the other specified fields are not present.
 func requiredWithoutAll(fl FieldLevel) bool {
@@ -1541,7 +1677,7 @@ func isGte(fl FieldLevel) bool {
 		return int64(field.Len()) >= p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() >= p
 
@@ -1588,7 +1724,7 @@ func isGt(fl FieldLevel) bool {
 		return int64(field.Len()) > p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() > p
 
@@ -1631,7 +1767,7 @@ func hasLengthOf(fl FieldLevel) bool {
 		return int64(field.Len()) == p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() == p
 
@@ -1767,7 +1903,7 @@ func isLte(fl FieldLevel) bool {
 		return int64(field.Len()) <= p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() <= p
 
@@ -1814,7 +1950,7 @@ func isLt(fl FieldLevel) bool {
 		return int64(field.Len()) < p
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		p := asInt(param)
+		p := asIntFromType(field.Type(), param)
 
 		return field.Int() < p
 
@@ -2087,6 +2223,29 @@ func isDatetime(fl FieldLevel) bool {
 
 	if field.Kind() == reflect.String {
 		_, err := time.Parse(param, field.String())
+
+		return err == nil
+	}
+
+	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
 		}
diff --git a/doc.go b/doc.go
index 4aba75f..0ab483b 100644
--- a/doc.go
+++ b/doc.go
@@ -245,6 +245,40 @@ ensures the value is not nil.
 
 	Usage: required
 
+Required If
+
+The field under validation must be present and not empty only if all
+the other specified fields are equal to the value following the specified
+field. For strings ensures value is not "". For slices, maps, pointers,
+interfaces, channels and functions ensures the value is not nil.
+
+	Usage: required_if
+
+Examples:
+
+	// require the field if the Field1 is equal to the parameter given:
+	Usage: required_if=Field1 foobar
+
+	// require the field if the Field1 and Field2 is equal to the value respectively:
+	Usage: required_if=Field1 foo Field2 bar
+
+Required Unless
+
+The field under validation must be present and not empty unless all
+the other specified fields are equal to the value following the specified
+field. For strings ensures value is not "". For slices, maps, pointers,
+interfaces, channels and functions ensures the value is not nil.
+
+	Usage: required_unless
+
+Examples:
+
+	// require the field unless the Field1 is equal to the parameter given:
+	Usage: required_unless=Field1 foobar
+
+	// require the field unless the Field1 and Field2 is equal to the value respectively:
+	Usage: required_unless=Field1 foo Field2 bar
+
 Required With
 
 The field under validation must be present and not empty only if any
@@ -321,8 +355,17 @@ equal to the parameter given. For strings, it checks that
 the string length is exactly that number of characters. For slices,
 arrays, and maps, validates the number of items.
 
+Example #1
+
 	Usage: len=10
 
+Example #2 (time.Duration)
+
+For time.Duration, len will ensure that the value is equal to the duration given
+in the parameter.
+
+	Usage: len=1h30m
+
 Maximum
 
 For numbers, max will ensure that the value is
@@ -330,8 +373,17 @@ less than or equal to the parameter given. For strings, it checks
 that the string length is at most that number of characters. For
 slices, arrays, and maps, validates the number of items.
 
+Example #1
+
 	Usage: max=10
 
+Example #2 (time.Duration)
+
+For time.Duration, max will ensure that the value is less than or equal to the
+duration given in the parameter.
+
+	Usage: max=1h30m
+
 Minimum
 
 For numbers, min will ensure that the value is
@@ -339,24 +391,51 @@ greater or equal to the parameter given. For strings, it checks that
 the string length is at least that number of characters. For slices,
 arrays, and maps, validates the number of items.
 
+Example #1
+
 	Usage: min=10
 
+Example #2 (time.Duration)
+
+For time.Duration, min will ensure that the value is greater than or equal to
+the duration given in the parameter.
+
+	Usage: min=1h30m
+
 Equals
 
 For strings & numbers, eq will ensure that the value is
 equal to the parameter given. For slices, arrays, and maps,
 validates the number of items.
 
+Example #1
+
 	Usage: eq=10
 
+Example #2 (time.Duration)
+
+For time.Duration, eq will ensure that the value is equal to the duration given
+in the parameter.
+
+	Usage: eq=1h30m
+
 Not Equal
 
 For strings & numbers, ne will ensure that the value is not
 equal to the parameter given. For slices, arrays, and maps,
 validates the number of items.
 
+Example #1
+
 	Usage: ne=10
 
+Example #2 (time.Duration)
+
+For time.Duration, ne will ensure that the value is not equal to the duration
+given in the parameter.
+
+	Usage: ne=1h30m
+
 One Of
 
 For strings, ints, and uints, oneof will ensure that the value
@@ -386,11 +465,17 @@ For time.Time ensures the time value is greater than time.Now.UTC().
 
 	Usage: gt
 
+Example #3 (time.Duration)
+
+For time.Duration, gt will ensure that the value is greater than the duration
+given in the parameter.
+
+	Usage: gt=1h30m
+
 Greater Than or Equal
 
 Same as 'min' above. Kept both to make terminology with 'len' easier.
 
-
 Example #1
 
 	Usage: gte=10
@@ -401,6 +486,13 @@ For time.Time ensures the time value is greater than or equal to time.Now.UTC().
 
 	Usage: gte
 
+Example #3 (time.Duration)
+
+For time.Duration, gte will ensure that the value is greater than or equal to
+the duration given in the parameter.
+
+	Usage: gte=1h30m
+
 Less Than
 
 For numbers, this will ensure that the value is less than the parameter given.
@@ -412,10 +504,18 @@ Example #1
 	Usage: lt=10
 
 Example #2 (time.Time)
+
 For time.Time ensures the time value is less than time.Now.UTC().
 
 	Usage: lt
 
+Example #3 (time.Duration)
+
+For time.Duration, lt will ensure that the value is less than the duration given
+in the parameter.
+
+	Usage: lt=1h30m
+
 Less Than or Equal
 
 Same as 'max' above. Kept both to make terminology with 'len' easier.
@@ -430,6 +530,13 @@ For time.Time ensures the time value is less than or equal to time.Now.UTC().
 
 	Usage: lte
 
+Example #3 (time.Duration)
+
+For time.Duration, lte will ensure that the value is less than or equal to the
+duration given in the parameter.
+
+	Usage: lte=1h30m
+
 Field Equals Another Field
 
 This will validate the field value against another fields value either within
@@ -476,9 +583,9 @@ relative to the top level struct.
 
 Field Greater Than Another Field
 
-Only valid for Numbers and time.Time types, this will validate the field value
-against another fields value either within a struct or passed in field.
-usage examples are for validation of a Start and End date:
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
 
 Example #1:
 
@@ -490,7 +597,6 @@ Example #2:
 	// Validating by field:
 	validate.VarWithValue(start, end, "gtfield")
 
-
 Field Greater Than Another Relative Field
 
 This does the same as gtfield except that it validates the field provided
@@ -500,9 +606,9 @@ relative to the top level struct.
 
 Field Greater Than or Equal To Another Field
 
-Only valid for Numbers and time.Time types, this will validate the field value
-against another fields value either within a struct or passed in field.
-usage examples are for validation of a Start and End date:
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
 
 Example #1:
 
@@ -523,9 +629,9 @@ to the top level struct.
 
 Less Than Another Field
 
-Only valid for Numbers and time.Time types, this will validate the field value
-against another fields value either within a struct or passed in field.
-usage examples are for validation of a Start and End date:
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
 
 Example #1:
 
@@ -546,9 +652,9 @@ to the top level struct.
 
 Less Than or Equal To Another Field
 
-Only valid for Numbers and time.Time types, this will validate the field value
-against another fields value either within a struct or passed in field.
-usage examples are for validation of a Start and End date:
+Only valid for Numbers, time.Duration and time.Time types, this will validate
+the field value against another fields value either within a struct or passed in
+field. usage examples are for validation of a Start and End date:
 
 Example #1:
 
@@ -684,6 +790,13 @@ This validates that a string value contains a valid hsla color
 
 	Usage: hsla
 
+E.164 Phone Number String
+
+This validates that a string value contains a valid E.164 Phone number
+https://en.wikipedia.org/wiki/E.164 (ex. +1123456789)
+
+	Usage: e164
+
 E-mail String
 
 This validates that a string value contains a valid email
@@ -766,8 +879,7 @@ Special thanks to Pieter Wuille for providng reference implementations.
 Ethereum Address
 
 This validates that a string value contains a valid ethereum address.
-The format of the string is checked to ensure it matches the standard Ethereum address format
-Full validation is blocked by https://github.com/golang/crypto/pull/28
+The format of the string is checked to ensure it matches the standard Ethereum address format.
 
 	Usage: eth_addr
 
@@ -1088,6 +1200,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/errors.go b/errors.go
index 86420b9..63293cf 100644
--- a/errors.go
+++ b/errors.go
@@ -155,6 +155,9 @@ type FieldError interface {
 	// NOTE: if no registered translator can be found it returns the same as
 	// calling fe.Error()
 	Translate(ut ut.Translator) string
+
+	// Error returns the FieldError's message
+	Error() string
 }
 
 // compile time interface checks
@@ -254,8 +257,8 @@ func (fe *fieldError) Error() string {
 // Translate returns the FieldError's translated error
 // from the provided 'ut.Translator' and registered 'TranslationFunc'
 //
-// NOTE: is not registered translation can be found it returns the same
-// as calling fe.Error()
+// NOTE: if no registered translation can be found, it returns the original
+// untranslated error message.
 func (fe *fieldError) Translate(ut ut.Translator) string {
 
 	m, ok := fe.v.transTagFunc[ut]
diff --git a/go.mod b/go.mod
index 21316b3..d457100 100644
--- a/go.mod
+++ b/go.mod
@@ -7,4 +7,5 @@ require (
 	github.com/go-playground/locales v0.13.0
 	github.com/go-playground/universal-translator v0.17.0
 	github.com/leodido/go-urn v1.2.0
+	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
 )
diff --git a/go.sum b/go.sum
index dc7dec4..0152642 100644
--- a/go.sum
+++ b/go.sum
@@ -13,6 +13,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
diff --git a/util.go b/util.go
index 0d3a181..6e568f8 100644
--- a/util.go
+++ b/util.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"strconv"
 	"strings"
+	"time"
 )
 
 // extractTypeInternal gets the actual underlying type of field value.
@@ -229,6 +230,26 @@ func asInt(param string) int64 {
 	return i
 }
 
+// asIntFromTimeDuration parses param as time.Duration and returns it as int64
+// or panics on error.
+func asIntFromTimeDuration(param string) int64 {
+	d, err := time.ParseDuration(param)
+	panicIf(err)
+
+	return int64(d)
+}
+
+// asIntFromType calls the proper function to parse param as int64,
+// given a field's Type t.
+func asIntFromType(t reflect.Type, param string) int64 {
+	switch t {
+	case timeDurationType:
+		return asIntFromTimeDuration(param)
+	default:
+		return asInt(param)
+	}
+}
+
 // asUint returns the parameter as a uint64
 // or panics if it can't convert
 func asUint(param string) uint64 {
diff --git a/validator.go b/validator.go
index 342e72e..f097f39 100644
--- a/validator.go
+++ b/validator.go
@@ -249,7 +249,7 @@ OUTER:
 			v.cf = cf
 			v.ct = ct
 
-			if !v.fldIsPointer && !hasValue(v) {
+			if !hasValue(v) {
 				return
 			}
 
diff --git a/validator_instance.go b/validator_instance.go
index 4a89d40..fe6a487 100644
--- a/validator_instance.go
+++ b/validator_instance.go
@@ -27,6 +27,8 @@ const (
 	requiredWithoutTag    = "required_without"
 	requiredWithTag       = "required_with"
 	requiredWithAllTag    = "required_with_all"
+	requiredIfTag         = "required_if"
+	requiredUnlessTag     = "required_unless"
 	skipValidationTag     = "-"
 	diveTag               = "dive"
 	keysTag               = "keys"
@@ -41,7 +43,9 @@ const (
 )
 
 var (
-	timeType      = reflect.TypeOf(time.Time{})
+	timeDurationType = reflect.TypeOf(time.Duration(0))
+	timeType         = reflect.TypeOf(time.Time{})
+
 	defaultCField = &cField{namesEqual: true}
 )
 
@@ -107,7 +111,7 @@ func New() *Validate {
 
 		switch k {
 		// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
-		case requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag:
+		case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag:
 			_ = v.registerValidation(k, wrapFunc(val), true, true)
 		default:
 			// no need to error check here, baked in will always be valid


Test patch:
diff --git a/validator_test.go b/validator_test.go
index e76a3cd..f9202cd 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -346,6 +346,18 @@ func StructLevelInvalidError(sl StructLevel) {
 	}
 }
 
+func stringPtr(v string) *string {
+	return &v
+}
+
+func intPtr(v int) *int {
+	return &v
+}
+
+func float64Ptr(v float64) *float64 {
+	return &v
+}
+
 func TestStructLevelInvalidError(t *testing.T) {
 
 	validate := New()
@@ -940,6 +952,8 @@ func TestStructPartial(t *testing.T) {
 }
 
 func TestCrossStructLteFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -982,8 +996,7 @@ func TestCrossStructLteFieldValidation(t *testing.T) {
 		Array:     []string{"val1"},
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	test.CreatedAt = &then
@@ -1046,9 +1059,68 @@ func TestCrossStructLteFieldValidation(t *testing.T) {
 	errs = validate.Struct(tst)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltecsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltecsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "ltecsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltecsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltecsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltecsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"ltecsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltecsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,ltecsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{-time.Minute}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossStructLtFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -1091,8 +1163,7 @@ func TestCrossStructLtFieldValidation(t *testing.T) {
 		Array:     []string{"val1"},
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	test.CreatedAt = &then
@@ -1142,9 +1213,70 @@ func TestCrossStructLtFieldValidation(t *testing.T) {
 	errs = validate.Struct(tst)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "ltcsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltcsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "ltcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltcsfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltcsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltcsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"ltcsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltcsfield")
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ltcsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,ltcsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{-time.Minute}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossStructGteFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -1187,8 +1319,7 @@ func TestCrossStructGteFieldValidation(t *testing.T) {
 		Array:     []string{"val1", "val2", "val3"},
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	test.CreatedAt = &then
@@ -1250,9 +1381,68 @@ func TestCrossStructGteFieldValidation(t *testing.T) {
 	errs = validate.Struct(tst)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtecsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtecsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "gtecsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtecsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtecsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtecsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"gtecsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtecsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,gtecsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossStructGtFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -1295,8 +1485,7 @@ func TestCrossStructGtFieldValidation(t *testing.T) {
 		Array:     []string{"val1", "val2", "val3"},
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	test.CreatedAt = &then
@@ -1346,9 +1535,70 @@ func TestCrossStructGtFieldValidation(t *testing.T) {
 	errs = validate.Struct(tst)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test2.Time", "Test2.Time", "Time", "Time", "gtcsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtcsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "gtcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtcsfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtcsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtcsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"gtcsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtcsfield")
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gtcsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,gtcsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossStructNeFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -1371,8 +1621,7 @@ func TestCrossStructNeFieldValidation(t *testing.T) {
 		CreatedAt: &now,
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	test.CreatedAt = &then
@@ -1454,9 +1703,68 @@ func TestCrossStructNeFieldValidation(t *testing.T) {
 	errs = validate.VarWithValue(nil, 1, "necsfield")
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "", "", "", "", "necsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "necsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "necsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "necsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "necsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Duration(0), "omitempty,necsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"necsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "necsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,necsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{time.Duration(0)}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossStructEqFieldValidation(t *testing.T) {
+	var errs error
+	validate := New()
 
 	type Inner struct {
 		CreatedAt *time.Time
@@ -1478,8 +1786,7 @@ func TestCrossStructEqFieldValidation(t *testing.T) {
 		CreatedAt: &now,
 	}
 
-	validate := New()
-	errs := validate.Struct(test)
+	errs = validate.Struct(test)
 	Equal(t, errs, nil)
 
 	newTime := time.Now().UTC()
@@ -1559,6 +1866,65 @@ func TestCrossStructEqFieldValidation(t *testing.T) {
 	errs = validate.VarWithValue(nil, 1, "eqcsfield")
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "eqcsfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "eqcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "eqcsfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eqcsfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,eqcsfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct and an inner struct with time.Duration type fields.
+
+	type TimeDurationInner struct {
+		Duration time.Duration
+	}
+	var timeDurationInner *TimeDurationInner
+
+	type TimeDurationTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"eqcsfield=Inner.Duration"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationInner = &TimeDurationInner{time.Hour - time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eqcsfield")
+
+	timeDurationInner = &TimeDurationInner{time.Hour + time.Minute}
+	timeDurationTest = &TimeDurationTest{timeDurationInner, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eqcsfield")
+
+	type TimeDurationOmitemptyTest struct {
+		Inner    *TimeDurationInner
+		Duration time.Duration `validate:"omitempty,eqcsfield=Inner.Duration"`
+	}
+	var timeDurationOmitemptyTest *TimeDurationOmitemptyTest
+
+	timeDurationInner = &TimeDurationInner{time.Hour}
+	timeDurationOmitemptyTest = &TimeDurationOmitemptyTest{timeDurationInner, time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestCrossNamespaceFieldValidation(t *testing.T) {
@@ -4182,7 +4548,7 @@ func TestContainsValidation(t *testing.T) {
 }
 
 func TestIsNeFieldValidation(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	var j uint64
@@ -4204,7 +4570,7 @@ func TestIsNeFieldValidation(t *testing.T) {
 	arr3 := []string{"test"}
 	now2 := now
 
-	errs := validate.VarWithValue(s, s2, "nefield")
+	errs = validate.VarWithValue(s, s2, "nefield")
 	Equal(t, errs, nil)
 
 	errs = validate.VarWithValue(i2, i, "nefield")
@@ -4287,10 +4653,57 @@ func TestIsNeFieldValidation(t *testing.T) {
 
 	errs = validate.Struct(tst)
 	Equal(t, errs, nil)
+
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "nefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "nefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "nefield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "nefield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Duration(0), "omitempty,nefield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"nefield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "nefield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,nefield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestIsNeValidation(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	var j uint64
@@ -4302,7 +4715,7 @@ func TestIsNeValidation(t *testing.T) {
 	arr := []string{"test"}
 	now := time.Now().UTC()
 
-	errs := validate.Var(s, "ne=abcd")
+	errs = validate.Var(s, "ne=abcd")
 	Equal(t, errs, nil)
 
 	errs = validate.Var(i, "ne=1")
@@ -4322,10 +4735,55 @@ func TestIsNeValidation(t *testing.T) {
 	AssertError(t, errs, "", "", "", "", "ne")
 
 	PanicMatches(t, func() { _ = validate.Var(now, "ne=now") }, "Bad field type time.Time")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour-time.Minute, "ne=1h")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+time.Minute, "ne=1h")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour, "ne=1h")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ne")
+
+	errs = validate.Var(time.Duration(0), "omitempty,ne=0")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"ne=1h"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "ne")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,ne=0"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestIsEqFieldValidation(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	var j uint64
@@ -4347,7 +4805,7 @@ func TestIsEqFieldValidation(t *testing.T) {
 	arr3 := []string{"test", "test2"}
 	now2 := now
 
-	errs := validate.VarWithValue(s, s2, "eqfield")
+	errs = validate.VarWithValue(s, s2, "eqfield")
 	Equal(t, errs, nil)
 
 	errs = validate.VarWithValue(i2, i, "eqfield")
@@ -4441,26 +4899,75 @@ func TestIsEqFieldValidation(t *testing.T) {
 	errs = validate.Struct(test)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "TStruct.CreatedAt", "TStruct.CreatedAt", "CreatedAt", "CreatedAt", "eqfield")
-}
-
-func TestIsEqValidation(t *testing.T) {
 
-	validate := New()
+	// Tests for time.Duration type.
 
-	var j uint64
-	var k float64
-	s := "abcd"
-	i := 1
-	j = 1
-	k = 1.543
-	arr := []string{"test"}
-	now := time.Now().UTC()
+	// -- Validations for variables of time.Duration type.
 
-	errs := validate.Var(s, "eq=abcd")
+	errs = validate.VarWithValue(time.Hour, time.Hour, "eqfield")
 	Equal(t, errs, nil)
 
-	errs = validate.Var(i, "eq=1")
-	Equal(t, errs, nil)
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "eqfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eqfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "eqfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eqfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,eqfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"eqfield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "eqfield")
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "eqfield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,eqfield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestIsEqValidation(t *testing.T) {
+	var errs error
+	validate := New()
+
+	var j uint64
+	var k float64
+	s := "abcd"
+	i := 1
+	j = 1
+	k = 1.543
+	arr := []string{"test"}
+	now := time.Now().UTC()
+
+	errs = validate.Var(s, "eq=abcd")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(i, "eq=1")
+	Equal(t, errs, nil)
 
 	errs = validate.Var(j, "eq=1")
 	Equal(t, errs, nil)
@@ -4476,6 +4983,53 @@ func TestIsEqValidation(t *testing.T) {
 	AssertError(t, errs, "", "", "", "", "eq")
 
 	PanicMatches(t, func() { _ = validate.Var(now, "eq=now") }, "Bad field type time.Time")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "eq=1h")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-time.Minute, "eq=1h")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eq")
+
+	errs = validate.Var(time.Hour+time.Minute, "eq=1h")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "eq")
+
+	errs = validate.Var(time.Duration(0), "omitempty,eq=1h")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"eq=1h"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eq")
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "eq")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,eq=1h"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestOneOfValidation(t *testing.T) {
@@ -4653,11 +5207,29 @@ func TestEthereumAddressValidation(t *testing.T) {
 		param    string
 		expected bool
 	}{
-		{"", false},
-		{"0x02F9AE5f22EA3fA88F05780B30385bEC", false},
-		{"123f681646d4a755815f9cb19e1acc8565a0c2ac", false},
-		{"0x02F9AE5f22EA3fA88F05780B30385bECFacbf130", true},
+		// All caps.
+		{"0x52908400098527886E0F7030069857D2E4169EE7", true},
+		{"0x8617E340B3D01FA5F11F306F4090FD50E238070D", true},
+
+		// All lower.
+		{"0xde709f2102306220921060314715629080e2fb77", true},
+		{"0x27b1fdb04752bbc536007a920d24acb045561c26", true},
 		{"0x123f681646d4a755815f9cb19e1acc8565a0c2ac", true},
+
+		// Mixed case: runs checksum validation.
+		{"0x02F9AE5f22EA3fA88F05780B30385bECFacbf130", true},
+		{"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", true},
+		{"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", true},
+		{"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", true},
+		{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", true},
+		{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDB", false}, // Invalid checksum.
+
+		// Other.
+		{"", false},
+		{"D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", false},    // Missing "0x" prefix.
+		{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDbc", false}, // More than 40 hex digits.
+		{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aD", false},   // Less than 40 hex digits.
+		{"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDw", false},  // Invalid hex digit "w".
 	}
 
 	for i, test := range tests {
@@ -4952,7 +5524,7 @@ func TestStructOnlyValidation(t *testing.T) {
 }
 
 func TestGtField(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	type TimeTest struct {
@@ -4969,7 +5541,7 @@ func TestGtField(t *testing.T) {
 		End:   &end,
 	}
 
-	errs := validate.Struct(timeTest)
+	errs = validate.Struct(timeTest)
 	Equal(t, errs, nil)
 
 	timeTest = &TimeTest{
@@ -4997,6 +5569,57 @@ func TestGtField(t *testing.T) {
 	errs = validate.VarWithValue("test bigger", "test", "gtfield")
 	Equal(t, errs, nil)
 
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "gtfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtfield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"gtfield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtfield")
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtfield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,gtfield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+
+	// Tests for Ints types.
+
 	type IntTest struct {
 		Val1 int `validate:"required"`
 		Val2 int `validate:"required,gtfield=Val1"`
@@ -5130,7 +5753,7 @@ func TestGtField(t *testing.T) {
 }
 
 func TestLtField(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	type TimeTest struct {
@@ -5147,7 +5770,7 @@ func TestLtField(t *testing.T) {
 		End:   &end,
 	}
 
-	errs := validate.Struct(timeTest)
+	errs = validate.Struct(timeTest)
 	Equal(t, errs, nil)
 
 	timeTest = &TimeTest{
@@ -5173,6 +5796,57 @@ func TestLtField(t *testing.T) {
 	errs = validate.VarWithValue("tes", "test", "ltfield")
 	Equal(t, errs, nil)
 
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltfield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "ltfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltfield")
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltfield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltfield")
+
+	errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltfield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"ltfield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltfield")
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltfield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,ltfield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), -time.Minute}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+
+	// Tests for Ints types.
+
 	type IntTest struct {
 		Val1 int `validate:"required"`
 		Val2 int `validate:"required,ltfield=Val1"`
@@ -5411,7 +6085,7 @@ func TestContainsAndExcludes(t *testing.T) {
 }
 
 func TestLteField(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	type TimeTest struct {
@@ -5428,7 +6102,7 @@ func TestLteField(t *testing.T) {
 		End:   &end,
 	}
 
-	errs := validate.Struct(timeTest)
+	errs = validate.Struct(timeTest)
 	Equal(t, errs, nil)
 
 	timeTest = &TimeTest{
@@ -5457,6 +6131,55 @@ func TestLteField(t *testing.T) {
 	errs = validate.VarWithValue("test", "test", "ltefield")
 	Equal(t, errs, nil)
 
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "ltefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "ltefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "ltefield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "ltefield")
+
+	errs = validate.VarWithValue(time.Duration(0), -time.Minute, "omitempty,ltefield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"ltefield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "ltefield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,ltefield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), -time.Minute}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+
+	// Tests for Ints types.
+
 	type IntTest struct {
 		Val1 int `validate:"required"`
 		Val2 int `validate:"required,ltefield=Val1"`
@@ -5572,7 +6295,7 @@ func TestLteField(t *testing.T) {
 }
 
 func TestGteField(t *testing.T) {
-
+	var errs error
 	validate := New()
 
 	type TimeTest struct {
@@ -5589,7 +6312,7 @@ func TestGteField(t *testing.T) {
 		End:   &end,
 	}
 
-	errs := validate.Struct(timeTest)
+	errs = validate.Struct(timeTest)
 	Equal(t, errs, nil)
 
 	timeTest = &TimeTest{
@@ -5618,6 +6341,55 @@ func TestGteField(t *testing.T) {
 	errs = validate.VarWithValue("test bigger", "test", "gtefield")
 	Equal(t, errs, nil)
 
+	// Tests for time.Duration type.
+
+	// -- Validations for variables of time.Duration type.
+
+	errs = validate.VarWithValue(time.Hour, time.Hour-time.Minute, "gtefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour, "gtefield")
+	Equal(t, errs, nil)
+
+	errs = validate.VarWithValue(time.Hour, time.Hour+time.Minute, "gtefield")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gtefield")
+
+	errs = validate.VarWithValue(time.Duration(0), time.Hour, "omitempty,gtefield")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with time.Duration type fields.
+
+	type TimeDurationTest struct {
+		First  time.Duration `validate:"gtefield=Second"`
+		Second time.Duration
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour, time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.First", "TimeDurationTest.First", "First", "First", "gtefield")
+
+	type TimeDurationOmitemptyTest struct {
+		First  time.Duration `validate:"omitempty,gtefield=Second"`
+		Second time.Duration
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0), time.Hour}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+
+	// Tests for Ints types.
+
 	type IntTest struct {
 		Val1 int `validate:"required"`
 		Val2 int `validate:"required,gtefield=Val1"`
@@ -5853,9 +6625,11 @@ func TestLength(t *testing.T) {
 }
 
 func TestIsGt(t *testing.T) {
+	var errs error
 	validate := New()
+
 	myMap := map[string]string{}
-	errs := validate.Var(myMap, "gt=0")
+	errs = validate.Var(myMap, "gt=0")
 	NotEqual(t, errs, nil)
 
 	f := 1.23
@@ -5900,17 +6674,66 @@ func TestIsGt(t *testing.T) {
 	errs = validate.Struct(s)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "gt")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "gt=59m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-time.Minute, "gt=59m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gt")
+
+	errs = validate.Var(time.Hour-2*time.Minute, "gt=59m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gt")
+
+	errs = validate.Var(time.Duration(0), "omitempty,gt=59m")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"gt=59m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gt")
+
+	timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gt")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,gt=59m"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestIsGte(t *testing.T) {
+	var errs error
 	validate := New()
+
 	i := true
 	PanicMatches(t, func() { _ = validate.Var(i, "gte") }, "Bad field type bool")
 
 	t1 := time.Now().UTC()
 	t1 = t1.Add(time.Hour * 24)
 
-	errs := validate.Var(t1, "gte")
+	errs = validate.Var(t1, "gte")
 	Equal(t, errs, nil)
 
 	t2 := time.Now().UTC().Add(-time.Hour)
@@ -5936,74 +6759,386 @@ func TestIsGte(t *testing.T) {
 	errs = validate.Struct(s)
 	NotEqual(t, errs, nil)
 	AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "gte")
-}
 
-func TestIsLt(t *testing.T) {
-	validate := New()
-	myMap := map[string]string{}
-	errs := validate.Var(myMap, "lt=0")
-	NotEqual(t, errs, nil)
-	AssertError(t, errs, "", "", "", "", "lt")
+	// Tests for time.Duration type.
 
-	f := 1.23
-	errs = validate.Var(f, "lt=0")
-	NotEqual(t, errs, nil)
-	AssertError(t, errs, "", "", "", "", "lt")
+	// -- Validations for a variable of time.Duration type.
 
-	var ui uint = 5
-	errs = validate.Var(ui, "lt=0")
-	NotEqual(t, errs, nil)
-	AssertError(t, errs, "", "", "", "", "lt")
+	errs = validate.Var(time.Hour, "gte=59m")
+	Equal(t, errs, nil)
 
-	i := true
-	PanicMatches(t, func() { _ = validate.Var(i, "lt") }, "Bad field type bool")
+	errs = validate.Var(time.Hour-time.Minute, "gte=59m")
+	Equal(t, errs, nil)
 
-	t1 := time.Now().UTC().Add(-time.Hour)
+	errs = validate.Var(time.Hour-2*time.Minute, "gte=59m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "gte")
 
-	errs = validate.Var(t1, "lt")
+	errs = validate.Var(time.Duration(0), "omitempty,gte=59m")
 	Equal(t, errs, nil)
 
-	t2 := time.Now().UTC()
-	t2 = t2.Add(time.Hour * 24)
-
-	errs = validate.Var(t2, "lt")
-	NotEqual(t, errs, nil)
-	AssertError(t, errs, "", "", "", "", "lt")
+	// -- Validations for a struct with a time.Duration type field.
 
-	type Test struct {
-		Now *time.Time `validate:"lt"`
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"gte=59m"`
 	}
+	var timeDurationTest *TimeDurationTest
 
-	s := &Test{
-		Now: &t1,
-	}
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
 
-	errs = validate.Struct(s)
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
 	Equal(t, errs, nil)
 
-	s = &Test{
-		Now: &t2,
+	timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "gte")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,gte=59m"`
 	}
 
-	errs = validate.Struct(s)
-	NotEqual(t, errs, nil)
-	AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "lt")
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
-func TestIsLte(t *testing.T) {
-
+func TestMinValidation(t *testing.T) {
+	var errs error
 	validate := New()
 
-	i := true
-	PanicMatches(t, func() { _ = validate.Var(i, "lte") }, "Bad field type bool")
+	// Tests for time.Duration type.
 
-	t1 := time.Now().UTC().Add(-time.Hour)
+	// -- Validations for a variable of time.Duration type.
 
-	errs := validate.Var(t1, "lte")
+	errs = validate.Var(time.Hour, "min=59m")
 	Equal(t, errs, nil)
 
-	t2 := time.Now().UTC()
-	t2 = t2.Add(time.Hour * 24)
+	errs = validate.Var(time.Hour-time.Minute, "min=59m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-2*time.Minute, "min=59m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "min")
+
+	errs = validate.Var(time.Duration(0), "omitempty,min=59m")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"min=59m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "min")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,min=59m"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestMaxValidation(t *testing.T) {
+	var errs error
+	validate := New()
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "max=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+time.Minute, "max=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+2*time.Minute, "max=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "max")
+
+	errs = validate.Var(time.Duration(0), "omitempty,max=-1s")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"max=1h1m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "max")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,max=-1s"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestMinMaxValidation(t *testing.T) {
+	var errs error
+	validate := New()
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "min=59m,max=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-time.Minute, "min=59m,max=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+time.Minute, "min=59m,max=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-2*time.Minute, "min=59m,max=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "min")
+
+	errs = validate.Var(time.Hour+2*time.Minute, "min=59m,max=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "max")
+
+	errs = validate.Var(time.Duration(0), "omitempty,min=59m,max=1h1m")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"min=59m,max=1h1m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "min")
+
+	timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "max")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,min=59m,max=1h1m"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestLenValidation(t *testing.T) {
+	var errs error
+	validate := New()
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "len=1h")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour-time.Minute, "len=1h")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "len")
+
+	errs = validate.Var(time.Hour+time.Minute, "len=1h")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "len")
+
+	errs = validate.Var(time.Duration(0), "omitempty,len=1h")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"len=1h"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour - time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "len")
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "len")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,len=1h"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestIsLt(t *testing.T) {
+	var errs error
+	validate := New()
+
+	myMap := map[string]string{}
+	errs = validate.Var(myMap, "lt=0")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	f := 1.23
+	errs = validate.Var(f, "lt=0")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	var ui uint = 5
+	errs = validate.Var(ui, "lt=0")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	i := true
+	PanicMatches(t, func() { _ = validate.Var(i, "lt") }, "Bad field type bool")
+
+	t1 := time.Now().UTC().Add(-time.Hour)
+
+	errs = validate.Var(t1, "lt")
+	Equal(t, errs, nil)
+
+	t2 := time.Now().UTC()
+	t2 = t2.Add(time.Hour * 24)
+
+	errs = validate.Var(t2, "lt")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	type Test struct {
+		Now *time.Time `validate:"lt"`
+	}
+
+	s := &Test{
+		Now: &t1,
+	}
+
+	errs = validate.Struct(s)
+	Equal(t, errs, nil)
+
+	s = &Test{
+		Now: &t2,
+	}
+
+	errs = validate.Struct(s)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "Test.Now", "Test.Now", "Now", "Now", "lt")
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "lt=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+time.Minute, "lt=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	errs = validate.Var(time.Hour+2*time.Minute, "lt=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lt")
+
+	errs = validate.Var(time.Duration(0), "omitempty,lt=0")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"lt=1h1m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lt")
+
+	timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lt")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,lt=0"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
+}
+
+func TestIsLte(t *testing.T) {
+	var errs error
+	validate := New()
+
+	i := true
+	PanicMatches(t, func() { _ = validate.Var(i, "lte") }, "Bad field type bool")
+
+	t1 := time.Now().UTC().Add(-time.Hour)
+
+	errs = validate.Var(t1, "lte")
+	Equal(t, errs, nil)
+
+	t2 := time.Now().UTC()
+	t2 = t2.Add(time.Hour * 24)
 
 	errs = validate.Var(t2, "lte")
 	NotEqual(t, errs, nil)
@@ -6026,6 +7161,51 @@ func TestIsLte(t *testing.T) {
 
 	errs = validate.Struct(s)
 	NotEqual(t, errs, nil)
+
+	// Tests for time.Duration type.
+
+	// -- Validations for a variable of time.Duration type.
+
+	errs = validate.Var(time.Hour, "lte=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+time.Minute, "lte=1h1m")
+	Equal(t, errs, nil)
+
+	errs = validate.Var(time.Hour+2*time.Minute, "lte=1h1m")
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "", "", "", "", "lte")
+
+	errs = validate.Var(time.Duration(0), "omitempty,lte=-1s")
+	Equal(t, errs, nil)
+
+	// -- Validations for a struct with a time.Duration type field.
+
+	type TimeDurationTest struct {
+		Duration time.Duration `validate:"lte=1h1m"`
+	}
+	var timeDurationTest *TimeDurationTest
+
+	timeDurationTest = &TimeDurationTest{time.Hour}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	Equal(t, errs, nil)
+
+	timeDurationTest = &TimeDurationTest{time.Hour + 2*time.Minute}
+	errs = validate.Struct(timeDurationTest)
+	NotEqual(t, errs, nil)
+	AssertError(t, errs, "TimeDurationTest.Duration", "TimeDurationTest.Duration", "Duration", "Duration", "lte")
+
+	type TimeDurationOmitemptyTest struct {
+		Duration time.Duration `validate:"omitempty,lte=-1s"`
+	}
+
+	timeDurationOmitemptyTest := &TimeDurationOmitemptyTest{time.Duration(0)}
+	errs = validate.Struct(timeDurationOmitemptyTest)
+	Equal(t, errs, nil)
 }
 
 func TestUrnRFC2141(t *testing.T) {
@@ -8144,6 +9324,12 @@ func TestUniqueValidation(t *testing.T) {
 		{[2]string{"a", "a"}, false},
 		{[2]interface{}{"a", "a"}, false},
 		{[4]interface{}{"a", 1, "b", 1}, false},
+		{[2]*string{stringPtr("a"), stringPtr("b")}, true},
+		{[2]*int{intPtr(1), intPtr(2)}, true},
+		{[2]*float64{float64Ptr(1), float64Ptr(2)}, true},
+		{[2]*string{stringPtr("a"), stringPtr("a")}, false},
+		{[2]*float64{float64Ptr(1), float64Ptr(1)}, false},
+		{[2]*int{intPtr(1), intPtr(1)}, false},
 		// Slices
 		{[]string{"a", "b"}, true},
 		{[]int{1, 2}, true},
@@ -8155,6 +9341,12 @@ func TestUniqueValidation(t *testing.T) {
 		{[]string{"a", "a"}, false},
 		{[]interface{}{"a", "a"}, false},
 		{[]interface{}{"a", 1, "b", 1}, false},
+		{[]*string{stringPtr("a"), stringPtr("b")}, true},
+		{[]*int{intPtr(1), intPtr(2)}, true},
+		{[]*float64{float64Ptr(1), float64Ptr(2)}, true},
+		{[]*string{stringPtr("a"), stringPtr("a")}, false},
+		{[]*float64{float64Ptr(1), float64Ptr(1)}, false},
+		{[]*int{intPtr(1), intPtr(1)}, false},
 		// Maps
 		{map[string]string{"one": "a", "two": "b"}, true},
 		{map[string]int{"one": 1, "two": 2}, true},
@@ -8232,57 +9424,100 @@ func TestUniqueValidationStructSlice(t *testing.T) {
 			}
 		}
 	}
-	PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
+	PanicMatches(t, func() { _ = validate.Var(testStructs, "unique=C") }, "Bad field name C")
 }
 
-func TestHTMLValidation(t *testing.T) {
+func TestUniqueValidationStructPtrSlice(t *testing.T) {
+	testStructs := []*struct {
+		A *string
+		B *string
+	}{
+		{A: stringPtr("one"), B: stringPtr("two")},
+		{A: stringPtr("one"), B: stringPtr("three")},
+	}
+
 	tests := []struct {
+		target   interface{}
 		param    string
 		expected bool
 	}{
-		{"<html>", true},
-		{"<script>", true},
-		{"<stillworks>", true},
-		{"</html", false},
-		{"</script>", true},
-		{"<//script>", false},
-		{"<123nonsense>", false},
-		{"test", false},
-		{"&example", false},
+		{testStructs, "unique", true},
+		{testStructs, "unique=A", false},
+		{testStructs, "unique=B", true},
 	}
 
 	validate := New()
 
 	for i, test := range tests {
 
-		errs := validate.Var(test.param, "html")
+		errs := validate.Var(test.target, test.param)
 
 		if test.expected {
 			if !IsEqual(errs, nil) {
-				t.Fatalf("Index: %d html failed Error: %v", i, errs)
+				t.Fatalf("Index: %d unique failed Error: %v", i, errs)
 			}
 		} else {
 			if IsEqual(errs, nil) {
-				t.Fatalf("Index: %d html failed Error: %v", i, errs)
+				t.Fatalf("Index: %d unique failed Error: %v", i, errs)
 			} else {
 				val := getError(errs, "", "")
-				if val.Tag() != "html" {
-					t.Fatalf("Index: %d html failed Error: %v", i, errs)
+				if val.Tag() != "unique" {
+					t.Fatalf("Index: %d unique failed Error: %v", i, errs)
 				}
 			}
 		}
 	}
+	PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
 }
 
-func TestHTMLEncodedValidation(t *testing.T) {
+func TestHTMLValidation(t *testing.T) {
 	tests := []struct {
 		param    string
 		expected bool
 	}{
-		{"&#x3c;", true},
-		{"&#xaf;", true},
-		{"&#x00;", true},
-		{"&#xf0;", true},
+		{"<html>", true},
+		{"<script>", true},
+		{"<stillworks>", true},
+		{"</html", false},
+		{"</script>", true},
+		{"<//script>", false},
+		{"<123nonsense>", false},
+		{"test", false},
+		{"&example", false},
+	}
+
+	validate := New()
+
+	for i, test := range tests {
+
+		errs := validate.Var(test.param, "html")
+
+		if test.expected {
+			if !IsEqual(errs, nil) {
+				t.Fatalf("Index: %d html failed Error: %v", i, errs)
+			}
+		} else {
+			if IsEqual(errs, nil) {
+				t.Fatalf("Index: %d html failed Error: %v", i, errs)
+			} else {
+				val := getError(errs, "", "")
+				if val.Tag() != "html" {
+					t.Fatalf("Index: %d html failed Error: %v", i, errs)
+				}
+			}
+		}
+	}
+}
+
+func TestHTMLEncodedValidation(t *testing.T) {
+	tests := []struct {
+		param    string
+		expected bool
+	}{
+		{"&#x3c;", true},
+		{"&#xaf;", true},
+		{"&#x00;", true},
+		{"&#xf0;", true},
 		{"&#x3c", true},
 		{"&#xaf", true},
 		{"&#x00", true},
@@ -8695,6 +9930,151 @@ func TestEndsWithValidation(t *testing.T) {
 	}
 }
 
+func TestRequiredIf(t *testing.T) {
+	type Inner struct {
+		Field *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner   *Inner
+		FieldE  string            `validate:"omitempty" json:"field_e"`
+		FieldER string            `validate:"required_if=FieldE test" json:"field_er"`
+		Field1  string            `validate:"omitempty" json:"field_1"`
+		Field2  *string           `validate:"required_if=Field1 test" json:"field_2"`
+		Field3  map[string]string `validate:"required_if=Field2 test" json:"field_3"`
+		Field4  interface{}       `validate:"required_if=Field3 1" json:"field_4"`
+		Field5  int               `validate:"required_if=Inner.Field test" json:"field_5"`
+		Field6  uint              `validate:"required_if=Field5 1" json:"field_6"`
+		Field7  float32           `validate:"required_if=Field6 1" json:"field_7"`
+		Field8  float64           `validate:"required_if=Field7 1.0" json:"field_8"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: 2,
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner   *Inner
+		Inner2  *Inner
+		FieldE  string            `validate:"omitempty" json:"field_e"`
+		FieldER string            `validate:"required_if=FieldE test" json:"field_er"`
+		Field1  string            `validate:"omitempty" json:"field_1"`
+		Field2  *string           `validate:"required_if=Field1 test" json:"field_2"`
+		Field3  map[string]string `validate:"required_if=Field2 test" json:"field_3"`
+		Field4  interface{}       `validate:"required_if=Field2 test" json:"field_4"`
+		Field5  string            `validate:"required_if=Field3 1" json:"field_5"`
+		Field6  string            `validate:"required_if=Inner.Field test" json:"field_6"`
+		Field7  string            `validate:"required_if=Inner2.Field test" json:"field_7"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field2: &fieldVal,
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 3)
+	AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_if")
+	AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_if")
+	AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_if")
+
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("test3 should have panicked!")
+		}
+	}()
+
+	test3 := struct {
+		Inner  *Inner
+		Field1 string `validate:"required_if=Inner.Field" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &fieldVal},
+	}
+	_ = validate.Struct(test3)
+}
+
+func TestRequiredUnless(t *testing.T) {
+	type Inner struct {
+		Field *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner   *Inner
+		FieldE  string            `validate:"omitempty" json:"field_e"`
+		FieldER string            `validate:"required_unless=FieldE test" json:"field_er"`
+		Field1  string            `validate:"omitempty" json:"field_1"`
+		Field2  *string           `validate:"required_unless=Field1 test" json:"field_2"`
+		Field3  map[string]string `validate:"required_unless=Field2 test" json:"field_3"`
+		Field4  interface{}       `validate:"required_unless=Field3 1" json:"field_4"`
+		Field5  int               `validate:"required_unless=Inner.Field test" json:"field_5"`
+		Field6  uint              `validate:"required_unless=Field5 2" json:"field_6"`
+		Field7  float32           `validate:"required_unless=Field6 0" json:"field_7"`
+		Field8  float64           `validate:"required_unless=Field7 0.0" json:"field_8"`
+	}{
+		FieldE: "test",
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: 2,
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner   *Inner
+		Inner2  *Inner
+		FieldE  string            `validate:"omitempty" json:"field_e"`
+		FieldER string            `validate:"required_unless=FieldE test" json:"field_er"`
+		Field1  string            `validate:"omitempty" json:"field_1"`
+		Field2  *string           `validate:"required_unless=Field1 test" json:"field_2"`
+		Field3  map[string]string `validate:"required_unless=Field2 test" json:"field_3"`
+		Field4  interface{}       `validate:"required_unless=Field2 test" json:"field_4"`
+		Field5  string            `validate:"required_unless=Field3 0" json:"field_5"`
+		Field6  string            `validate:"required_unless=Inner.Field test" json:"field_6"`
+		Field7  string            `validate:"required_unless=Inner2.Field test" json:"field_7"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		FieldE: "test",
+		Field1: "test",
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 3)
+	AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_unless")
+	AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_unless")
+	AssertError(t, errs, "Field7", "Field7", "Field7", "Field7", "required_unless")
+
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("test3 should have panicked!")
+		}
+	}()
+
+	test3 := struct {
+		Inner  *Inner
+		Field1 string `validate:"required_unless=Inner.Field" json:"field_1"`
+	}{
+		Inner: &Inner{Field: &fieldVal},
+	}
+	_ = validate.Struct(test3)
+}
+
 func TestRequiredWith(t *testing.T) {
 	type Inner struct {
 		Field *string
@@ -8750,6 +10130,268 @@ func TestRequiredWith(t *testing.T) {
 	AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_with")
 }
 
+func TestExcludedWith(t *testing.T) {
+	type Inner struct {
+		FieldE string
+		Field  *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_with=FieldE" json:"field_1"`
+		Field2 *string           `validate:"excluded_with=FieldE" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_with=FieldE" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_with=FieldE" json:"field_4"`
+		Field5 string            `validate:"excluded_with=Inner.FieldE" json:"field_5"`
+		Field6 string            `validate:"excluded_with=Inner2.FieldE" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_with=Field" json:"field_1"`
+		Field2 *string           `validate:"excluded_with=Field" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_with=Field" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_with=Field" json:"field_4"`
+		Field5 string            `validate:"excluded_with=Inner.Field" json:"field_5"`
+		Field6 string            `validate:"excluded_with=Inner2.Field" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field:  "populated",
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 5)
+	for i := 1; i <= 5; i++ {
+		name := fmt.Sprintf("Field%d", i)
+		AssertError(t, errs, name, name, name, name, "excluded_with")
+	}
+}
+
+func TestExcludedWithout(t *testing.T) {
+	type Inner struct {
+		FieldE string
+		Field  *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_without=Field" json:"field_1"`
+		Field2 *string           `validate:"excluded_without=Field" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_without=Field" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_without=Field" json:"field_4"`
+		Field5 string            `validate:"excluded_without=Inner.Field" json:"field_5"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field:  "populated",
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_without=FieldE" json:"field_1"`
+		Field2 *string           `validate:"excluded_without=FieldE" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_without=FieldE" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_without=FieldE" json:"field_4"`
+		Field5 string            `validate:"excluded_without=Inner.FieldE" json:"field_5"`
+		Field6 string            `validate:"excluded_without=Inner2.FieldE" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 6)
+	for i := 1; i <= 6; i++ {
+		name := fmt.Sprintf("Field%d", i)
+		AssertError(t, errs, name, name, name, name, "excluded_without")
+	}
+}
+
+func TestExcludedWithAll(t *testing.T) {
+	type Inner struct {
+		FieldE string
+		Field  *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_with_all=FieldE Field" json:"field_1"`
+		Field2 *string           `validate:"excluded_with_all=FieldE Field" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_with_all=FieldE Field" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_with_all=FieldE Field" json:"field_4"`
+		Field5 string            `validate:"excluded_with_all=Inner.FieldE" json:"field_5"`
+		Field6 string            `validate:"excluded_with_all=Inner2.FieldE" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field:  fieldVal,
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_with_all=Field FieldE" json:"field_1"`
+		Field2 *string           `validate:"excluded_with_all=Field FieldE" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_with_all=Field FieldE" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_with_all=Field FieldE" json:"field_4"`
+		Field5 string            `validate:"excluded_with_all=Inner.Field" json:"field_5"`
+		Field6 string            `validate:"excluded_with_all=Inner2.Field" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field:  "populated",
+		FieldE: "populated",
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 5)
+	for i := 1; i <= 5; i++ {
+		name := fmt.Sprintf("Field%d", i)
+		AssertError(t, errs, name, name, name, name, "excluded_with_all")
+	}
+}
+
+func TestExcludedWithoutAll(t *testing.T) {
+	type Inner struct {
+		FieldE string
+		Field  *string
+	}
+
+	fieldVal := "test"
+	test := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_without_all=Field FieldE" json:"field_1"`
+		Field2 *string           `validate:"excluded_without_all=Field FieldE" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_without_all=Field FieldE" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_without_all=Field FieldE" json:"field_4"`
+		Field5 string            `validate:"excluded_without_all=Inner.Field Inner.Field2" json:"field_5"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field:  "populated",
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+	}
+
+	validate := New()
+
+	errs := validate.Struct(test)
+	Equal(t, errs, nil)
+
+	test2 := struct {
+		Inner  *Inner
+		Inner2 *Inner
+		Field  string            `validate:"omitempty" json:"field"`
+		FieldE string            `validate:"omitempty" json:"field_e"`
+		Field1 string            `validate:"excluded_without_all=FieldE Field" json:"field_1"`
+		Field2 *string           `validate:"excluded_without_all=FieldE Field" json:"field_2"`
+		Field3 map[string]string `validate:"excluded_without_all=FieldE Field" json:"field_3"`
+		Field4 interface{}       `validate:"excluded_without_all=FieldE Field" json:"field_4"`
+		Field5 string            `validate:"excluded_without_all=Inner.FieldE" json:"field_5"`
+		Field6 string            `validate:"excluded_without_all=Inner2.FieldE" json:"field_6"`
+	}{
+		Inner:  &Inner{Field: &fieldVal},
+		Field1: fieldVal,
+		Field2: &fieldVal,
+		Field3: map[string]string{"key": "val"},
+		Field4: "test",
+		Field5: "test",
+		Field6: "test",
+	}
+
+	errs = validate.Struct(test2)
+	NotEqual(t, errs, nil)
+
+	ve := errs.(ValidationErrors)
+	Equal(t, len(ve), 6)
+	for i := 1; i <= 6; i++ {
+		name := fmt.Sprintf("Field%d", i)
+		AssertError(t, errs, name, name, name, name, "excluded_without_all")
+	}
+}
+
 func TestRequiredWithAll(t *testing.T) {
 	type Inner struct {
 		Field *string
@@ -8858,6 +10500,16 @@ func TestRequiredWithout(t *testing.T) {
 	AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_without")
 	AssertError(t, errs, "Field7", "Field7", "Field7", "Field7", "required_without")
 	AssertError(t, errs, "Field8", "Field8", "Field8", "Field8", "required_without")
+
+	test3 := struct {
+		Field1 *string `validate:"required_without=Field2,omitempty,min=1" json:"field_1"`
+		Field2 *string `validate:"required_without=Field1,omitempty,min=1" json:"field_2"`
+	}{
+		Field1: &fieldVal,
+	}
+
+	errs = validate.Struct(&test3)
+	Equal(t, errs, nil)
 }
 
 func TestRequiredWithoutAll(t *testing.T) {
@@ -9000,7 +10652,7 @@ func TestGetTag(t *testing.T) {
 	}
 
 	val := New()
-	val.RegisterValidation("mytag", func(fl FieldLevel) bool {
+	_ = val.RegisterValidation("mytag", func(fl FieldLevel) bool {
 		tag = fl.GetTag()
 		return true
 	})
@@ -9201,3 +10853,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")
+}