Docs 菜单

Docs 主页开发应用程序MongoDB Manual

$dateSubtract(聚合)

在此页面上

  • 定义
  • 行为
  • 举例
$dateSubtract

版本 5.0 中的新增功能

Date对象递减指定数量的时间单位。

$dateSubtract表达式采用以下语法:

{
$dateSubtract: {
startDate: <Expression>,
unit: <Expression>,
amount: <Expression>,
timezone: <tzExpression>
}
}

返回一个DatestartDate可以是解析为 Date、Timestamp 或 ObjectId 类型的任何表达式。 无论使用哪种数据类型作为输入,返回的值都将是Date对象。

字段
必需/可选
说明
startDate
必需
减法运算的开始日期(以 UTC 为单位)。startDate 可以是任何可解析为日期时间戳对象标识符表达式
unit
必需

unit 用来衡量从 startDate 中减去的 amount 时间。unit 是一个表达式,可解析为以下字符串之一:

  • year

  • quarter

  • week

  • month

  • day

  • hour

  • minute

  • second

  • millisecond

amount
必需
startDate中减去units的数量。amount 是解析为整数或长整数的表达式amount 也可以解析为十进制整数或双精度浮点数,前提是该值可以在不降低精度的情况下转换为长整数。
timezone
Optional

<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 时间。dateSubtract 表达式始终采用 UTC 格式的startDate 并返回 UTC 格式的结果。如果指定了 timezone,则将使用指定的 timezone 完成计算。当计算涉及夏令时 (DST) 时,时区尤其重要。

如果 unitmonth 或更大,则操作将根据该月的最后一天进行调整。例如,在三月的最后一天减去一个 month,即可演示“该月最后一天”调整。

{
$dateSubtract:
{
startDate: ISODate("2021-03-31T12:10:05Z"),
unit: "month",
amount: 1
}
}

请注意,返回的日期 ISODate("2021-02-28T12:10:05Z") 是 28 日而不是 31 日,因为二月的天数比三月的天数少。

<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.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 Date 对象中提取年份和月份。然后检查月份和年份,看看它们是否与选择目标匹配。由于 "January" 被编码为 "1",因此当年份和月份 $expr ($eq) 等于 "2021" 和 "1" 时,为 true。

$project 阶段使用 $dateSubtract 从每份选定文档的 logoutTime 中减去 3 小时。

最后,$merge 阶段更新集合,为修改后的文档编写新的 logoutTime

注意

$out 不同,$merge 阶段仅更新匹配的文档,而保留集合的其余部分。有关更多详细信息,请参见:$out 与 $merge 的比较。

生成的文档如下所示:

{
"_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 格式返回日期。输出显示上周有两位客户注销。

{ "custId" : 459, "loggedOut" : "2021-02-17" }
{ "custId" : 460, "loggedOut" : "2021-02-18" }

所有日期均以 UTC 时间存储在内部。指定 timezone 时,$dateSubtract 使用当地时间进行计算。结果以 UTC 时间显示。

您的客户分布在多个时区,您想了解如果您在 dayhour 之前计费,夏令时可能会对计费周期产生什么影响。

创建此连接时间的集合:

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")
}
]
)

首先从每个文档中的 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:
{
$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
开始,TZ Info
2021-03-14 11:00
2021-03-14 04:00
1 天
2021-03-13 16:00
2021-03-13 15:00
1 天,TZinfo
2021-03-13 11:00
2021-03-13 09:00
24 小时
2021-03-13 15:00
2021-03-13 15:00
24 小时,TZInfo
2021-03-13 10:00
2021-03-13 09:00

该图表突出显示以下几点:

  • 未格式化的日期以 UTC 格式返回。纽约的 $login 为 UTC -5,但startdayshours 行显示 UTC 时间。

  • 3 月 14 日是纽约而不是墨西哥的夏令时开始时间。当一个位置切换到夏令时并从一个 day 跨越到下一个时,计算出的时间会进行调整。

  • DST 修改 day 的长度,而不是 hour 的长度。hours 的 DST 没有变化。仅当测量值 unit 不低于 day 且计算跨越指定的 timezone 中的时钟变化时,才会对 DST 进行调整。

提示

另请参阅:

← $dateFromString(聚合)

在此页面上