$dateSubtract (聚合)
定义
$dateSubtract
新版本5.0.
减少一个
Date()
对象,通过指定的数量单位。表达式
$dateSubtract
表达式具有以下语法{ $dateSubtract: { startDate: <Expression>, unit: <Expression>, amount: <Expression>, timezone: <tzExpression> } } 返回一个
Date()
。startDate
可以是任何解析为日期、时间戳或ObjectId类型的表达式。无论使用哪种数据类型作为输入,返回的值都将是一个Date()
对象。字段必需/可选描述startDate
必需减法操作的起始日期,以UTC表示。startDate
可以是任何解析为日期、时间戳或ObjectId的表达式。unit
必需用于测量从
startDate
减去的时间量的单位。unit
是一个解析为以下字符串之一的表达式年
季度
周
月
日
小时
分钟
秒
毫秒
amount
必需时区
可选执行操作的时区。
<tzExpression>
必须是一个有效的表达式,其结果为格式化为Olson时区标识符的字符串或UTC偏移量。如果没有提供timezone
,则结果将显示为UTC
。格式示例Olson时区标识符"America/New_York" "Europe/London" "GMT" UTC偏移量+/-[hh]:[mm], e.g. "+04:45" +/-[hh][mm], e.g. "-0530" +/-[hh], e.g. "+03"
行为
时间测量
MongoDB遵循数据库的通用使用规则,以UTC时间处理时间。`dateSubtract`表达式始终以UTC的`startDate`作为参数,并以UTC返回结果。如果指定了`timezone`,则将使用指定的`timezone`进行计算。时区在涉及夏令时(DST)的计算中尤为重要。
如果`unit`是一个`month`或更大,则操作会调整到最后一天。例如,在三月的最后一天减去一个月,就显示了“月底调整”。
{ $dateSubtract: { startDate: ISODate("2021-03-31T12:10:05Z"), unit: "month", amount: 1 } }
请注意,返回的日期`ISODate("2021-02-28T12:10:05Z")`是28日而不是31日,因为2月的天数少于3月。
时区
当在`
例如,考虑一个包含以下文档的`sales`集合
{ "_id" : 1, "item" : "abc", "price" : 20, "quantity" : 5, "date" : ISODate("2017-05-20T10:24:51.303Z") }
以下聚合演示了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.connectionTime.insertMany( [ { custId: 457, login: ISODate("2020-12-25T19:04:00"), logout: ISODate("2020-12-28T09:04:00") }, { custId: 457, login: ISODate("2021-01-27T05:12:00"), logout: ISODate("2021-01-28T13:05:00") }, { custId: 458, login: ISODate("2021-01-22T06:27:00"), logout: ISODate("2021-01-31T11:00:00") }, { custId: 459, login: ISODate("2021-02-14T20:14:00"), logout: ISODate("2021-02-17T16:05:00") }, { custId: 460, login: ISODate("2021-02-26T02:44:00"), logout: ISODate("2021-02-18T14:13:00") } ] )
由于服务问题,您需要从2021年1月的每个登出时间中减去3小时。您可以在聚合管道中使用$dateSubtract
来递减logoutTime
。
db.connectionTime.aggregate( [ { $match: { $expr: { $eq: [ { $year: "$logout" }, 2021 ] }, $expr: { $eq: [ { $month: "$logout" }, 1 ] } } }, { $project: { logoutTime: { $dateSubtract: { startDate: "$logout", unit: "hour", amount: 3 } } } }, { $merge: "connectionTime" } ] )
在$match
阶段进行了两个类似的比较。首先,$year
和$month
运算符分别从logoutTime
日期对象中提取年份和月份。然后检查月份和年份是否与选择目标匹配。由于“January”编码为“1”,当年份和月份等于“2021”和“1”时,$expr
为真($eq
)。
$project
阶段使用$dateSubtract
从每个选定文档的logoutTime
中减去3小时。
最后,$merge
阶段更新集合,为修改后的文档写入新的 logoutTime
。
注意
与 $out
不同,$merge
阶段仅更新匹配的文档,并保留集合的其余部分。更多详情请见:[与 $merge 对比 $out](#std-label-out-merge-comparison)。
结果文档如下所示
{ "_id" : ObjectId("603dd94b044b995ad331c0b5"), "custId" : 457, "login" : ISODate("2020-12-25T19:04:00Z"), "logout" : ISODate("2020-12-28T09:04:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b6"), "custId" : 457, "login" : ISODate("2021-01-27T05:12:00Z"), "logout" : ISODate("2021-01-28T13:05:00Z"), "logoutTime" : ISODate("2021-01-28T10:05:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b7"), "custId" : 458, "login" : ISODate("2021-01-22T06:27:00Z"), "logout" : ISODate("2021-01-31T11:00:00Z"), "logoutTime" : ISODate("2021-01-31T08:00:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b8"), "custId" : 459, "login" : ISODate("2021-02-14T20:14:00Z"), "logout" : ISODate("2021-02-17T16:05:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b9"), "custId" : 460, "login" : ISODate("2021-02-26T02:44:00Z"), "logout" : ISODate("2021-02-18T14:13:00Z") }
按相对日期筛选
您想向在过去一周内使用过您服务的客户发送调查问卷。使用 $dateSubtract
表达式可以创建一个相对于查询执行时间的范围筛选。
db.connectionTime.aggregate( [ { $match: { $expr: { $gt: [ "$logoutTime", { $dateSubtract: { startDate: "$$NOW", unit: "week", amount: 1 } } ] } } }, { $project: { _id: 0, custId: 1, loggedOut: { $dateToString: { format: "%Y-%m-%d", date: "$logoutTime" } } } } ] )
内置的聚合变量 $$NOW
返回当前日期时间,格式为 ISODate。在 $match
阶段中,使用 $$NOW
的值来获取今天的日期。然后,使用比较表达式 ($expr
) 通过大于 ($gt
) 和 $dateSubtract
来筛选集合,以匹配在过去一周内有 logoutTime
的文档。
$project
阶段使用 $dateToString
表达式将日期转换为更易读的格式。如果不进行转换,MongoDB 会以 ISODate 格式返回日期,并假设时区为 UTC。
输出显示过去一周内有两位客户已登出。
{ "custId" : 459, "loggedOut" : "2021-02-17" } { "custId" : 460, "loggedOut" : "2021-02-18" }
调整夏令时
所有日期都存储为UTC时间。当指定了时区
时,$dateSubtract
使用本地时间进行计算。结果以UTC显示。
您有来自几个时区的客户,并想查看如果按天
或按小时
计费,夏令时可能会对您的计费周期产生什么影响。
创建此连接时间集合
db.billing.insertMany( [ { location: "America/New_York", login: ISODate("2021-03-14T10:00:00-0500"), logout: ISODate("2021-03-14T18:00:00-0500") }, { location: "America/Mexico_City", login: ISODate("2021-03-14T10:00:00-00:00"), logout: ISODate("2021-03-15T08:00:00-0500") } ] )
首先从每个文档中的登录
日期中减去1天,然后减去24小时。
db.billing.aggregate( [ { $project: { _id: 0, location: 1, start: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$login" } }, days: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } } } }, hours: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { startDate: "$login", unit: "hour", amount: 24, timezone: "$location" } } } }, startTZInfo: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$login", timezone: "$location" } }, daysTZInfo: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } }, timezone: "$location" } }, hoursTZInfo: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { startDate: "$login", unit: "hour", amount: 24, timezone: "$location" } }, timezone: "$location" } }, } } ] ).pretty()
$dateToString
表达式重新格式化输出以提高可读性。结果在此总结
字段 | 纽约 | 墨西哥城 |
---|---|---|
开始 | 2021-03-14 15:00 | 2021-03-14 15:00 |
开始,时区信息 | 2021-03-14 11:00 | 2021-03-14 04:00 |
1天 | 2021-03-13 16:00 | 2021-03-13 15:00 |
1天,时区信息 | 2021-03-13 11:00 | 2021-03-13 09:00 |
24小时 | 2021-03-13 15:00 | 2021-03-13 15:00 |
24小时,时区信息 | 2021-03-13 10:00 | 2021-03-13 09:00 |
图表突出了几个重点
未格式化的日期以UTC返回。纽约的
$login
是UTC -5,但是开始
、天数
和小时数
行显示的是UTC时间。3月14日是纽约夏令时的开始,但不是墨西哥。当位置切换到夏令时并从一天跨越到下一天时,计算时间会进行调整。
夏令时改变的是
天
的长度,而不是小时
。没有针对小时
的夏令时更改。只有当测量单位
是天
或更大,并且计算跨越指定时区
的时钟变化时,才会调整夏令时。