最稳妥方式是用 $gte 和 $lt 配合 isodate(),需注意时区与精度;字符串日期会被字典序比较导致错误;应以服务端时间为准,确保日期字段有索引。

直接用 $gte 和 $lt 配合 ISODate() 是最稳妥的方式,但必须注意时区和时间精度——否则查不到数据是常态,不是偶然。
为什么不能直接写字符串日期(如 "2026-05-10")
MongoDB 会把纯字符串(如 "2026-05-10")当作普通字符串比较,而不是日期。即使字段是 ISODate 类型,查询时仍会做字典序比对,结果完全不可靠。
- 错误写法:
{"createdAt": {"$gte": "2026-05-10"}}→ 匹配失败或范围错乱 - 正确写法:
{"createdAt": {"$gte": ISODate("2026-05-10T00:00:00.000Z")}} - 如果应用层用的是本地时区(如东八区),而数据库存的是 UTC,
ISODate("2026-05-10T00:00:00.000Z")实际对应北京时间 2026-05-10 08:00:00 —— 这就是“查不到昨天数据”的常见原因
计算过去 24 小时的起止时间(推荐用服务器时间)
客户端时间不可信,且存在时钟漂移;应以 MongoDB 服务端时间为基准,用 $$NOW 动态计算,避免硬编码。
- 起始时间 =
$$NOW减去 24 小时(86,400,000 毫秒) - 终止时间 =
$$NOW(不含,所以用$lt) - 聚合写法示例(统计数量):
db.collection.aggregate([ { "$match": { "$expr": { "$and": [ { "$gte": ["$createdAt", { "$subtract": ["$$NOW", 86400000] }] }, { "$lt": ["$createdAt", "$$NOW"] } ] } } }, { "$count": "total" } ]) - 注意:
$subtract返回毫秒数,$createdAt必须是日期类型,否则比较结果为null,整条文档被过滤掉
使用 $match + ISODate() 的实操要点
适用于简单计数、导出或应用层已知时间范围的场景,比聚合更轻量。
- 先算好本地时间对应的 UTC 时间:当前是 2026-05-11T13:35:00+08:00 → UTC 是 2026-05-11T05:35:00Z,往前推 24 小时得 2026-05-10T05:35:00Z
- 查询语句:
db.collection.find({ "createdAt": { "$gte": ISODate("2026-05-10T05:35:00.000Z"), "$lt": ISODate("2026-05-11T05:35:00.000Z") } }).count() - 务必检查字段名是否真叫
createdAt(常见别名有created_at、ts、insertedAt) - 如果字段可能为
null或字符串,加一层类型校验:{"createdAt": {"$type": "date"}}
容易被忽略的坑:索引与性能
没有索引时,24 小时查询可能扫全表,尤其在千万级集合上延迟飙升。
- 必须确保日期字段上有单字段升序索引:
db.collection.createIndex({"createdAt": 1}) - 如果同时按用户 ID 和时间查(如“某用户最近 24 小时操作”),建复合索引:
db.collection.createIndex({"userId": 1, "createdAt": -1}) -
$lt和$gte能走索引,但$where或含$expr的复杂条件不一定能充分利用索引,优先用静态范围











