对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

$dateDiff(表达式操作符)

$dateDiff

版本 5.0 中的新增功能。

返回两个日期之间的差值。

The $dateDiff expression has this syntax:

{
$dateDiff: {
startDate: <Expression>,
endDate: <Expression>,
unit: <Expression>,
timezone: <tzExpression>,
startOfWeek: <String>
}
}

$dateDiff calculates the difference by counting the upper boundaries of time intervals crossed from startDate to endDate within the timezone. The time intervals length is equal to one unit and are aligned so that their boundaries occur at integer multiples of unit along the time axis. It excludes partial units that do not cross a boundary. This expression returns an integer in the specified unit. If endDate precedes startDate, it returns a negative integer.

字段
必需/可选
说明

startDate

必需

时间段的开始。startDate 可以是任何可解析为日期、时间戳或对象 ID 的表达式。

endDate

必需

时间段的结束。endDate 可以是任何解析为日期、时间戳 或对象标识符的表达式。

unit

必需

startDate 和 endDate 之间的时间测量单位 (unit)。它是一个解析为字符串的表达式:

  • year

  • quarter

  • week

  • month

  • day

  • hour

  • minute

  • second

  • millisecond

timezone

Optional

执行操作的时区。<tzExpression> 必须是一个有效的 表达式 ,解析为格式为 Olson 时区标识符 或 UTC 偏移 的 string:

  • Olson 时区标识符:例如 "America/New_York"、"Europe/London"、"GMT"

  • UTC 偏移:示例 "+04:45"、"-0530"、"+03"

如果您省略 timezone,则结果将在 UTC 中显示。

startOfWeek

Optional

当单位等于 week 时使用。默认为 Sunday。startOfWeek 参数是一个解析为不区分大小写的字符串的表达式:

  • monday (或 mon)

  • tuesday (或 tue)

  • wednesday (或 wed)

  • thursday (或 thu)

  • friday (或 fri)

  • saturday (或 sat)

  • sunday (或 sun)

$dateDiff 表达式通过比较每个日期的特定部分返回整数。对于 year 单位,它会比较年份值:2022 中的任何日期与 2023 中的任何日期相差一个 year。对于 week 单位,它会比较周数:第 5 周中的任何日期与第 6 周中的任何日期相差一个 week。

$dateDiff counts the unit boundaries crossed between startDate and endDate. It does not measure elapsed duration. For example, $dateDiff reports a difference of one year between December 31, 2022 and January 1, 2023. A year boundary was crossed, even though a few hours passed between the two dates. See Year Boundary Crossing for a full example.

除非被 startOfWeek 参数修改,否则 week 的起始为 Sunday。$dateDiff 会计算在指定日子所介于 startDate 和 endDate 之间的任何一周。周计数不受日历 month 或日历 year 限制。

在 <timezone> 字段中使用 Olson 时区标识符时,如果适用于指定的时区,MongoDB 会应用 DST 偏移量。

例如,考虑包含以下文档的 sales 集合:

db.sales.insertOne(
{
"_id" : 1,
"item" : "abc",
"price" : 10,
"quantity" : 2,
"date" : ISODate("2014-01-01T08:15:39.736Z")
}
)

以下聚合说明了 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.orders.insertMany(
[
{
custId: 456,
purchased: ISODate("2020-12-31"),
delivered: ISODate("2021-01-05")
},
{
custId: 457,
purchased: ISODate("2021-02-28"),
delivered: ISODate("2021-03-07")
},
{
custId: 458,
purchased: ISODate("2021-02-16"),
delivered: ISODate("2021-02-18")
}
]
)

如下示例:

  • 返回平均交付天数。

  • 使用 dateDiff 计算 purchased 日期和 delivered 日期之间的差值。

db.orders.aggregate(
[
{
$group:
{
_id: null,
averageTime:
{
$avg:
{
$dateDiff:
{
startDate: "$purchased",
endDate: "$delivered",
unit: "day"
}
}
}
}
},
{
$project:
{
_id: 0,
numDays:
{
$trunc:
[ "$averageTime", 1 ]
}
}
}
]
)

$group 阶段中的 $avg 累加器在每个文档上使用 $dateDiff 来获取 purchased 和 delivered 日期之间的时间。结果值作为 averageTime 返回。

averageTime 的十进制部分在 $project 阶段被截断 ($trunc) 以生成如下输出:

{ "numDays" : 4.6 }

使用订阅的开始日期和结束日期创建此集合。

db.subscriptions.insertMany(
[
{
custId: 456,
start: ISODate("2010-01-01"),
end: ISODate("2011-01-01")
},
{
custId: 457,
start: ISODate("2010-01-01"),
end: ISODate("2011-06-31")
},
{
custId: 458,
start: ISODate("2010-03-01"),
end: ISODate("2010-04-30")
}
]
)

$dateDiff 表达式返回以整数 units 表示的时间差。单位没有小数部分。例如,在计算 years 时,没有半年。

在本例中,请注意更改 unit 对返回结果的精度产生的影响:

db.subscriptions.aggregate(
[
{
$project:
{
Start: "$start",
End: "$end",
years:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "year"
}
},
months:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "month"
}
},
days:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "day"
}
},
_id: 0
}
}
]
)

结果汇总如下表所示:

开始
End
年份
月数
天数

2010-01-01

2011-01-01

1

12

365

2010-01-01

2011-07-01

1

18

546

2010-03-01

2010-04-30

0

1

60

在第二行中,2010-01-01 和 2011-07-01 在不同的年份(2010 和 2011),因此 $dateDiff 返回 1 year。在第三行中,2010-03-01 (3 月)和 2010-04-30 (4 月)相差一个月,因此 $dateDiff 返回 1 month。

创建一个包含两个日期的集合,这两个日期仅相差几小时,但分别跨越了一个历年年度边界线:

db.events.insertOne(
{
start: ISODate("2022-12-31T20:00:00Z"),
end: ISODate("2023-01-01T02:00:00Z")
}
)

将 $dateDiff 与 year 单位一起使用,以查找两个日期之间的差异:

db.events.aggregate(
[
{
$project: {
_id: 0,
yearsApart: {
$dateDiff: {
startDate: "$start",
endDate: "$end",
unit: "year"
}
}
}
}
]
)

操作返回以下结果:

{ "yearsApart" : 1 }

即使两个日期之间只过去了六小时,$dateDiff 也会返回 1,因为这些日期属于不同的公历年份。这反映了 $dateDiff 计算的是跨越的单位边界,而不是经过的持续时间。

创建一个月份集合:

db.months.insertMany(
[
{
month: "January",
start: ISODate("2021-01-01"),
end: ISODate("2021-01-31")
},
{
month: "February",
start: ISODate("2021-02-01"),
end: ISODate("2021-02-28")
},
{
month: "March",
start: ISODate("2021-03-01"),
end: ISODate("2021-03-31")
},
]
)

您可以使用以下代码更改每周的开始日期,并计算每个月的最终周数:

db.months.aggregate(
[
{
$project:
{
wks_default:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "week"
}
},
wks_monday:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "week",
startOfWeek: "Monday"
}
},
wks_friday:
{
$dateDiff:
{
startDate: "$start",
endDate: "$end",
unit: "week",
startOfWeek: "fri"
}
},
_id: 0
}
}
]
)

结果汇总如下表所示:

月
星期日
星期一
星期五

一月

5

4

4

二月

4

3

4

2022 年

4

4

4

从结果来看:

  • 当 startOfWeek 是星期日时,2021 年 1 月的第 5 个 week 从 31 号开始。

  • 由于 31 日是周日,且介于 startDate 和 endDate 之间,因此计数时要加上一个 week。

  • 即使日历周在endDate之后或在下一个日历周期内结束, week计数也会增加。

给本页内容打分

在此页面上