Why 24-Hour Time Validation Matters
Server logs, cron schedules, calendar APIs, SQL TIME columns, ISO 8601 timestamps — every system that touches time storage uses the 24-hour clock. When a form asks for a time and users type 13:45 instead of 1:45 PM, you need a fast regex to reject typos before the value hits your database.
The 24-hour clock runs from 00:00 (midnight) through 23:59. There is no 24:00— that's midnight of the next day. A correct regex must reject hours 24 and above and minutes 60 and above.
Breaking Down the Pattern
The core pattern /^([01][0-9]|2[0-3]):[0-5][0-9]$/ uses alternation for the hour and a simple range for the minute:
^— Start anchor; ensures no garbage prefix.([01][0-9]|2[0-3])— Hour: either 00–19 (first alt) or 20–23 (second alt). This is the only tricky part — a single range like[0-2][0-9]would incorrectly accept 24, 25, 26 etc.:— Literal colon separator.[0-5][0-9]— Minute: 00–59.$— End anchor; nothing may follow.
Variants for Different Precision Levels
HH:MM (most common)
/^([01][0-9]|2[0-3]):[0-5][0-9]$/HH:MM:SS
/^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/HH:MM:SS.sss (fractional seconds)
/^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?$/ISO 8601 time with timezone
/^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|[+-]([01][0-9]|2[0-3]):[0-5][0-9])?$/This last pattern matches strings like 14:30:00, 14:30:00Z,14:30:00.123+05:30. Note ISO 8601 permits either T or space as the date-time separator when combined with a date.
Language-Specific Usage
JavaScript
const timeRegex = /^([01][0-9]|2[0-3]):[0-5][0-9]$/;
function isValidTime(s) {
return timeRegex.test(s);
}
isValidTime('14:30'); // true
isValidTime('00:00'); // true
isValidTime('23:59'); // true
isValidTime('24:00'); // false
isValidTime('12:60'); // false
isValidTime('4:30'); // false (no leading zero)
Python
import re
TIME_RE = re.compile(r'^([01][0-9]|2[0-3]):[0-5][0-9]$')
def is_valid_time(s):
return bool(TIME_RE.match(s))
print(is_valid_time('14:30')) # True
print(is_valid_time('25:00')) # False
# For HH:MM:SS
TIME_SEC_RE = re.compile(r'^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$')
print(bool(TIME_SEC_RE.match('23:59:59'))) # True
PHP
<?php
function isValidTime($s) {
return (bool) preg_match('/^([01][0-9]|2[0-3]):[0-5][0-9]$/', $s);
}
var_dump(isValidTime('14:30')); // true
var_dump(isValidTime('26:00')); // false
Java
import java.util.regex.Pattern;
Pattern TIME_RE = Pattern.compile("^([01][0-9]|2[0-3]):[0-5][0-9]$");
boolean isValidTime(String s) {
return TIME_RE.matcher(s).matches();
}
isValidTime("14:30"); // true
isValidTime("24:00"); // false
Optional Leading Zero
Some UIs allow the user to type 9:05 instead of 09:05. If you need to accept both, make the leading zero optional:
/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/Matches 9:05, 09:05, 14:30, 23:59. Still rejects 24:00 and 12:60. But be aware: relaxing the leading zero means your parsers must handle both widths — most databases and log formats insist on the two-digit form.
Combine With Native Date Parsing
Regex tells you the string looks like a valid time; native parsing tells you whether it is a valid time-of-day and lets you compare, subtract or format it. Use both:
function parseTime(s) {
if (!/^([01][0-9]|2[0-3]):[0-5][0-9]$/.test(s)) return null;
const d = new Date('1970-01-01T' + s + ':00Z');
return isNaN(d.getTime()) ? null : d;
}
const t = parseTime('14:30');
// t.getUTCHours() === 14
Explore Related Regex Patterns
- Regex for date format — YYYY-MM-DD, DD/MM/YYYY and MM/DD/YYYY.
- Regex for email validation — the standard email pattern.
- Regex for numbers — integers, decimals and signed values.
- Regex for UUIDs — version-aware UUID patterns.
- Regex for credit card numbers — Visa, Mastercard, Amex.
- Regex for domain names — matching bare hostnames.
Frequently Asked Questions
Should I use regex or a date library?
Regex is perfect for format validation (does the string look like a time). For arithmetic, timezone handling and formatting, use a library like date-fns,Luxon or Python's datetime. They complement each other.
How do I validate 12-hour time with AM/PM?
Use /^(0?[1-9]|1[0-2]):[0-5][0-9]\s?(AM|PM|am|pm)$/. The hour range 01–12, then colon, then minutes 00–59, then optional space, then AM/PM in either case.
Can I match times without seconds but with an optional seconds group?
Yes: /^([01][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/. The final group is wrapped in (...)? making it optional.
Does this work for time durations like "14:30" meaning 14 hours 30 minutes?
The pattern only validates times-of-day (00:00–23:59). For durations that can exceed 24 hours use /^\\d+:[0-5][0-9]$/ — no hour cap.