直接用BIGINT存毫秒时间戳最准最简单,须存13位毫秒值(如1746221520123),建表用BIGINT UNSIGNED;差值直接减法计算得毫秒整数,避免TIMESTAMPDIFF损失精度,单位换算在应用层或SQL中按需拆解。

直接用 BigInt 存毫秒时间戳,计算差值最准、最简单——只要单位统一、不跨时区、不混淆秒/毫秒,结果就是原始高精度差值,毫秒级甚至微秒级都可推导。
确保字段是毫秒级 BigInt
数据库里必须存的是 13 位毫秒时间戳(如 1746221520123),不是 10 位秒级。常见错误是误用 UNIX_TIMESTAMP(NOW())(只返回秒),应改用:UNIX_TIMESTAMP(NOW(3)) * 1000 或 FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000)
建表时声明为 BIGINT,不加 UNSIGNED 也可,但推荐加,避免负值干扰。
差值计算:优先用数学减法,而非日期函数
两个毫秒时间戳相减,直接得到毫秒差,精度无损:
SELECT end_time_ms - start_time_ms AS duration_ms FROM events;- 结果是整数,单位毫秒,比如 128456 表示 128 秒 456 毫秒
- 想转成秒:除以 1000.0(保留小数);转分钟:再除以 60;转小时:再除以 60
- 避免套用
TIMESTAMPDIFF(SECOND, ...)处理毫秒字段——它会先截断或四舍五入,丢失毫秒精度
需要格式化展示时,再做单位换算与拆分
纯展示需求(如“2m8.456s”)可在应用层或 SQL 中拆解:
- 毫秒差
dur→ 秒部分:FLOOR(dur / 1000),毫秒余数:dur % 1000 - 再把总秒数转为时分秒:
SEC_TO_TIME(FLOOR(dur / 1000))得到H:i:s,然后拼接毫秒 - 示例:
CONCAT(SEC_TO_TIME(FLOOR((end_time_ms - start_time_ms)/1000)), '.', LPAD((end_time_ms - start_time_ms) % 1000, 3, '0'))
跨时区或需人类可读时间时,先转再算
如果原始 BigInt 是 UTC 毫秒戳,而你要按本地时区算“上午9点到下午5点”的业务时长,则:
- 先用
FROM_UNIXTIME(start_time_ms / 1000)转成 MySQL 的 datetime(自动按 server 时区解释) - 再用
TIMESTAMPDIFF计算,但注意:此时已损失毫秒精度(MySQL datetime 最高支持微秒,但函数常忽略) - 更稳妥做法:在应用层用
Instant或LocalDateTime转换,保持纳秒级运算能力

















