Skip to content

Commit 456221b

Browse files
author
Dean Karn
authored
Fix time.Duration parsing for int param (#678)
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.
1 parent d6b17fd commit 456221b

File tree

4 files changed

+170
-130
lines changed

4 files changed

+170
-130
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package validator
22
================
33
<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)
4-
![Project status](https://img.shields.io/badge/version-10.3.0-green.svg)
4+
![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
55
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
66
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
77
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

baked_in.go

Lines changed: 120 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -67,125 +67,125 @@ var (
6767
// you can add, remove or even replace items to suite your needs,
6868
// or even disregard and use your own map if so desired.
6969
bakedInValidators = map[string]Func{
70-
"required": hasValue,
71-
"required_if": requiredIf,
72-
"required_unless": requiredUnless,
73-
"required_with": requiredWith,
74-
"required_with_all": requiredWithAll,
75-
"required_without": requiredWithout,
76-
"required_without_all": requiredWithoutAll,
77-
"excluded_with": excludedWith,
78-
"excluded_with_all": excludedWithAll,
79-
"excluded_without": excludedWithout,
80-
"excluded_without_all": excludedWithoutAll,
81-
"isdefault": isDefault,
82-
"len": hasLengthOf,
83-
"min": hasMinOf,
84-
"max": hasMaxOf,
85-
"eq": isEq,
86-
"ne": isNe,
87-
"lt": isLt,
88-
"lte": isLte,
89-
"gt": isGt,
90-
"gte": isGte,
91-
"eqfield": isEqField,
92-
"eqcsfield": isEqCrossStructField,
93-
"necsfield": isNeCrossStructField,
94-
"gtcsfield": isGtCrossStructField,
95-
"gtecsfield": isGteCrossStructField,
96-
"ltcsfield": isLtCrossStructField,
97-
"ltecsfield": isLteCrossStructField,
98-
"nefield": isNeField,
99-
"gtefield": isGteField,
100-
"gtfield": isGtField,
101-
"ltefield": isLteField,
102-
"ltfield": isLtField,
103-
"fieldcontains": fieldContains,
104-
"fieldexcludes": fieldExcludes,
105-
"alpha": isAlpha,
106-
"alphanum": isAlphanum,
107-
"alphaunicode": isAlphaUnicode,
108-
"alphanumunicode": isAlphanumUnicode,
109-
"numeric": isNumeric,
110-
"number": isNumber,
111-
"hexadecimal": isHexadecimal,
112-
"hexcolor": isHEXColor,
113-
"rgb": isRGB,
114-
"rgba": isRGBA,
115-
"hsl": isHSL,
116-
"hsla": isHSLA,
117-
"e164": isE164,
118-
"email": isEmail,
119-
"url": isURL,
120-
"uri": isURI,
121-
"urn_rfc2141": isUrnRFC2141, // RFC 2141
122-
"file": isFile,
123-
"base64": isBase64,
124-
"base64url": isBase64URL,
125-
"contains": contains,
126-
"containsany": containsAny,
127-
"containsrune": containsRune,
128-
"excludes": excludes,
129-
"excludesall": excludesAll,
130-
"excludesrune": excludesRune,
131-
"startswith": startsWith,
132-
"endswith": endsWith,
133-
"startsnotwith": startsNotWith,
134-
"endsnotwith": endsNotWith,
135-
"isbn": isISBN,
136-
"isbn10": isISBN10,
137-
"isbn13": isISBN13,
138-
"eth_addr": isEthereumAddress,
139-
"btc_addr": isBitcoinAddress,
140-
"btc_addr_bech32": isBitcoinBech32Address,
141-
"uuid": isUUID,
142-
"uuid3": isUUID3,
143-
"uuid4": isUUID4,
144-
"uuid5": isUUID5,
145-
"uuid_rfc4122": isUUIDRFC4122,
146-
"uuid3_rfc4122": isUUID3RFC4122,
147-
"uuid4_rfc4122": isUUID4RFC4122,
148-
"uuid5_rfc4122": isUUID5RFC4122,
149-
"ascii": isASCII,
150-
"printascii": isPrintableASCII,
151-
"multibyte": hasMultiByteCharacter,
152-
"datauri": isDataURI,
153-
"latitude": isLatitude,
154-
"longitude": isLongitude,
155-
"ssn": isSSN,
156-
"ipv4": isIPv4,
157-
"ipv6": isIPv6,
158-
"ip": isIP,
159-
"cidrv4": isCIDRv4,
160-
"cidrv6": isCIDRv6,
161-
"cidr": isCIDR,
162-
"tcp4_addr": isTCP4AddrResolvable,
163-
"tcp6_addr": isTCP6AddrResolvable,
164-
"tcp_addr": isTCPAddrResolvable,
165-
"udp4_addr": isUDP4AddrResolvable,
166-
"udp6_addr": isUDP6AddrResolvable,
167-
"udp_addr": isUDPAddrResolvable,
168-
"ip4_addr": isIP4AddrResolvable,
169-
"ip6_addr": isIP6AddrResolvable,
170-
"ip_addr": isIPAddrResolvable,
171-
"unix_addr": isUnixAddrResolvable,
172-
"mac": isMAC,
173-
"hostname": isHostnameRFC952, // RFC 952
174-
"hostname_rfc1123": isHostnameRFC1123, // RFC 1123
175-
"fqdn": isFQDN,
176-
"unique": isUnique,
177-
"oneof": isOneOf,
178-
"html": isHTML,
179-
"html_encoded": isHTMLEncoded,
180-
"url_encoded": isURLEncoded,
181-
"dir": isDir,
182-
"json": isJSON,
183-
"hostname_port": isHostnamePort,
184-
"lowercase": isLowercase,
185-
"uppercase": isUppercase,
186-
"datetime": isDatetime,
187-
"timezone": isTimeZone,
188-
"iso3166_1_alpha2": isIso3166Alpha2,
70+
"required": hasValue,
71+
"required_if": requiredIf,
72+
"required_unless": requiredUnless,
73+
"required_with": requiredWith,
74+
"required_with_all": requiredWithAll,
75+
"required_without": requiredWithout,
76+
"required_without_all": requiredWithoutAll,
77+
"excluded_with": excludedWith,
78+
"excluded_with_all": excludedWithAll,
79+
"excluded_without": excludedWithout,
80+
"excluded_without_all": excludedWithoutAll,
81+
"isdefault": isDefault,
82+
"len": hasLengthOf,
83+
"min": hasMinOf,
84+
"max": hasMaxOf,
85+
"eq": isEq,
86+
"ne": isNe,
87+
"lt": isLt,
88+
"lte": isLte,
89+
"gt": isGt,
90+
"gte": isGte,
91+
"eqfield": isEqField,
92+
"eqcsfield": isEqCrossStructField,
93+
"necsfield": isNeCrossStructField,
94+
"gtcsfield": isGtCrossStructField,
95+
"gtecsfield": isGteCrossStructField,
96+
"ltcsfield": isLtCrossStructField,
97+
"ltecsfield": isLteCrossStructField,
98+
"nefield": isNeField,
99+
"gtefield": isGteField,
100+
"gtfield": isGtField,
101+
"ltefield": isLteField,
102+
"ltfield": isLtField,
103+
"fieldcontains": fieldContains,
104+
"fieldexcludes": fieldExcludes,
105+
"alpha": isAlpha,
106+
"alphanum": isAlphanum,
107+
"alphaunicode": isAlphaUnicode,
108+
"alphanumunicode": isAlphanumUnicode,
109+
"numeric": isNumeric,
110+
"number": isNumber,
111+
"hexadecimal": isHexadecimal,
112+
"hexcolor": isHEXColor,
113+
"rgb": isRGB,
114+
"rgba": isRGBA,
115+
"hsl": isHSL,
116+
"hsla": isHSLA,
117+
"e164": isE164,
118+
"email": isEmail,
119+
"url": isURL,
120+
"uri": isURI,
121+
"urn_rfc2141": isUrnRFC2141, // RFC 2141
122+
"file": isFile,
123+
"base64": isBase64,
124+
"base64url": isBase64URL,
125+
"contains": contains,
126+
"containsany": containsAny,
127+
"containsrune": containsRune,
128+
"excludes": excludes,
129+
"excludesall": excludesAll,
130+
"excludesrune": excludesRune,
131+
"startswith": startsWith,
132+
"endswith": endsWith,
133+
"startsnotwith": startsNotWith,
134+
"endsnotwith": endsNotWith,
135+
"isbn": isISBN,
136+
"isbn10": isISBN10,
137+
"isbn13": isISBN13,
138+
"eth_addr": isEthereumAddress,
139+
"btc_addr": isBitcoinAddress,
140+
"btc_addr_bech32": isBitcoinBech32Address,
141+
"uuid": isUUID,
142+
"uuid3": isUUID3,
143+
"uuid4": isUUID4,
144+
"uuid5": isUUID5,
145+
"uuid_rfc4122": isUUIDRFC4122,
146+
"uuid3_rfc4122": isUUID3RFC4122,
147+
"uuid4_rfc4122": isUUID4RFC4122,
148+
"uuid5_rfc4122": isUUID5RFC4122,
149+
"ascii": isASCII,
150+
"printascii": isPrintableASCII,
151+
"multibyte": hasMultiByteCharacter,
152+
"datauri": isDataURI,
153+
"latitude": isLatitude,
154+
"longitude": isLongitude,
155+
"ssn": isSSN,
156+
"ipv4": isIPv4,
157+
"ipv6": isIPv6,
158+
"ip": isIP,
159+
"cidrv4": isCIDRv4,
160+
"cidrv6": isCIDRv6,
161+
"cidr": isCIDR,
162+
"tcp4_addr": isTCP4AddrResolvable,
163+
"tcp6_addr": isTCP6AddrResolvable,
164+
"tcp_addr": isTCPAddrResolvable,
165+
"udp4_addr": isUDP4AddrResolvable,
166+
"udp6_addr": isUDP6AddrResolvable,
167+
"udp_addr": isUDPAddrResolvable,
168+
"ip4_addr": isIP4AddrResolvable,
169+
"ip6_addr": isIP6AddrResolvable,
170+
"ip_addr": isIPAddrResolvable,
171+
"unix_addr": isUnixAddrResolvable,
172+
"mac": isMAC,
173+
"hostname": isHostnameRFC952, // RFC 952
174+
"hostname_rfc1123": isHostnameRFC1123, // RFC 1123
175+
"fqdn": isFQDN,
176+
"unique": isUnique,
177+
"oneof": isOneOf,
178+
"html": isHTML,
179+
"html_encoded": isHTMLEncoded,
180+
"url_encoded": isURLEncoded,
181+
"dir": isDir,
182+
"json": isJSON,
183+
"hostname_port": isHostnamePort,
184+
"lowercase": isLowercase,
185+
"uppercase": isUppercase,
186+
"datetime": isDatetime,
187+
"timezone": isTimeZone,
188+
"iso3166_1_alpha2": isIso3166Alpha2,
189189
"iso3166_1_alpha3": isIso3166Alpha3,
190190
"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
191191
}
@@ -2250,11 +2250,7 @@ func isTimeZone(fl FieldLevel) bool {
22502250
}
22512251

22522252
_, err := time.LoadLocation(field.String())
2253-
if err != nil {
2254-
return false
2255-
}
2256-
2257-
return true
2253+
return err == nil
22582254
}
22592255

22602256
panic(fmt.Sprintf("Bad field type %T", field.Interface()))

util.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ BEGIN:
223223
// asInt returns the parameter as a int64
224224
// or panics if it can't convert
225225
func asInt(param string) int64 {
226-
227226
i, err := strconv.ParseInt(param, 0, 64)
228227
panicIf(err)
229228

@@ -234,8 +233,10 @@ func asInt(param string) int64 {
234233
// or panics on error.
235234
func asIntFromTimeDuration(param string) int64 {
236235
d, err := time.ParseDuration(param)
237-
panicIf(err)
238-
236+
if err != nil {
237+
// attempt parsing as an an integer assuming nanosecond precision
238+
return asInt(param)
239+
}
239240
return int64(d)
240241
}
241242

validator_test.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9475,7 +9475,7 @@ func TestUniqueValidationStructPtrSlice(t *testing.T) {
94759475
}
94769476
}
94779477
}
9478-
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
9478+
PanicMatches(t, func() { _ = validate.Var(testStructs, "unique=C") }, "Bad field name C")
94799479
}
94809480

94819481
func TestHTMLValidation(t *testing.T) {
@@ -10989,4 +10989,47 @@ func TestTimeZoneValidation(t *testing.T) {
1098910989
PanicMatches(t, func() {
1099010990
_ = validate.Var(2, "timezone")
1099110991
}, "Bad field type int")
10992-
}
10992+
}
10993+
10994+
func TestDurationType(t *testing.T) {
10995+
tests := []struct {
10996+
name string
10997+
s interface{} // struct
10998+
success bool
10999+
}{
11000+
{
11001+
name: "valid duration string pass",
11002+
s: struct {
11003+
Value time.Duration `validate:"gte=500ns"`
11004+
}{
11005+
Value: time.Second,
11006+
},
11007+
success: true,
11008+
},
11009+
{
11010+
name: "valid duration int pass",
11011+
s: struct {
11012+
Value time.Duration `validate:"gte=500"`
11013+
}{
11014+
Value: time.Second,
11015+
},
11016+
success: true,
11017+
},
11018+
}
11019+
11020+
validate := New()
11021+
11022+
for _, tc := range tests {
11023+
tc := tc
11024+
t.Run(tc.name, func(t *testing.T) {
11025+
t.Parallel()
11026+
11027+
errs := validate.Struct(tc.s)
11028+
if tc.success {
11029+
Equal(t, errs, nil)
11030+
return
11031+
}
11032+
NotEqual(t, errs, nil)
11033+
})
11034+
}
11035+
}

0 commit comments

Comments
 (0)