• MongoDB聚合运算符:$dateAdd


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

    语法

    {
       $dateAdd: {
          startDate: <Expression>,
          unit: <Expression>,
          amount: <Expression>,
          timezone: <tzExpression>
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    参数字段说明:

    |字段|是否必须|描述|
    |-|-|
    |startDate|是|开始日期(UTC),可以是日期、时间戳或对象Id表达式|
    |unit|是|要增加的时间的单位,单位可以是能被解析为下列值的表达式:yearquarterweekmonthdayhourminutesecondmillisecond|
    |amount|是|以units为单位,在startDate基础上的增量,amount是可以被解析为整数、小数或双精度数的表达式|
    |timezone|否|执行操作的时区,必须是能被解析为奥尔森时区标识符格式的字符串或UTC偏移量,如果timezone不指定,返回值显示为UTC|

    使用

    时间测量

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

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

    {
       $dateAdd:
          {
             startDate: ISODate("2020-10-31T12:10:05Z"),
             unit: "month",
             amount: 1
          }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:返回的日期ISODate("2020-11-30T12:10:05Z")是30日,而不是 31日,因为11月的天数比10月少。

    时区

    字段中使用 Olson 时区标识符时,MongoDB 会应用 DST 偏移(如果适用于指定的时区)。

    例如,包含以下文件的sales集合:

    {
       "_id" : 1,
       "item" : "abc",
       "price" : 20,
       "quantity" : 5,
       "date" : ISODate("2017-05-20T10:24:51.303Z")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    下面的聚合说明了 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" }
           }
       }
    }])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    操作返回以下结果:

    {
       "_id": 1,
       "nycHour" : 5,
       "nycMinute" : 24,
       "gmtHour" : 10,
       "gmtMinute" : 24,
       "nycOlsonHour" : 6,
       "nycOlsonMinute" : 24
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    举例

    添加未来日期

    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") }
      ]
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    假设正常发货时间为3天。您以在聚合管道中使用$dateAdd来设置3天后的预期交货日期:

    db.shipping.aggregate(
       [
          {
             $project:
                {
                   expectedDeliveryDate:
                      {
                         $dateAdd:
                            {
                               startDate: "$purchaseDate",
                               unit: "day",
                               amount: 3
                            }
                      }
                }
           },
           {
              $merge: "shipping"
           }
        ]
     )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    $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")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    根据日期范围筛选

    使用下面的代码更新上一个示例中的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" ) } }
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    下面的聚合查找延迟发货的文档,在$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"
                             }
                       }
                 }
           }
       ]
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    $match阶段使用表达式($expr)中的$gt$dateAdd来比较实际交货日期和预期日期。交货日期比购买日期晚5天以上的文档会被转到$project阶段。

    $project阶段使用$dateToString表达式将日期转换为更易读的格式。如果不进行转换,MongoDB将以ISODate格式返回日期。

    本例中只返回一条记录:

    { "custId" : 456, "purchased" : "2020-12-31", "delivery" : "2021-01-10" }
    
    • 1

    调整夏令时

    所有日期在内部都以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")
          }
       ]
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    首先,在每个文档中的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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

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

    • 相关阅读:
      取余和取模和取整
      HTTP请求:GET/POST请求
      JWT定义、生成规则及认证
      “我放弃编程,写了一本130万字的硬科幻小说”
      智慧社区解决方案
      【计算机组成原理】读书笔记第二期:使用有棱有角的内存
      01的token的年度总结
      推出全新分布式计算接口,OneFlow v0.7.0发布,LiBai代码库、Serving、MLIR一应俱全...
      如何用java股票量化交易接口读取股票数据?
      消息队列十连问
    • 原文地址:https://blog.csdn.net/superatom01/article/details/136441116