@ -155,6 +155,7 @@ Baked-in Validations
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| cron | Cron |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
@ -218,6 +218,7 @@ var (
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
"cron": isCron,
}
)
@ -2579,3 +2580,9 @@ func isCreditCard(fl FieldLevel) bool {
return (sum % 10) == 0
// isCron is the validation function for validating if the current field's value is a valid cron expression
func isCron(fl FieldLevel) bool {
cronString := fl.Field().String()
return cronRegex.MatchString(cronString)
@ -1323,6 +1323,12 @@ This validates that a string value contains a valid credit card number using Luh
Usage: credit_card
Cron
This validates that a string value contains a valid cron expression.
Usage: cron
Alias Validators and Tags
NOTE: When returning an error, the tag returned in "FieldError" will be
@ -64,6 +64,7 @@ const (
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
var (
@ -126,4 +127,5 @@ var (
bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
cronRegex = regexp.MustCompile(cronRegexString)
@ -1281,6 +1281,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid color",
override: false,
},
{
tag: "cron",
translation: "{0} must be a valid cron expression",
tag: "oneof",
translation: "{0} must be one of [{1}]",
@ -124,7 +124,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
tag: "min",
customRegisFunc: func(ut ut.Translator) (err error) {
if err = ut.Add("min-string", "{0} deve essere lungo almeno {1}", false); err != nil {
return
@ -432,7 +432,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
tag: "lte",
if err = ut.Add("lte-string", "{0} deve essere lungo al massimo {1}", false); err != nil {
@ -1132,6 +1132,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} deve essere un colore valido",
translation: "{0} deve essere una stringa cron valida",
translation: "{0} deve essere uno di [{1}]",
@ -12524,3 +12524,37 @@ func TestMultiOrOperatorGroup(t *testing.T) {
func TestCronExpressionValidation(t *testing.T) {
tests := []struct {
value string `validate:"cron"`
tag string
expected bool
}{
{"0 0 12 * * ?", "cron", true},
{"0 15 10 ? * *", "cron", true},
{"0 15 10 * * ?", "cron", true},
{"0 15 10 * * ? 2005", "cron", true},
{"0 15 10 ? * 6L", "cron", true},
{"0 15 10 ? * 6L 2002-2005", "cron", true},
{"*/20 * * * *", "cron", true},
{"0 15 10 ? * MON-FRI", "cron", true},
{"0 15 10 ? * 6#3", "cron", true},
{"wrong", "cron", 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 cron "%s" failed Error: %s`, i, test.value, errs)
} else {
if IsEqual(errs, nil) {
t.Fatalf(`Index: %d cron "%s" should have errs`, i, test.value)