$toDate直接转换毫秒时间戳会失败,因为它仅接受10位秒级时间戳或ISO字符串,传入13位毫秒值时静默返回null;正确做法是先用$divide将毫秒字段除以1000转为秒,再传给$toDate,且需注意MongoDB版本兼容性及字段数据质量。

为什么 $toDate 直接转换毫秒时间戳会失败
因为 $toDate 仅接受「秒级」时间戳(10 位数字)或 ISO 字符串,传入毫秒级(13 位)数值时会静默返回 null,不报错但结果为空。常见于前端 Date.now() 或 Node.js new Date().getTime() 生成的时间戳。
用 $addFields + $toDate 正确转换毫秒时间戳的写法
必须先将毫秒值除以 1000 转为秒,再传给 $toDate。注意:除法要用 $divide,不能用 / 运算符(聚合阶段不支持中缀除法)。
{
$addFields: {
createdAtDate: { $toDate: { $divide: ["$createdAtMs", 1000] } }
}
}
-
$createdAtMs是文档中存的毫秒级数值字段(如1717023456789) - 结果
createdAtDate是 BSONDate类型,可直接用于$dateToString、范围查询等 - 如果字段可能为
null或非数字,建议加$cond判断,否则整个表达式会出错
容易忽略的兼容性问题:MongoDB 版本与驱动影响
$toDate 从 MongoDB 4.0 开始支持,但早期版本(如 4.0–4.2)对毫秒转秒的整除处理较敏感——若毫秒值不能被 1000 整除,部分旧驱动可能截断小数导致秒数偏差 1 秒。
- 稳妥做法:用
$floor显式取整,例如{ $toDate: { $floor: { $divide: ["$createdAtMs", 1000] } } } - MongoDB 5.0+ 已修复该问题,
$divide返回浮点数后$toDate自动向下取整到秒 - Node.js 驱动 4.0+、Python PyMongo 4.0+ 对此处理一致,老版本驱动建议升级
替代方案:不用 $toDate 的轻量级格式化(仅需字符串)
如果只是想输出「YYYY-MM-DD HH:mm:ss」这类格式,且不依赖日期类型操作,可用 $dateToString 配合 $multiply 反向构造(更安全,绕过 $toDate 限制):
{
$addFields: {
createdAtStr: {
$dateToString: {
format: "%Y-%m-%d %H:%M:%S",
date: { $multiply: ["$createdAtMs", 1] } // 强制转为数字,再由 $dateToString 内部处理
}
}
}
}
注意:$dateToString 在 4.0+ 中也支持直接传毫秒数值(自动识别),但行为不如显式除法稳定;5.0+ 文档已明确说明它接受毫秒时间戳,此时可省略 $divide 步骤。
实际使用时,优先检查你的 MongoDB 版本和字段数据质量——毫秒字段是否全为数字、有无异常大值(如 0 或负数)、是否混入字符串,这些都会让 $toDate 返回 null 而不是报错。











