How to validate a 5-field cron (without relying on your scheduler)
Basic validation for 5-field cron (min hour dom month dow) with *, */n and numbers. Useful to catch obvious mistakes before deploying.
Use this article when...
This section is built for speed: one concrete task, multiple stacks, and the minimum context needed not to copy the wrong thing.
These pages work best when the incident is already concrete and you need a fast implementation path.
The snippet is only useful if the runtime, dependency model, and assumptions match your environment.
If the problem expands into architecture, security, or production tradeoffs, stop treating it like a copy-paste task.
What this article gives you
A short path from incident to implementation: context, language-specific snippets, and the warning signs that usually get skipped.
13
3
cron, devops, scheduling, validation
Cron has dialects. Before relying on your scheduler, validate the basics: 5 fields and in-range values. This avoids most silly PR mistakes.
These snippets validate a useful subset (*, */n, and numbers). For ranges/lists or “next run times”, use a cron library or the Cron Parser.
Dependency snapshot by language
Check this first to see which snippets use built-in/runtime APIs and which ones need external packages before you start copying code.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Built-in or runtime API
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
Review the notes for setup details.
How to use these code examples well
The snippet is only the fast path. The explanation and notes are what keep you from copying the wrong assumption into production.
Make sure the article really matches the issue you are debugging before copying code just because the title looks close.
That is where portability limits, dependency requirements, and runtime assumptions usually appear.
If the code runs but the bug remains, jump to the related tool and inspect the real payload, token, timestamp, or config.
Code examples by language
Snippets that compile/run, plus short explanations and real-world notes.
Bash
is_num() { [[ "$1" =~ ^[0-9]+$ ]]; }
check_field() {
local f="$1" min=$2 max=$3
if [ "$f" = "*" ]; then return 0; fi
if [[ "$f" =~ ^\*/([0-9]+)$ ]]; then
local step=${BASH_REMATCH[1]}
is_num "$step" && [ "$step" -ge 1 ] && [ "$step" -le "$max" ]
return $?
fi
if is_num "$f" && [ "$f" -ge "$min" ] && [ "$f" -le "$max" ]; then return 0; fi
return 1
}
cron="*/5 * * * *"
read -r m h dom mon dow <<<"$cron"
if check_field "$m" 0 59 && check_field "$h" 0 23 && check_field "$dom" 1 31 && check_field "$mon" 1 12 && check_field "$dow" 0 7; then
echo OK
else
echo INVALID
fi
Validates simple cron with no dependencies: 5 fields, with * or */n or a number within range. Useful as a guardrail.
- No ranges (1-5) or lists (1,2).
- DOW varies by platform; this example accepts 0–7.
C#
using System;
using System.Text.RegularExpressions;
bool CheckField(string f, int min, int max) {
if (f == "*") return true;
var m = Regex.Match(f, @"^\*/(\d+)$");
if (m.Success) {
var step = int.Parse(m.Groups[1].Value);
return step >= 1 && step <= max;
}
if (int.TryParse(f, out var n)) return n >= min && n <= max;
return false;
}
var cron = "*/5 * * * *";
var p = cron.Split(" ", StringSplitOptions.RemoveEmptyEntries);
var ok = p.Length == 5
&& CheckField(p[0], 0, 59)
&& CheckField(p[1], 0, 23)
&& CheckField(p[2], 1, 31)
&& CheckField(p[3], 1, 12)
&& CheckField(p[4], 0, 7);
Console.WriteLine(ok ? "OK" : "INVALID");
Split + ranges + */n support. Good first filter before using a full parser.
- No ranges/lists.
- For “next run” use NCrontab or another library.
Delphi
program CronValidate;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.RegularExpressions;
function CheckField(const F: string; MinV, MaxV: Integer): Boolean;
var
N: Integer;
M: TMatch;
begin
if F = "*" then Exit(True);
M := TRegEx.Match(F, "^\*/(\d+)$");
if M.Success then
begin
N := StrToIntDef(M.Groups[1].Value, 0);
Exit((N >= 1) and (N <= MaxV));
end;
N := StrToIntDef(F, -1);
Result := (N >= MinV) and (N <= MaxV);
end;
var
Cron: string;
Parts: TArray<string>;
Ok: Boolean;
begin
Cron := "*/5 * * * *";
Parts := Cron.Split([Char(32)], TStringSplitOptions.ExcludeEmpty);
Ok := (Length(Parts) = 5)
and CheckField(Parts[0], 0, 59)
and CheckField(Parts[1], 0, 23)
and CheckField(Parts[2], 1, 31)
and CheckField(Parts[3], 1, 12)
and CheckField(Parts[4], 0, 7);
if Ok then Writeln("OK") else Writeln("INVALID");
end.
Minimal validator: * / */n / number in range. For advanced cron, use a dedicated library.
- No ranges/lists.
Go
package main
import (
"fmt"
"regexp"
"strconv"
"strings"
)
var stepRe = regexp.MustCompile(`^\*/(\d+)$`)
func checkField(f string, min, max int) bool {
if f == "*" {
return true
}
if m := stepRe.FindStringSubmatch(f); m != nil {
step, _ := strconv.Atoi(m[1])
return step >= 1 && step <= max
}
n, err := strconv.Atoi(f)
if err != nil {
return false
}
return n >= min && n <= max
}
func main() {
cron := "*/5 * * * *"
p := strings.Fields(cron)
ok := len(p) == 5 &&
checkField(p[0], 0, 59) &&
checkField(p[1], 0, 23) &&
checkField(p[2], 1, 31) &&
checkField(p[3], 1, 12) &&
checkField(p[4], 0, 7)
fmt.Println(map[bool]string{true: "OK", false: "INVALID"}[ok])
}
Quick validation for simple cron with no dependencies.
- No ranges/lists.
- For “next run”, use a cron parser.
Java
import java.util.regex.*;
public class Main {
static boolean checkField(String f, int min, int max) {
if (f.equals("*")) return true;
Matcher m = Pattern.compile("^\*/(\d+)$").matcher(f);
if (m.find()) {
int step = Integer.parseInt(m.group(1));
return step >= 1 && step <= max;
}
try {
int n = Integer.parseInt(f);
return n >= min && n <= max;
} catch (NumberFormatException e) {
return false;
}
}
public static void main(String[] args) {
String cron = "*/5 * * * *";
String[] p = cron.trim().split("\s+");
boolean ok = p.length == 5
&& checkField(p[0], 0, 59)
&& checkField(p[1], 0, 23)
&& checkField(p[2], 1, 31)
&& checkField(p[3], 1, 12)
&& checkField(p[4], 0, 7);
System.out.println(ok ? "OK" : "INVALID");
}
}
Simple validation without cron libraries. Useful to avoid out-of-range values.
- Limited.
- For real cron, use cron-utils.
JavaScript
function checkField(f, min, max) {
if (f === "*") return true;
const step = f.match(/^\*\/(\d+)$/);
if (step) return Number(step[1]) >= 1 && Number(step[1]) <= max;
const n = Number(f);
return Number.isInteger(n) && n >= min && n <= max;
}
const cron = "*/5 * * * *";
const p = cron.trim().split(/\s+/);
const ok = p.length === 5
&& checkField(p[0], 0, 59)
&& checkField(p[1], 0, 23)
&& checkField(p[2], 1, 31)
&& checkField(p[3], 1, 12)
&& checkField(p[4], 0, 7);
console.log(ok ? "OK" : "INVALID");
Validates simple cron in JS. Perfect for pre-checks in tooling.
- No ranges/lists.
Kotlin
fun checkField(f: String, min: Int, max: Int): Boolean {
if (f == "*") return true
val step = Regex("^\*/(\d+)$").matchEntire(f)
if (step != null) {
val n = step.groupValues[1].toInt()
return n in 1..max
}
val n = f.toIntOrNull() ?: return false
return n in min..max
}
fun main() {
val cron = "*/5 * * * *"
val p = cron.trim().split(Regex("\s+"))
val ok = p.size == 5
&& checkField(p[0], 0, 59)
&& checkField(p[1], 0, 23)
&& checkField(p[2], 1, 31)
&& checkField(p[3], 1, 12)
&& checkField(p[4], 0, 7)
println(if (ok) "OK" else "INVALID")
}
Validates simple cron in Kotlin with no dependencies.
- Limited to *, */n, number.
PHP
<?php
function checkField(string $f, int $min, int $max): bool {
if ($f === "*") return true;
if (preg_match("/^\*\/(\d+)$/", $f, $m)) {
$step = (int)$m[1];
return $step >= 1 && $step <= $max;
}
if (!ctype_digit($f)) return false;
$n = (int)$f;
return $n >= $min && $n <= $max;
}
$cron = "*/5 * * * *";
$p = preg_split("/\s+/", trim($cron));
$ok = count($p) === 5
&& checkField($p[0], 0, 59)
&& checkField($p[1], 0, 23)
&& checkField($p[2], 1, 31)
&& checkField($p[3], 1, 12)
&& checkField($p[4], 0, 7);
echo $ok ? "OK\n" : "INVALID\n";
Minimal validation for simple cron in PHP.
- No ranges/lists.
Python
import re
step_re = re.compile(r"^\*/(\d+)$")
def check_field(f: str, min_v: int, max_v: int) -> bool:
if f == "*":
return True
m = step_re.match(f)
if m:
step = int(m.group(1))
return 1 <= step <= max_v
if f.isdigit():
n = int(f)
return min_v <= n <= max_v
return False
cron = "*/5 * * * *"
p = cron.split()
ok = (
len(p) == 5
and check_field(p[0], 0, 59)
and check_field(p[1], 0, 23)
and check_field(p[2], 1, 31)
and check_field(p[3], 1, 12)
and check_field(p[4], 0, 7)
)
print("OK" if ok else "INVALID")
Minimal validator in Python. For real cron, croniter is a common choice (dependency).
- Std.
- Timezone/DST matters in production.
Ruby
def check_field(f, min, max)
return true if f == "*"
m = f.match(/^\*\/(\d+)$/)
return (1..max).include?(m[1].to_i) if m
return false unless f.match?(/^\d+$/)
n = f.to_i
n >= min && n <= max
end
cron = "*/5 * * * *"
p = cron.split
ok = p.length == 5 &&
check_field(p[0], 0, 59) &&
check_field(p[1], 0, 23) &&
check_field(p[2], 1, 31) &&
check_field(p[3], 1, 12) &&
check_field(p[4], 0, 7)
puts(ok ? "OK" : "INVALID")
Simple validation without gems. For “next run”, use a cron gem (fugit).
- Limited.
Rust
fn check_field(f: &str, min: i32, max: i32) -> bool {
if f == "*" {
return true;
}
if let Some(step) = f.strip_prefix("*/") {
return step.parse::<i32>().map(|n| n >= 1 && n <= max).unwrap_or(false);
}
f.parse::<i32>().map(|n| n >= min && n <= max).unwrap_or(false)
}
fn main() {
let cron = "*/5 * * * *";
let p: Vec<&str> = cron.split_whitespace().collect();
let ok = p.len() == 5
&& check_field(p[0], 0, 59)
&& check_field(p[1], 0, 23)
&& check_field(p[2], 1, 31)
&& check_field(p[3], 1, 12)
&& check_field(p[4], 0, 7);
println!("{}", if ok { "OK" } else { "INVALID" });
}
Validates a useful cron subset with no crates.
- No ranges/lists.
TypeScript
function checkField(f: string, min: number, max: number): boolean {
if (f === "*") return true;
const step = f.match(/^\*\/(\d+)$/);
if (step) return Number(step[1]) >= 1 && Number(step[1]) <= max;
const n = Number(f);
return Number.isInteger(n) && n >= min && n <= max;
}
const cron = "*/5 * * * *";
const p = cron.trim().split(/\s+/);
const ok = p.length === 5
&& checkField(p[0], 0, 59)
&& checkField(p[1], 0, 23)
&& checkField(p[2], 1, 31)
&& checkField(p[3], 1, 12)
&& checkField(p[4], 0, 7);
console.log(ok ? "OK" : "INVALID");
Minimal validator in TS.
- Limited.
VB.NET
Imports System
Imports System.Text.RegularExpressions
Module Program
Private Function CheckField(f As String, min As Integer, max As Integer) As Boolean
If f = "*" Then Return True
Dim m = Regex.Match(f, "^\*/(\d+)$")
If m.Success Then
Dim stepN = Integer.Parse(m.Groups(1).Value)
Return stepN >= 1 AndAlso stepN <= max
End If
Dim n As Integer
If Integer.TryParse(f, n) Then Return n >= min AndAlso n <= max
Return False
End Function
Sub Main()
Dim cron = "*/5 * * * *"
Dim p = cron.Split(New Char() {" "c}, StringSplitOptions.RemoveEmptyEntries)
Dim ok = p.Length = 5 AndAlso
CheckField(p(0), 0, 59) AndAlso
CheckField(p(1), 0, 23) AndAlso
CheckField(p(2), 1, 31) AndAlso
CheckField(p(3), 1, 12) AndAlso
CheckField(p(4), 0, 7)
Console.WriteLine(If(ok, "OK", "INVALID"))
End Sub
End Module
Basic validation in VB.NET to catch out-of-range values.
- No ranges/lists.
Related tools
Use the tool when you need to generate or validate output fast.
Trust and privacy
Copy/paste-friendly by design. Still, treat real production secrets carefully.
- Prefer synthetic examples when sharing screens or recording.
- Do not paste real API keys, tokens, or personal data unless you fully trust your environment.
- If you need ordering, do not use UUID v4 as a sortable id: add createdAt or use sortable ids.