定义
$dateAdd版本 5.0 中的新增功能。
将日期对象递增指定数量的时间单位。
$dateAdd表达式的语法如下:{ $dateAdd: { startDate: <Expression>, unit: <Expression>, amount: <Expression>, timezone: <tzExpression> } } 返回一个Date 。
startDate可以是解析为 Date、Timestamp 或 ObjectId 类型的任何表达式。 无论使用哪种数据类型作为输入,返回的值都将是Date对象。字段必需/可选说明startDate必需
unit必需
用于测量添加到
startDate中的时间amount的unit。unit是一个表达式,可解析为以下字符串之一:yearquarterweekmonthdayhourminutesecondmillisecond
amount必需
添加到
startDate中的units个数。amount是解析为整数或长整数的表达式。amount也可以解析为十进制整数或 double,前提是该值可以在不降低精度的情况下转换为长整数。timezoneOptional
执行操作的时区。
<tzExpression>必须是有效表达式,可解析为格式为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" 有关表达式和类型的更多信息,请参阅表达式和BSON types。
行为
时间测量
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 进行调整。