MongoDB计算time-in与time-out时间差求和并转小时格式方案
MongoDB计算考勤打卡总时长实现方案
实现思路
- 展开嵌套的
record打卡数组,将每一条打卡记录作为独立文档处理 - 对所有打卡记录按时间升序排序,保证打卡顺序正确
- 按打卡日期分组,分别收集当天的所有上班(time-in)和下班(time-out)时间戳
- 按索引位置配对同组的上班和下班时间,计算单组打卡的时长差值
- 累加所有差值得到总毫秒数,最后转换为小时格式
具体聚合查询代码
db.你的集合名.aggregate([ // 第一步:展开record数组 { $unwind: "$record" }, // 第二步:提取打卡日期、时间戳、状态,方便后续处理 { $project: { date: { $dateToString: { format: "%Y-%m-%d", date: "$record.dateTime.$date" } }, timestamp: { $toLong: "$record.dateTime.$date" }, status: "$record.status" } }, // 第三步:按日期分组,分别收集上下班时间列表 { $group: { _id: "$date", timeIns: { $push: { $cond: [{ $eq: ["$status", "time-in"] }, "$timestamp", "$$REMOVE"] } }, timeOuts: { $push: { $cond: [{ $eq: ["$status", "time-out"] }, "$timestamp", "$$REMOVE"] } } } }, // 第四步:配对上下班时间,计算单日总时长(毫秒) { $project: { dayTotalMs: { $sum: { $map: { input: { $range: [0, { $size: "$timeIns" }] }, as: "idx", in: { $subtract: [ { $arrayElemAt: ["$timeOuts", "$$idx"] }, { $arrayElemAt: ["$timeIns", "$$idx"] } ] } } } } } }, // 第五步:累加所有日期的时长,转换为小时 { $group: { _id: null, totalMs: { $sum: "$dayTotalMs" } } }, { $project: { _id: 0, // 毫秒转小时:除以 1000*60*60 = 3600000 totalWorkHours: { $divide: ["$totalMs", 3600000] } } } ])
补充说明
- 如果业务允许同一天有多段打卡记录(比如上午、下午分别打卡),上述代码会自动按顺序配对计算
- 如果需要保留两位小数的小时格式,可以在最后一步的
$divide外包裹$round操作:totalWorkHours: { $round: [ { $divide: ["$totalMs", 3600000] }, 2 ] } - 请把代码里的
你的集合名替换为你实际的集合名称
内容的提问来源于stack exchange,提问作者Pinky Promise
相关产品推荐
相关产品推荐

