正则^d{10}(d{3})?$可精准匹配10位秒级或13位毫秒级时间戳,避免11/12位非法长度;但需配合数值校验防越界,并通过除1000后用date()校验合法性,不可依赖strtotime()。

用 ^d{10}(d{3})?$ 匹配秒级或毫秒级时间戳
这个正则能同时覆盖 10 位(秒)和 13 位(毫秒)两种常见格式,^ 和 $ 保证整串匹配,避免误判像 "abc1717023600000def" 这类混杂字符串。
常见错误现象:只写 ^d{10,13}$ —— 它会匹配 11 位、12 位等非法长度,比如 "12345678901"(11 位)会被放过,但实际不是标准时间戳。
-
d{10}强制开头必须是 10 位数字(对应 Unix 秒级起点) -
(d{3})?表示后面可选 3 位,不能是 1 位或 2 位,杜绝"171702360012"(12 位)这种中间态 - PHP 中使用时注意转义:双引号字符串里要写成
"^\d{10}(\d{3})?$"
为什么不能只靠正则判断时间戳有效性
正则只能验格式,不能防数值越界。比如 "0000000000000" 或 "9999999999999" 都能过 ^d{10}(d{3})?$,但前者是 1970 年初,后者远超当前时间(2026 年对应约 1780300000000)。
必须补一步数值校验:
- 先用
is_numeric()或ctype_digit()确保全是数字(避免科学计数法或负号) - 再用
(int) $str转为整型,注意 32 位系统上超过2147483647会溢出,建议用gmp_init()或直接走filter_var($str, FILTER_VALIDATE_INT) - 设定合理范围:下限建议 ≥
631152000(1990-01-01),上限 ≤(int) round(microtime(true) * 1000)(当前毫秒时间戳)
strtotime() 对毫秒时间戳完全无效
传 "1717023600000" 给 strtotime(),它不会除以 1000,而是当成年份“1717”去解析,结果是 1717-05-30 左右,彻底错乱。
所以别指望用 strtotime($str) !== false 来辅助验证毫秒值——它永远返回 false 或一个荒谬时间。
- 唯一安全做法:先用正则筛出疑似毫秒串 → 转整型 → 除以 1000 → 再丢给
date()或DateTime校验是否合法日期 - 例如:
date('Y', (int)round(1717023600000 / 1000)) === '2024'才算通过逻辑层 - 若需批量校验,封装函数时务必把除法、取整、时区设置全包进去,别漏掉
round()(用floor()会丢掉最多 0.999 毫秒)
MySQL 插入前别直接拼字符串
看到 "1717023600000" 就往 DATETIME(3) 字段里插?MySQL 会把它当“1717 年”处理,不是你想要的毫秒精度。
正确路径只有两条:
- 转成带小数点的秒级浮点数,用
FROM_UNIXTIME(1717023600.000)(注意小数点后三位) - 或存进
BIGINT字段,查询时手动除以 1000 做比较,但索引效率略低 - PHP 侧生成标准时间字符串更稳妥:
date('Y-m-d H:i:s.v', (int)round($ms / 1000))(v是 PHP 7.3+ 的毫秒格式符)
最易忽略的一点:毫秒部分不足三位时,v 不会自动补零,得用 str_pad($ms % 1000, 3, '0', STR_PAD_LEFT) 手动对齐,否则 MySQL 存的是 "2024-05-30 15:00:00.12" 而不是 "2024-05-30 15:00:00.012"。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











