Mongo updateMany 语句 date.withZoneSameInstant(xxx)

Mongo updateMany statement with date.withZoneSameInstant(xxx)

我的 Mongo 数据库中有这个 collection:

id    | place | local time
--------------------------
3     | A     | 12pm
4     | A     | 11pm
5     | B     | 4pm
6     |       | 7pm

本地时间存储为具有 UTC 时区的 ISODates。当地时间 12 表示为 2022-01-01T12:00:00.000Z.

在数据库之外,我有一个地方和时区之间的部分完整映射。

place | timezone
A     | Europe/London
B     | Europe/Brussels

使用此信息,我想通过添加捕获时区并修复日期偏移量的新字段来使我的数据库行 timezone-aware。这将是我的理想结果:

id    | place | local time | newDate                         | timezone
----------------------------------------------------------------------------
3     | A     | 12pm       | ISODate("2022-07-01T11:00:00Z") | Europe/London
4     | A     | 11pm       | ISODate("2022-07-01T22:00:00Z") | Europe/London
5     | B     | 4pm        | ISODate("2022-07-01T14:00:00Z") | Europe/Brussels
6     |       | 7pm        | ISODate("2022-07-01T07:00:00Z") | UTC

添加时区列很简单,我遍历我的映射 table 并为每个映射构建一个单独的更新语句,如下所示:

db.testcollection.updateMany({ place: "A" }, { $set: { "timezone" : "Europe/London" }});

处理时区未知的位置(我们回退到只使用 UTC)也很简单:

db.testcollection.updateMany({ "timezone" : { $exists: false } }, { $set: { "timezone" : "UTC" }});

但我似乎无法做的是使用 Mongo 更新语句将时区更改本身一次应用于多行(具有相同位置)。我不确定是否可行。

我试过:

rs0 [direct: primary] test> db.testcollection.updateMany({ place: "A" }, { $set: { "utctime" : { $dateAdd: { startDate: "$localtime", amount: 1, unit: "hour" }}}});

可以将时间更改 adding/subtracting 一个小时左右,但实际上像我想要的那样应用时区更改对于 $dateAdd 功能来说太难了。 Europe/London 有 GMT/BST 取决于夏令时,某些年份有和放弃不同的历史值:https://en.wikipedia.org/wiki/Daylight_saving_time_by_country

即使是一系列基于时间的过滤子句也显得异常复杂。

我要应用的更新与Java功能基本相同:

correctUTC = localTimeMarkedAsUTC          // 9pm local time
    .withZoneSameLocal(portTimezone)       // 9pm Europe/London
    .withZoneSameInstant(UTC)              // 8pm UTC

我可以在更新函数中使用日期 object,但我仍在努力研究如何操纵 $dateToString 和朋友们生成我想要的输出:

test> db.testcollections.updateMany({ place: "A" }, [{ $set: { "utctime" : { $convert : { input: { $dateToString: { date: "$localtime", format: "%Y-%m-%dT%H:%M:%S%z", timezone: "UTC" }}, to: "date" }}}}]);

设置为已设置的相同内容。使用伦敦时区只会增加一个小时。我想要相反的结果,即从 UTC 变量中减去一个小时。

由于数据库的大小,我更喜欢一种解决方案,该解决方案可以批量更新整个位置,或者不使用 .forEach(function(e){ ... }) 来命中每条记录,除非那是最后可能的解决方案这可能有用吗?

我在 Mongo 5.0

你只需要用[]包裹它来创建一个管道:

db.collection.update({
  place: "A"
},
[{
  $set: {
    "utctime": {
      $dateAdd: {
        startDate: "$localtime",
        amount: 1,
        unit: "hour"
      }
    }
  }
}],
{
  multi: true
})

如您所见here。 当然可以进行批量操作

试试这个:

db.collection.updateMany(
  {timezone: {$ne: "UTC"} }, // UTC times do not need any correction
  [
    {
      $set: {
        newDate: {
          $dateFromString: {
            // "cut" wrong time zone
            dateString: {
              $dateToString: {
                date: "$newDate",
                format: "%Y-%m-%dT%H:%M:%S.%L"
              }
            },
            format: "%Y-%m-%dT%H:%M:%S.%L",
            timezone: "$timezone" // "attach" correct time zone
          }
        }
      }
    }
  ]
)

Mongo Playground