定义
$dateDiff版本 5.0 中的新增功能。
返回两个日期之间的差值。
The
$dateDiffexpression has this syntax:{ $dateDiff: { startDate: <Expression>, endDate: <Expression>, unit: <Expression>, timezone: <tzExpression>, startOfWeek: <String> } } $dateDiffcalculates the difference by counting the upper boundaries of time intervals crossed fromstartDatetoendDatewithin thetimezone. The time intervals length is equal to oneunitand are aligned so that their boundaries occur at integer multiples ofunitalong the time axis. It excludes partial units that do not cross a boundary. This expression returns an integer in the specifiedunit. IfendDateprecedesstartDate, it returns a negative integer.字段必需/可选说明startDate必需
endDate必需
unit必需
startDate和endDate之间的时间测量单位 (unit)。它是一个解析为字符串的表达式:yearquarterweekmonthdayhourminutesecondmillisecond
timezoneOptional
执行操作的时区。
<tzExpression>必须是一个有效的 表达式 ,解析为格式为 Olson 时区标识符 或 UTC 偏移 的 string:Olson 时区标识符:例如
"America/New_York"、"Europe/London"、"GMT"UTC 偏移:示例
"+04:45"、"-0530"、"+03"
如果您省略
timezone,则结果将在UTC中显示。startOfWeekOptional
当单位等于
week时使用。默认为Sunday。startOfWeek参数是一个解析为不区分大小写的字符串的表达式:monday(或mon)tuesday(或tue)wednesday(或wed)thursday(或thu)friday(或fri)saturday(或sat)sunday(或sun)
提示
行为
无小数单位
$dateDiff 表达式通过比较每个日期的特定部分返回整数。对于 year 单位,它会比较年份值:2022 中的任何日期与 2023 中的任何日期相差一个 year。对于 week 单位,它会比较周数:第 5 周中的任何日期与第 6 周中的任何日期相差一个 week。
$dateDiff counts the unit boundaries crossed between startDate and endDate. It does not measure elapsed duration. For example, $dateDiff reports a difference of one year between December 31, 2022 and January 1, 2023. A year boundary was crossed, even though a few hours passed between the two dates. See Year Boundary Crossing for a full example.
一周开始
除非被 startOfWeek 参数修改,否则 week 的起始为 Sunday。$dateDiff 会计算在指定日子所介于 startDate 和 endDate 之间的任何一周。周计数不受日历 month 或日历 year 限制。
时区
在 <timezone> 字段中使用 Olson 时区标识符时,如果适用于指定的时区,MongoDB 会应用 DST 偏移量。
例如,考虑包含以下文档的 sales 集合:
db.sales.insertOne( { "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:15:39.736Z") } )
以下聚合说明了 MongoDB 如何处理 Olson 时区标识符的 DST 偏移量。该示例使用 $hour 和 $minute 操作符返回 date 字段的相应部分:
db.sales.aggregate([ { $project: { "nycHour": { $hour: { date: "$date", timezone: "-05:00" } }, "nycMinute": { $minute: { date: "$date", timezone: "-05:00" } }, "gmtHour": { $hour: { date: "$date", timezone: "GMT" } }, "gmtMinute": { $minute: { date: "$date", timezone: "GMT" } }, "nycOlsonHour": { $hour: { date: "$date", timezone: "America/New_York" } }, "nycOlsonMinute": { $minute: { date: "$date", timezone: "America/New_York" } } } }])
操作返回以下结果:
{ "_id": 1, "nycHour" : 5, "nycMinute" : 24, "gmtHour" : 10, "gmtMinute" : 24, "nycOlsonHour" : 6, "nycOlsonMinute" : 24 }
其他详情
该算法使用公历计算日期差异。
考虑了闰年和夏令时,但不考虑闰秒。
返回的差值可以为负数。
示例
经过的时间
创建一个客户订单集合:
db.orders.insertMany( [ { custId: 456, purchased: ISODate("2020-12-31"), delivered: ISODate("2021-01-05") }, { custId: 457, purchased: ISODate("2021-02-28"), delivered: ISODate("2021-03-07") }, { custId: 458, purchased: ISODate("2021-02-16"), delivered: ISODate("2021-02-18") } ] )
如下示例:
返回平均交付天数。
使用
dateDiff计算purchased日期和delivered日期之间的差值。
db.orders.aggregate( [ { $group: { _id: null, averageTime: { $avg: { $dateDiff: { startDate: "$purchased", endDate: "$delivered", unit: "day" } } } } }, { $project: { _id: 0, numDays: { $trunc: [ "$averageTime", 1 ] } } } ] )
$group 阶段中的 $avg 累加器在每个文档上使用 $dateDiff 来获取 purchased 和 delivered 日期之间的时间。结果值作为 averageTime 返回。
averageTime 的十进制部分在 $project 阶段被截断 ($trunc) 以生成如下输出:
{ "numDays" : 4.6 }
结果精度
使用订阅的开始日期和结束日期创建此集合。
db.subscriptions.insertMany( [ { custId: 456, start: ISODate("2010-01-01"), end: ISODate("2011-01-01") }, { custId: 457, start: ISODate("2010-01-01"), end: ISODate("2011-06-31") }, { custId: 458, start: ISODate("2010-03-01"), end: ISODate("2010-04-30") } ] )
$dateDiff 表达式返回以整数 units 表示的时间差。单位没有小数部分。例如,在计算 years 时,没有半年。
在本例中,请注意更改 unit 对返回结果的精度产生的影响:
db.subscriptions.aggregate( [ { $project: { Start: "$start", End: "$end", years: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "year" } }, months: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "month" } }, days: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "day" } }, _id: 0 } } ] )
结果汇总如下表所示:
开始 | End | 年份 | 月数 | 天数 |
|---|---|---|---|---|
2010-01-01 | 2011-01-01 | 1 | 12 | 365 |
2010-01-01 | 2011-07-01 | 1 | 18 | 546 |
2010-03-01 | 2010-04-30 | 0 | 1 | 60 |
在第二行中,2010-01-01 和 2011-07-01 在不同的年份(2010 和 2011),因此 $dateDiff 返回 1 year。在第三行中,2010-03-01 (3 月)和 2010-04-30 (4 月)相差一个月,因此 $dateDiff 返回 1 month。
年度跨界
创建一个包含两个日期的集合,这两个日期仅相差几小时,但分别跨越了一个历年年度边界线:
db.events.insertOne( { start: ISODate("2022-12-31T20:00:00Z"), end: ISODate("2023-01-01T02:00:00Z") } )
将 $dateDiff 与 year 单位一起使用,以查找两个日期之间的差异:
db.events.aggregate( [ { $project: { _id: 0, yearsApart: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "year" } } } } ] )
操作返回以下结果:
{ "yearsApart" : 1 }
即使两个日期之间只过去了六小时,$dateDiff 也会返回 1,因为这些日期属于不同的公历年份。这反映了 $dateDiff 计算的是跨越的单位边界,而不是经过的持续时间。
每月周数
创建一个月份集合:
db.months.insertMany( [ { month: "January", start: ISODate("2021-01-01"), end: ISODate("2021-01-31") }, { month: "February", start: ISODate("2021-02-01"), end: ISODate("2021-02-28") }, { month: "March", start: ISODate("2021-03-01"), end: ISODate("2021-03-31") }, ] )
您可以使用以下代码更改每周的开始日期,并计算每个月的最终周数:
db.months.aggregate( [ { $project: { wks_default: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "week" } }, wks_monday: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "week", startOfWeek: "Monday" } }, wks_friday: { $dateDiff: { startDate: "$start", endDate: "$end", unit: "week", startOfWeek: "fri" } }, _id: 0 } } ] )
结果汇总如下表所示:
月 | 星期日 | 星期一 | 星期五 |
|---|---|---|---|
一月 | 5 | 4 | 4 |
二月 | 4 | 3 | 4 |
2022 年 | 4 | 4 | 4 |
从结果来看:
当
startOfWeek是星期日时,2021 年 1 月的第 5 个week从 31 号开始。由于 31 日是周日,且介于
startDate和endDate之间,因此计数时要加上一个week。即使日历周在
endDate之后或在下一个日历周期内结束,week计数也会增加。