文章目录
- 语法
- 使用
- 时间测量
- 时区
- 举例
- 添加未来日期
- 根据日期范围筛选
- 调整夏令时
$dateAdd
聚合运算符将Date()
对象按指定的时间单位递增。
语法
{$dateAdd: {startDate: <Expression>,unit: <Expression>,amount: <Expression>,timezone: <tzExpression>}
}
返回一个日期对象Date()
,startDate
可以是任何能被解析为日期、时间戳或对象Id的表达式,这三种类型都会返回Date()
对象。
参数字段说明:
|字段|是否必须|描述|
|-|-|
|startDate
|是|开始日期(UTC),可以是日期、时间戳或对象Id表达式|
|unit
|是|要增加的时间的单位,单位可以是能被解析为下列值的表达式:year
、quarter
、week
、month
、day
、hour
、minute
、second
、millisecond
|
|amount
|是|以units
为单位,在startDate
基础上的增量,amount
是可以被解析为整数、小数或双精度数的表达式|
|timezone
|否|执行操作的时区,<tzExpression>
必须是能被解析为奥尔森时区标识符格式的字符串或UTC偏移量,如果timezone
不指定,返回值显示为UTC
|
使用
时间测量
MongoDB遵循流行的数据库用法,以 UTC 为时间单位工作。dateAdd
表达式总是以 UTC 为起始日期,并以 UTC 为结果返回。如果指定了时区,计算将使用指定的时区进行。当计算涉及夏令时(DST)时,时区尤为重要。
如果单位是一个月或更大,操作会根据该月的最后一天进行调整。例如,在 10 月的最后一天增加一个月,这就是 "月末最后一天 "调整。
{$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
操作符返回日期字段的相应部分:
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
}
举例
添加未来日期
shipping
集合中包括了顾客订单的日期:
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
来设置3天后的预期交货日期:
db.shipping.aggregate([{$project:{expectedDeliveryDate:{$dateAdd:{startDate: "$purchaseDate",unit: "day",amount: 3}}}},{$merge: "shipping"}])
在$project
阶段用$dateAdd
将购买日期增加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
来比较实际交货日期和预期日期。交货日期比购买日期晚5天以上的文档会被转到$project
阶段。
$project
阶段使用$dateToString
表达式将日期转换为更易读的格式。如果不进行转换,MongoDB将以ISODate格式返回日期。
本例中只返回一条记录:
{ "custId" : 456, "purchased" : "2020-12-31", "delivery" : "2021-01-10" }
调整夏令时
所有日期在内部都以UTC时间存储。如果指定了时区,$dateAdd
将使用当地时间进行计算。计算结果以UTC
显示。
本例假如客户分布在多个时区,按天或按小时计费,现在要了解夏令时对计费期的影响。
创建billing
集合:
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
表达式对输出进行了重新格式化,以提高可读性。结果汇总如下:
字段 | 纽约 | 墨西哥城 |
---|---|---|
start | 2021-03-13 15:00 | 2021-03-13 10:00 |
Start, TZ Info | 2021-03-13 10:00 | 2021-03-13 04:00 |
1 Day | 2021-03-14 14:00 | 2021-03-14 10:00 |
1 Day, TZ Info | 2021-03-14 10:00 | 2021-03-14 04:00 |
24 Hours | 2021-03-14 15:00 | 2021-03-14 10:00 |
24 Hours, TZ Info | 2021-03-14 11:00 | 2021-03-14 04:00 |
上表强调了几点:
-
未格式化的日期以 UTC 返回。纽约的
$login
是 UTC -5,但开始、天数和小时行显示的时间是 UTC 时间。 -
3月14日是纽约的夏令时开始日,但不是墨西哥的夏令时开始日。当某地切换到DST并从某一天跨入下一天时,计算的时间会进行调整。
-
夏令时改变的是一天的长度,而不是小时。夏令时不会改变小时数。只有当测量单位为日或更大且计算跨越指定时区的时钟变化时,才会对 DST 进行调整。