$dateAdd(聚合)
定义
$dateAdd
版本 5.0 中的新增功能。
将
Date()
对象递增指定数量的时间单位。$dateAdd
表达式的语法如下:{ $dateAdd: { startDate: <Expression>, unit: <Expression>, amount: <Expression>, timezone: <tzExpression> } } 返回
Date()
。startDate
可以是解析为 Date、Timestamp 或 ObjectId 类型的任何表达式。无论使用哪种数据类型作为输入,返回的值都将是Date()
对象。字段必需/可选说明startDate
必需unit
必需用于测量添加到
startDate
中的时间amount
的unit
。unit
是一个表达式,可解析为以下字符串之一:year
quarter
week
month
day
hour
minute
second
millisecond
amount
必需timezone
Optional执行操作的时区。
<tzExpression>
必须是有效 表达式 string,可解析为格式为 Olson 时区标识符 的 或 UTC 偏移量 。如果未提供timezone
,则结果将显示在UTC
中。format示例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 时间。dateAdd
表达式始终采用 UTC 格式的startDate
并返回 UTC 格式的结果。如果指定了 timezone
,则将使用指定的 timezone
完成计算。当计算涉及夏令时 (DST) 时,时区尤其重要。
如果 unit
为 month
,则操作将调整以考虑该月的最后一天。例如,在 10 月的最后一天添加一个 month
,结果演示了“月末最后一天”调整。
{ $dateAdd: { startDate: ISODate("2020-10-31T12:10:05Z"), unit: "month", amount: 1 } }
请注意,返回的日期 ISODate("2020-11-30T12:10:05Z")
是 30 日,而不是 31 日,因为 11 月的天数比 10 月少。
时区
在 <timezone>
字段中使用 Olson 时区标识符时,如果适用于指定的时区,MongoDB 会应用 DST 偏移量。
例如,考虑包含以下文档的 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.shipping.insertMany( [ { custId: 456, purchaseDate: ISODate("2020-12-31") }, { custId: 457, purchaseDate: ISODate("2021-02-28") }, { custId: 458, purchaseDate: ISODate("2021-02-26") } ] )
正常发货时间为 3 天。您可以在聚合管道中使用 $dateAdd
设置 expectedDeliveryDate
为未来 3 天。
db.shipping.aggregate( [ { $project: { expectedDeliveryDate: { $dateAdd: { startDate: "$purchaseDate", unit: "day", amount: 3 } } } }, { $merge: "shipping" } ] )
在 $project
阶段用 $dateAdd
给 purchaseDate
加了 3 天后,$merge
阶段用 expectedDeliveryDate
更新原始文档。
生成的文档如下所示:
{ "_id" : ObjectId("603dd4b2044b995ad331c0b2"), "custId" : 456, "purchaseDate" : ISODate("2020-12-31T00:00:00Z"), "expectedDeliveryDate" : ISODate("2021-01-03T00:00:00Z") } { "_id" : ObjectId("603dd4b2044b995ad331c0b3"), "custId" : 457, "purchaseDate" : ISODate("2021-02-28T00:00:00Z"), "expectedDeliveryDate" : ISODate("2021-03-03T00:00:00Z") } { "_id" : ObjectId("603dd4b2044b995ad331c0b4"), "custId" : 458, "purchaseDate" : ISODate("2021-02-26T00:00:00Z"), "expectedDeliveryDate" : ISODate("2021-03-01T00:00:00Z") }
按日期范围筛选
使用以下代码更新上一个示例中的 shipping
集合,以将交付日期添加到文档中:
db.shipping.updateOne( { custId: 456 }, { $set: { deliveryDate: ISODate( "2021-01-10" ) } } ) db.shipping.updateOne( { custId: 457 }, { $set: { deliveryDate: ISODate( "2021-03-01" ) } } ) db.shipping.updateOne( { custId: 458 }, { $set: { deliveryDate: ISODate( "2021-03-02" ) } } )
您想要找到延迟发货的订单。在 $match
阶段使用 $dateAdd
来创建筛选器,以匹配在由起始点 ($purchaseDate
) 和由$dateAdd
给定的时间段共同确定的日期范围内的文档。
db.shipping.aggregate( [ { $match: { $expr: { $gt: [ "$deliveryDate", { $dateAdd: { startDate: "$purchaseDate", unit: "day", amount: 5 } } ] } } }, { $project: { _id: 0, custId: 1, purchased: { $dateToString: { format: "%Y-%m-%d", date: "$purchaseDate" } }, delivery: { $dateToString: { format: "%Y-%m-%d", date: "$deliveryDate" } } } } ] )
$match
阶段在表达式 ($expr
) 中使用 $gt
和 $dateAdd
将实际 deliveryDate
与预期日期相比较。交付日期超过 purchaseDate
后 5 天的文档将被传送到 $project
阶段。
$project
阶段使用 $dateToString
表达式将日期转换为更可读的格式。如果不转换,MongoDB 将以 ISODate 格式返回日期。
在此示例中,仅返回一条记录:
{ "custId" : 456, "purchased" : "2020-12-31", "delivery" : "2021-01-10" }
调整夏令时
所有日期均以 UTC 时间存储在内部。指定 timezone
时,$dateAdd
使用当地时间进行计算。结果以 UTC 时间显示。
您的客户分布在多个时区,您想了解如果您在 day
或 hour
之前计费,夏令时可能会对计费周期产生什么影响。
创建此连接时间的集合:
db.billing.insertMany( [ { location: "America/New_York", login: ISODate("2021-03-13T10:00:00-0500"), logout: ISODate("2021-03-14T18:00:00-0500") }, { location: "America/Mexico_City", login: ISODate("2021-03-13T10:00:00-00:00"), logout: ISODate("2021-03-14T08:00:00-0500") } ] )
首先向每个文档中的 login
日期添加 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: { $dateAdd: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } } } }, hours: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateAdd: { 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: { $dateAdd: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } }, timezone: "$location" } }, hoursTZInfo: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateAdd: { startDate: "$login", unit: "hour", amount: 24, timezone: "$location" } }, timezone: "$location" } }, } } ] ).pretty()
$dateToString
表达式将输出重新格式化,以提高可读性。现将结果汇总如下:
字段 | New York | 墨西哥城 |
---|---|---|
开始 | 2021-03-13 15:00 | 2021-03-13 10:00 |
开始,TZ Info | 2021-03-13 10:00 | 2021-03-13 04:00 |
1 天 | 2021-03-14 14:00 | 2021-03-14 10:00 |
1 天,TZ Info | 2021-03-14 10:00 | 2021-03-14 04:00 |
24 小时 | 2021-03-14 15:00 | 2021-03-14 10:00 |
24 小时,TZ Info | 2021-03-14 11:00 | 2021-03-14 04:00 |
该图表突出显示以下几点:
未格式化的日期以 UTC 格式返回。纽约的
$login
为 UTC -5,但start
、days
和hours
行显示 UTC 时间。3 月 14 日是纽约而不是墨西哥的夏令时开始时间。当一个位置切换到夏令时并从一个
day
跨越到下一个时,计算出的时间会进行调整。DST 修改
day
的长度,而不是hour
的长度。hours
的 DST 没有变化。仅当测量值unit
不低于day
且计算跨越指定的timezone
中的时钟变化时,才会对 DST 进行调整。