必须使用^和$锚定正则以实现全字符串匹配,防止误判;24小时制应校验小时0-23、分钟秒0-59,避免\d{1,2}导致99:00等非法值;12小时制需显式处理空格与am/pm大小写;混合日期时间建议分步验证;警惕灾难性回溯,优先用DateTime::createFromFormat()校验。

匹配 24 小时制时间(HH:MM 或 HH:MM:SS)要严格锚定
不加 ^ 和 $ 容易把 "abc12:30def" 这类字符串误判为有效。真实场景中,用户可能在表单里多输空格或符号,所以必须强制全字符串匹配。
-
/^(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?$/—— 支持秒字段可选,且能匹配"9:05"、"09:05:00"、"23:59:59" - 别用
\d{1,2}直接匹配小时:它会放过"99:00";[0-9]{1,2}同样危险 - 分组用
(?:...)而非(...):避免无意义捕获,减少$matches数组冗余项
处理 12 小时制(am/pm)时大小写和空格必须显式覆盖
用户输入可能是 "1:30pm"、"01:30 PM"、"7:45am ",甚至带中文空格。正则不能依赖 i 修饰符兜底所有情况,因为 am 和 pm 前后空格位置不固定。
- 推荐写法:
/^(0?[1-9]|1[0-2]):[0-5][0-9]\s*(?i)(am|pm)$/ -
\s*比\s?更稳妥:容忍多个空格、制表符甚至零宽空格 - 不要写
(a.m.|p.m.):实际输入极少带点,反而增加失败率;真有需求再单独加分支 - 注意
0?[1-9]能匹配"1"到"9",但排除"00"—— 12 小时制没有 00 时
混合日期时间时,分隔符和空格要灵活但不放水
常见组合如 "2026-06-30 14:30"、"06/30/2026 9:15am",其中空格可能是普通空格、全角空格、甚至换行。硬编码一个 \s 不够,但用 .* 又太宽泛。
- 安全写法:
/^\d{4}[-\/]\d{1,2}[-\/]\d{1,2}\s+(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?$/(纯 24 小时制) - 若要兼容 12 小时制 + 日期,建议拆成两步:
preg_match先提取日期部分,再对时间子串单独验证,避免正则爆炸 - 分隔符用
[-\/]比[-/.]更干净:斜杠不需要转义,点号在字符组里是字面量,但容易误读 - 年份用
\d{4}足够,别写(19|20)\d{2}—— 2026 年之后的年份已超出该限制,且现代系统无需兼容 19xx 年老数据
真正容易被忽略的是边界逻辑和性能陷阱
正则本身快,但写错锚点或过度回溯会让 preg_match 在畸形输入上卡住。比如 "12:30:45:67" 这种超长秒字段,如果正则没写好,可能触发灾难性回溯。
立即学习“PHP免费学习笔记(深入)”;
- 所有时间正则都应以
^开头、$结尾,哪怕只是做提取 —— 否则preg_match_all会匹配到意外片段 - 避免嵌套量词,例如
(\d+)+或(.*?)+:PHP 的 PCRE 在某些版本下对这类模式响应极慢 - 秒字段用
(?::[0-5][0-9])?而非(:[0-5][0-9]){0,1}:前者更轻量,且不会因重复匹配引发回溯 - 如果业务允许,优先用
DateTime::createFromFormat()替代正则:它自动校验闰年、月末天数等,正则做不到



















