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.
\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.
# 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"
\d{2} for the month happily matches 13 through 99. You can encode real months — (0[1-9]|1[0-2]) — and real days — (0[1-9]|[12]\d|3[01]) — and that's still wrong for Feb 30. The honest division of labor: regex finds candidates, the date parser validates them. datetime.strptime(s, "%Y-%m-%d") raises on Feb 30; your regex doesn't have to know the calendar.
# 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)
strptime / Date.parse with the declared format is stricter and clearer. Regex earns its place when dates hide in unstructured text: logs, emails, scraped pages.