The patterns for finding dates in text, with capture groups ready to use. Principle up front: regex finds the shape; your date library validates the calendar. A regex that "knows" February has 28 days is a regex nobody can read.
🎙️ Published & recorded: ·
Prefer year, month, day when you control the format. It sorts correctly and avoids regional arguments. Capture the three parts when your code needs them separately. Slashed dates are inherently ambiguous: the same value means different days in Boston and London, so the data contract must decide the order.
\b\d{4}-\d{2}-\d{2}\b
# with capture groups — year, month, day come back separately:
(\d{4})-(\d{2})-(\d{2})
# Python
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
year, month, day = m.groups() # ("2026", "07", "22")\b\d{1,2}/\d{1,2}/\d{4}\b # both DD/MM and MM/DD shapes
# the regex CANNOT tell you which one it is. 04/07/2026 is
# July 4th in Boston and April 7th in London. only context
# (or the data's spec) knows. no pattern fixes ambiguity.Regex earns its keep when dates are embedded in messy text. In logs, capture the date and time as separate pieces, or anchor a search to the beginning of each line. Month names need an explicit list so ordinary words do not become accidental dates.
# classic log line: 2026-07-22 14:30:07,123 ERROR ...
(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})
# grep all of yesterday's errors from a log:
grep -E "^2026-07-21 .*ERROR" app.log
# time on its own (24h):
\b([01]?\d|2[0-3]):[0-5]\d\b # 9:05, 14:30 — rejects 25:99\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]* \d{1,2},? \d{4}\b
# matches "Jul 22, 2026", "July 22 2026", "Dec 3, 1999"A shape match can still describe an impossible day. Do not teach a regex leap years and month lengths. Find candidate strings first, then ask the date library to parse each one. Its actual parse error is useful evidence; catch that error only when skipping bad candidates is the intended behavior.
datetime.strptime raises ValueError: day is out of range for month; fix the source date or reject that candidate rather than growing the regex.# the find-then-parse pattern (Python):
for m in re.finditer(r"\b\d{4}-\d{2}-\d{2}\b", text):
try:
d = datetime.strptime(m.group(), "%Y-%m-%d")
except ValueError:
continue # shaped like a date, isn't one (2026-13-45)