Repository: go-playground/validator
Pull request #678 (2020-10-17 15:44:52)

Fix time.Duration parsing for int param
## Fixes Or Enhances # .
This fixes an issue where if the param of a time.Durtion type is specified
as an integer, denoting nanosecond precision, instead of time duration
string the validation would panic.

the fixes ensures it falls back to the previous expected behaviour.

**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.


coveralls: 
[![Coverage Status](https://coveralls.io/builds/34252075/badge)](https://coveralls.io/builds/34252075)

Coverage decreased (-0.006%) to 76.07% when pulling **6b11ad6f225e751e4d3e8608000578b2833b6742 on fix-duration** into **d6b17fd90bd4dd9d16a594c3035ceadc3de0193a on master**.


Patch:
diff --git a/README.md b/README.md
index 054f294..04fbb3c 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 Package validator
 ================
 <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-![Project status](https://img.shields.io/badge/version-10.3.0-green.svg)
+![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
 [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
 [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
 [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
diff --git a/baked_in.go b/baked_in.go
index ca3d3b9..6ce762d 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -67,125 +67,125 @@ var (
 	// you can add, remove or even replace items to suite your needs,
 	// 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,
-		"max":                  hasMaxOf,
-		"eq":                   isEq,
-		"ne":                   isNe,
-		"lt":                   isLt,
-		"lte":                  isLte,
-		"gt":                   isGt,
-		"gte":                  isGte,
-		"eqfield":              isEqField,
-		"eqcsfield":            isEqCrossStructField,
-		"necsfield":            isNeCrossStructField,
-		"gtcsfield":            isGtCrossStructField,
-		"gtecsfield":           isGteCrossStructField,
-		"ltcsfield":            isLtCrossStructField,
-		"ltecsfield":           isLteCrossStructField,
-		"nefield":              isNeField,
-		"gtefield":             isGteField,
-		"gtfield":              isGtField,
-		"ltefield":             isLteField,
-		"ltfield":              isLtField,
-		"fieldcontains":        fieldContains,
-		"fieldexcludes":        fieldExcludes,
-		"alpha":                isAlpha,
-		"alphanum":             isAlphanum,
-		"alphaunicode":         isAlphaUnicode,
-		"alphanumunicode":      isAlphanumUnicode,
-		"numeric":              isNumeric,
-		"number":               isNumber,
-		"hexadecimal":          isHexadecimal,
-		"hexcolor":             isHEXColor,
-		"rgb":                  isRGB,
-		"rgba":                 isRGBA,
-		"hsl":                  isHSL,
-		"hsla":                 isHSLA,
-		"e164":                 isE164,
-		"email":                isEmail,
-		"url":                  isURL,
-		"uri":                  isURI,
-		"urn_rfc2141":          isUrnRFC2141, // RFC 2141
-		"file":                 isFile,
-		"base64":               isBase64,
-		"base64url":            isBase64URL,
-		"contains":             contains,
-		"containsany":          containsAny,
-		"containsrune":         containsRune,
-		"excludes":             excludes,
-		"excludesall":          excludesAll,
-		"excludesrune":         excludesRune,
-		"startswith":           startsWith,
-		"endswith":             endsWith,
-		"startsnotwith":        startsNotWith,
-		"endsnotwith":          endsNotWith,
-		"isbn":                 isISBN,
-		"isbn10":               isISBN10,
-		"isbn13":               isISBN13,
-		"eth_addr":             isEthereumAddress,
-		"btc_addr":             isBitcoinAddress,
-		"btc_addr_bech32":      isBitcoinBech32Address,
-		"uuid":                 isUUID,
-		"uuid3":                isUUID3,
-		"uuid4":                isUUID4,
-		"uuid5":                isUUID5,
-		"uuid_rfc4122":         isUUIDRFC4122,
-		"uuid3_rfc4122":        isUUID3RFC4122,
-		"uuid4_rfc4122":        isUUID4RFC4122,
-		"uuid5_rfc4122":        isUUID5RFC4122,
-		"ascii":                isASCII,
-		"printascii":           isPrintableASCII,
-		"multibyte":            hasMultiByteCharacter,
-		"datauri":              isDataURI,
-		"latitude":             isLatitude,
-		"longitude":            isLongitude,
-		"ssn":                  isSSN,
-		"ipv4":                 isIPv4,
-		"ipv6":                 isIPv6,
-		"ip":                   isIP,
-		"cidrv4":               isCIDRv4,
-		"cidrv6":               isCIDRv6,
-		"cidr":                 isCIDR,
-		"tcp4_addr":            isTCP4AddrResolvable,
-		"tcp6_addr":            isTCP6AddrResolvable,
-		"tcp_addr":             isTCPAddrResolvable,
-		"udp4_addr":            isUDP4AddrResolvable,
-		"udp6_addr":            isUDP6AddrResolvable,
-		"udp_addr":             isUDPAddrResolvable,
-		"ip4_addr":             isIP4AddrResolvable,
-		"ip6_addr":             isIP6AddrResolvable,
-		"ip_addr":              isIPAddrResolvable,
-		"unix_addr":            isUnixAddrResolvable,
-		"mac":                  isMAC,
-		"hostname":             isHostnameRFC952,  // RFC 952
-		"hostname_rfc1123":     isHostnameRFC1123, // RFC 1123
-		"fqdn":                 isFQDN,
-		"unique":               isUnique,
-		"oneof":                isOneOf,
-		"html":                 isHTML,
-		"html_encoded":         isHTMLEncoded,
-		"url_encoded":          isURLEncoded,
-		"dir":                  isDir,
-		"json":                 isJSON,
-		"hostname_port":        isHostnamePort,
-		"lowercase":            isLowercase,
-		"uppercase":            isUppercase,
-		"datetime":             isDatetime,
-		"timezone":             isTimeZone,
-    "iso3166_1_alpha2":        isIso3166Alpha2,
+		"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,
+		"max":                     hasMaxOf,
+		"eq":                      isEq,
+		"ne":                      isNe,
+		"lt":                      isLt,
+		"lte":                     isLte,
+		"gt":                      isGt,
+		"gte":                     isGte,
+		"eqfield":                 isEqField,
+		"eqcsfield":               isEqCrossStructField,
+		"necsfield":               isNeCrossStructField,
+		"gtcsfield":               isGtCrossStructField,
+		"gtecsfield":              isGteCrossStructField,
+		"ltcsfield":               isLtCrossStructField,
+		"ltecsfield":              isLteCrossStructField,
+		"nefield":                 isNeField,
+		"gtefield":                isGteField,
+		"gtfield":                 isGtField,
+		"ltefield":                isLteField,
+		"ltfield":                 isLtField,
+		"fieldcontains":           fieldContains,
+		"fieldexcludes":           fieldExcludes,
+		"alpha":                   isAlpha,
+		"alphanum":                isAlphanum,
+		"alphaunicode":            isAlphaUnicode,
+		"alphanumunicode":         isAlphanumUnicode,
+		"numeric":                 isNumeric,
+		"number":                  isNumber,
+		"hexadecimal":             isHexadecimal,
+		"hexcolor":                isHEXColor,
+		"rgb":                     isRGB,
+		"rgba":                    isRGBA,
+		"hsl":                     isHSL,
+		"hsla":                    isHSLA,
+		"e164":                    isE164,
+		"email":                   isEmail,
+		"url":                     isURL,
+		"uri":                     isURI,
+		"urn_rfc2141":             isUrnRFC2141, // RFC 2141
+		"file":                    isFile,
+		"base64":                  isBase64,
+		"base64url":               isBase64URL,
+		"contains":                contains,
+		"containsany":             containsAny,
+		"containsrune":            containsRune,
+		"excludes":                excludes,
+		"excludesall":             excludesAll,
+		"excludesrune":            excludesRune,
+		"startswith":              startsWith,
+		"endswith":                endsWith,
+		"startsnotwith":           startsNotWith,
+		"endsnotwith":             endsNotWith,
+		"isbn":                    isISBN,
+		"isbn10":                  isISBN10,
+		"isbn13":                  isISBN13,
+		"eth_addr":                isEthereumAddress,
+		"btc_addr":                isBitcoinAddress,
+		"btc_addr_bech32":         isBitcoinBech32Address,
+		"uuid":                    isUUID,
+		"uuid3":                   isUUID3,
+		"uuid4":                   isUUID4,
+		"uuid5":                   isUUID5,
+		"uuid_rfc4122":            isUUIDRFC4122,
+		"uuid3_rfc4122":           isUUID3RFC4122,
+		"uuid4_rfc4122":           isUUID4RFC4122,
+		"uuid5_rfc4122":           isUUID5RFC4122,
+		"ascii":                   isASCII,
+		"printascii":              isPrintableASCII,
+		"multibyte":               hasMultiByteCharacter,
+		"datauri":                 isDataURI,
+		"latitude":                isLatitude,
+		"longitude":               isLongitude,
+		"ssn":                     isSSN,
+		"ipv4":                    isIPv4,
+		"ipv6":                    isIPv6,
+		"ip":                      isIP,
+		"cidrv4":                  isCIDRv4,
+		"cidrv6":                  isCIDRv6,
+		"cidr":                    isCIDR,
+		"tcp4_addr":               isTCP4AddrResolvable,
+		"tcp6_addr":               isTCP6AddrResolvable,
+		"tcp_addr":                isTCPAddrResolvable,
+		"udp4_addr":               isUDP4AddrResolvable,
+		"udp6_addr":               isUDP6AddrResolvable,
+		"udp_addr":                isUDPAddrResolvable,
+		"ip4_addr":                isIP4AddrResolvable,
+		"ip6_addr":                isIP6AddrResolvable,
+		"ip_addr":                 isIPAddrResolvable,
+		"unix_addr":               isUnixAddrResolvable,
+		"mac":                     isMAC,
+		"hostname":                isHostnameRFC952,  // RFC 952
+		"hostname_rfc1123":        isHostnameRFC1123, // RFC 1123
+		"fqdn":                    isFQDN,
+		"unique":                  isUnique,
+		"oneof":                   isOneOf,
+		"html":                    isHTML,
+		"html_encoded":            isHTMLEncoded,
+		"url_encoded":             isURLEncoded,
+		"dir":                     isDir,
+		"json":                    isJSON,
+		"hostname_port":           isHostnamePort,
+		"lowercase":               isLowercase,
+		"uppercase":               isUppercase,
+		"datetime":                isDatetime,
+		"timezone":                isTimeZone,
+		"iso3166_1_alpha2":        isIso3166Alpha2,
 		"iso3166_1_alpha3":        isIso3166Alpha3,
 		"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
 	}
@@ -2250,11 +2250,7 @@ func isTimeZone(fl FieldLevel) bool {
 		}
 
 		_, err := time.LoadLocation(field.String())
-		if err != nil {
-			return false
-		}
-
-		return true
+		return err == nil
 	}
 
 	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
diff --git a/util.go b/util.go
index 6e568f8..56420f4 100644
--- a/util.go
+++ b/util.go
@@ -223,7 +223,6 @@ BEGIN:
 // asInt returns the parameter as a int64
 // or panics if it can't convert
 func asInt(param string) int64 {
-
 	i, err := strconv.ParseInt(param, 0, 64)
 	panicIf(err)
 
@@ -234,8 +233,10 @@ func asInt(param string) int64 {
 // or panics on error.
 func asIntFromTimeDuration(param string) int64 {
 	d, err := time.ParseDuration(param)
-	panicIf(err)
-
+	if err != nil {
+		// attempt parsing as an an integer assuming nanosecond precision
+		return asInt(param)
+	}
 	return int64(d)
 }
 


Test patch:
diff --git a/validator_test.go b/validator_test.go
index a918bce..6c9fb1e 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -9475,7 +9475,7 @@ func TestUniqueValidationStructPtrSlice(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) {
@@ -10989,4 +10989,47 @@ func TestTimeZoneValidation(t *testing.T) {
 	PanicMatches(t, func() {
 		_ = validate.Var(2, "timezone")
 	}, "Bad field type int")
-}
\ No newline at end of file
+}
+
+func TestDurationType(t *testing.T) {
+	tests := []struct {
+		name    string
+		s       interface{} // struct
+		success bool
+	}{
+		{
+			name: "valid duration string pass",
+			s: struct {
+				Value time.Duration `validate:"gte=500ns"`
+			}{
+				Value: time.Second,
+			},
+			success: true,
+		},
+		{
+			name: "valid duration int pass",
+			s: struct {
+				Value time.Duration `validate:"gte=500"`
+			}{
+				Value: time.Second,
+			},
+			success: true,
+		},
+	}
+
+	validate := New()
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			errs := validate.Struct(tc.s)
+			if tc.success {
+				Equal(t, errs, nil)
+				return
+			}
+			NotEqual(t, errs, nil)
+		})
+	}
+}