文章目录
$dateDiff
聚合运算符返回两个日期只差。
语法
js
{
$dateDiff: {
startDate: <Expression>,
endDate: <Expression>,
unit: <Expression>,
timezone: <tzExpression>,
startOfWeek: <String>
}
}
结束日期endDate
减去开始日期startDate
,返回指定时间单位unit
的整数。
参数字段说明:
|字段|是否必须|描述|
|-|-|
|startDate
|是|开始时间,可以是任何合法的日期、时间戳或ObjectID表达式|
|startDate
|是|结束时间,可以是任何合法的日期、时间戳或ObjectID表达式|
|unit
|是|时间单位,可以是结果为year
、quarter
、week
、month
、day
、hour
、minute
、second
、millisecond
的表达式|
|timezone
|否|执行操作的时区,<tzExpression>
必须是能被解析为奥尔森时区标识符格式的字符串或UTC偏移量,如果timezone
不指定,返回值显示为UTC
|
|startOfWeek
|否|当单位unit
为week
时使用,默认为星期日,可以是结果为monday (or mon)
、tuesday (or tue)
、wednesday (or wed)
、thursday (or thu)
、friday (or fri)
、saturday (or sat)
、sunday (or sun)
之一的字符串表达式|
使用
没有小数单位
$dateDiff
表达式返回以指定单位计算的开始日期和结束日期之间的整数差,持续时间是通过计算通过单位边界的次数来确定的,例如,相差 18 个月的两个日期将返回1年的差值,而不是1.5年。
周的开始
除非通过参数startOfWeek
指定,否则一周的开始日期为周日。在指定日期的开始日期和结束日期之间开始的任何一周都会被计算在内。周计数不受日历月或日历年的限制。
时区
在<timezone>
字段中使用 Olson 时区标识符时,MongoDB 会应用 DST 偏移(如果适用于指定的时区)。
例如,包含以下文件的sales
集合:
json
{
"_id" : 1,
"item" : "abc",
"price" : 20,
"quantity" : 5,
"date" : ISODate("2017-05-20T10:24:51.303Z")
}
下面的聚合说明了 MongoDB 如何处理 Olson 时区标识符的 DST 偏移量。示例使用$hour
和$minute
操作符返回日期字段的相应部分:
js
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" }
}
}
}])
操作返回以下结果:
json
{
"_id": 1,
"nycHour" : 5,
"nycMinute" : 24,
"gmtHour" : 10,
"gmtMinute" : 24,
"nycOlsonHour" : 6,
"nycOlsonMinute" : 24
}
其它细节
该算法使用公历计算日期差异。闰年和夏令时计算在内,但不包括闰秒。返回的差值可以是负数。
举例
持续时间
创建一个消费订单集合:
js
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
计算购买日期和交货日期之间的天数
js
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
来获取购买日期和交付日期之间的时间。得出的值将作为averageTime
返回。
平均时间的小数部分会在$project
阶段被截断 ($trunc
),从而产生类似这样的输出结果:
json
{ "numDays" : 4.6 }
结果精度
创建subscriptions
集合,其中包含订阅的开始日期和结束日期:
js
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
表达式返回以整数单位表示的时间差,单位中没有小数部分。例如,以年为单位计算时,就没有半年。
在本例中,请注意单位的改变对返回精度的影响:
js
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
}
}
]
)
结果汇总如下表所示:
Start | End | Years | Months | Days |
---|---|---|---|---|
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 |
计数只在新单位开始时递增,因此第二行中的18个月为1年,第三行中的60天为1个月。
每月周数
创建一个months
集合:
js
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")
},
]
)
可以使用以下代码更改每周的开始时间,并计算出每月的周数:
js
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
}
}
]
)
下表对结果进行了汇总:
Month | Sunday | Monday | Friday |
---|---|---|---|
January | 5 | 4 | 4 |
February | 4 | 3 | 4 |
March | 4 | 4 | 4 |
从结果来看:
- 当 "周初 "是周日时,2021年1月的第5周从31日开始。
- 由于31日是周日,且位于开始日期和结束日期之间,因此计数会增加一周。
- 即使日历周在结束日期之后或在下一个日历期间结束,周计数也会递增。