MongoDB聚合运算符:$dateAdd

文章目录

$dateAdd聚合运算符将Date()对象按指定的时间单位递增。

语法

js 复制代码
{
   $dateAdd: {
      startDate: <Expression>,
      unit: <Expression>,
      amount: <Expression>,
      timezone: <tzExpression>
   }
}

返回一个日期对象Date()startDate可以是任何能被解析为日期、时间戳或对象Id的表达式,这三种类型都会返回Date()对象。

参数字段说明:

|字段|是否必须|描述|

|-|-|

|startDate|是|开始日期(UTC),可以是日期、时间戳或对象Id表达式|

|unit|是|要增加的时间的单位,单位可以是能被解析为下列值的表达式:yearquarterweekmonthdayhourminutesecondmillisecond|

|amount|是|以units为单位,在startDate基础上的增量,amount是可以被解析为整数、小数或双精度数的表达式|

|timezone|否|执行操作的时区,<tzExpression>必须是能被解析为奥尔森时区标识符格式的字符串或UTC偏移量,如果timezone不指定,返回值显示为UTC|

使用

时间测量

MongoDB遵循流行的数据库用法,以 UTC 为时间单位工作。dateAdd表达式总是以 UTC 为起始日期,并以 UTC 为结果返回。如果指定了时区,计算将使用指定的时区进行。当计算涉及夏令时(DST)时,时区尤为重要。

如果单位是一个月或更大,操作会根据该月的最后一天进行调整。例如,在 10 月的最后一天增加一个月,这就是 "月末最后一天 "调整。

js 复制代码
{
   $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集合:

json 复制代码
{
   "_id" : 1,
   "item" : "abc",
   "price" : 20,
   "quantity" : 5,
   "date" : ISODate("2017-05-20T10:24:51.303Z")
}

下面的聚合说明了 MongoDB 如何处理 Olson 时区标识符的 DST 偏移量。示例使用$hour$minute操作符返回日期字段的相应部分:

js 复制代码
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" }
       }
   }
}])

操作返回以下结果:

json 复制代码
{
   "_id": 1,
   "nycHour" : 5,
   "nycMinute" : 24,
   "gmtHour" : 10,
   "gmtMinute" : 24,
   "nycOlsonHour" : 6,
   "nycOlsonMinute" : 24
}

举例

添加未来日期

shipping集合中包括了顾客订单的日期:

js 复制代码
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天后的预期交货日期:

js 复制代码
db.shipping.aggregate(
   [
      {
         $project:
            {
               expectedDeliveryDate:
                  {
                     $dateAdd:
                        {
                           startDate: "$purchaseDate",
                           unit: "day",
                           amount: 3
                        }
                  }
            }
       },
       {
          $merge: "shipping"
       }
    ]
 )

$project阶段用$dateAdd将购买日期增加3天后,$merge阶段用 expectedDeliveryDate更新原始文档。

最后得到的文档如下:

json 复制代码
{
   "_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集合,在文档中添加交货日期:

js 复制代码
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 给定的时间段所定义的日期范围内的文档:

js 复制代码
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格式返回日期。

本例中只返回一条记录:

json 复制代码
{ "custId" : 456, "purchased" : "2020-12-31", "delivery" : "2021-01-10" }

调整夏令时

所有日期在内部都以UTC时间存储。如果指定了时区,$dateAdd将使用当地时间进行计算。计算结果以UTC显示。

本例假如客户分布在多个时区,按天或按小时计费,现在要了解夏令时对计费期的影响。

创建billing集合:

js 复制代码
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小时。

js 复制代码
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 进行调整。

复制代码
相关推荐
是小崔啊3 小时前
事务03之MVCC机制
数据库·mysql·事务·
LUCIAZZZ7 小时前
简单的SQL语句的快速复习
java·数据库·sql
Elastic 中国社区官方博客9 小时前
使用真实 Elasticsearch 进行高级集成测试
大数据·数据库·elasticsearch·搜索引擎·全文检索·jenkins·集成测试
@_@哆啦A梦9 小时前
Redis 基础命令
java·数据库·redis
fajianchen9 小时前
MySQL 索引存储结构
数据库·mysql
想做富婆9 小时前
oracle: 多表查询之联合查询[交集intersect, 并集union,差集minus]
数据库·oracle·联合查询
xianwu54311 小时前
反向代理模块jmh
开发语言·网络·数据库·c++·mysql
Leven19952711 小时前
Flink (十三) :Table API 与 DataStream API 的转换 (一)
数据库·sql·flink
geovindu11 小时前
neo4j-community-5.26.0 create new database
数据库·mysql·neo4j
因特麦克斯12 小时前
索引的底层数据结构、B+树的结构、为什么InnoDB使用B+树而不是B树呢
数据库