最可靠的方式是直接使用 $currentdate 操作符,由 mongodb 服务端生成时间,避免客户端时钟偏差;插入时推荐用 upsert + $setoninsert 统一时间来源,聚合更新中则用 $$now 变量。

直接用 $currentDate 操作符,这是最可靠、最省心的方式。它由 MongoDB 服务端生成时间,避免客户端时钟偏差,且无需业务代码干预。
更新时用 $currentDate 设置 updatedAt
在 updateOne 或 updateMany 中直接写入,字段会自动设为服务端当前时间(ISODate):
db.orders.updateOne(
{ _id: ObjectId("...") },
{ $set: { status: "shipped" }, $currentDate: { updatedAt: true } }
)
-
updatedAt字段不存在也会被创建 - 设为
true表示存 ISODate 类型;若需 timestamp 类型,写成{ $type: "timestamp" } - 多个时间字段可同时设置:
$currentDate: { updatedAt: true, lastChecked: true }
插入时也要带 createdAt?别混用 new Date()
插入用 new Date() 是可行的,但和 $currentDate 不同源 —— 它依赖客户端时间,可能因本地时钟不准或时区错乱导致数据不一致:
db.orders.insertOne({
item: "laptop",
createdAt: new Date(), // ❌ 客户端时间,有风险
updatedAt: new Date()
})
- 插入和更新逻辑分离时,容易漏掉
updatedAt初始化 - 推荐统一用
$currentDate:插入时也走 update 操作(如upsert: true+$setOnInsert),保持时间来源一致 - 若必须插入即设时间,至少确保客户端时区为 UTC,并显式调用
new Date().toISOString()
聚合更新中怎么填当前时间?用 $$NOW
当使用聚合管道做更新(比如 updateOne 的 update 参数传 pipeline),不能用 $currentDate,得改用变量 $$NOW:
db.logs.updateOne(
{ _id: 123 },
[{ $set: { processedAt: "$$NOW", status: "done" } }]
)
-
$$NOW是聚合上下文中的常量,值为该 pipeline 执行时刻的服务端时间 - 它只在聚合更新中有效,普通 update 操作里写
$$NOW会报错undefined variable - 注意:
$$NOW返回的是 ISODate,不是字符串,无需额外转换
Go 或 Mongoose 场景下别自己手写时间逻辑
如果你用 Go(mongo-go-driver)或 Node.js(Mongoose),已有成熟方案替代手动赋值:
- Go 项目建议用
mongox:给 struct 字段加mongox:"autoUpdateTime"标签,插入/更新时自动填充 - Mongoose 推荐用 schema 中间件:
schema.pre('save', ...)和schema.pre('findOneAndUpdate', ...)统一处理,避免路由层散落时间赋值逻辑 - 自己在业务代码里反复写
doc.updatedAt = new Date()容易遗漏、难维护,且无法覆盖 bulk 操作或直连 shell 的场景
真正容易被忽略的点是:TTL 索引(expireAfterSeconds)只认 createdAt 这类字段的值,但它不关心你怎么设这个值 —— 如果你用客户端时间插入,而服务器时钟快 5 分钟,那文档可能提前过期。所以时间字段的“源头”比格式更重要。











