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 进行调整。

复制代码
相关推荐
知识分享小能手5 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019数据库的操作(2)
数据库·学习·sqlserver
踩坑小念6 小时前
秒杀场景下如何处理redis扣除状态不一致问题
数据库·redis·分布式·缓存·秒杀
萧曵 丶7 小时前
MySQL 语句书写顺序与执行顺序对比速记表
数据库·mysql
Wiktok7 小时前
MySQL的常用数据类型
数据库·mysql
曹牧7 小时前
Oracle 表闪回(Flashback Table)
数据库·oracle
J_liaty8 小时前
Redis 超详细入门教程:从零基础到实战精通
数据库·redis·缓存
m0_706653238 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
山岚的运维笔记8 小时前
SQL Server笔记 -- 第15章:INSERT INTO
java·数据库·笔记·sql·microsoft·sqlserver
Lw老王要学习9 小时前
CentOS 7.9达梦数据库安装全流程解析
linux·运维·数据库·centos·达梦
qq_423233909 小时前
Python深度学习入门:TensorFlow 2.0/Keras实战
jvm·数据库·python