MongoDB $ вычитает дату с продолжительностью и учитывает изменения летнего времени для данного часового пояса. - PullRequest
0 голосов
/ 01 апреля 2019

У нас есть особая потребность, когда нам нужно выполнить агрегацию по полям времени с учетом определенной «маржи» и обработки, пересекающей границы летнего времени.

Предположим, у нас есть проект, который начинается в момент времени starts_atи заканчивается в момент времени ends_at мы на самом деле хотим глобализировать все события, которые были созданы от starts_at - safety_margin до ends_at

Итак, у нас есть документы, которые выглядят так:

project: { "starts_at": "2019-04-01T10:28:05.711Z", "ends_at": "2019-01-29T10:28:05.711Z" }

А safety_margin на данный момент является константой в 1 неделю (переводится в миллисекунды в нашей агрегации MongoDB)

В нашей агрегации мы имеем следующую стадию

{ '$project': {
  safety_margin_starts_at: {
    '$subtract': ['$starts_at', safety_margin_duration]
  },
}

Это полностью завершится с DST с заданным контекстом:

  • Часовой пояс Франции (смещение +2 до 1 апреля, становится +1 после первого апреля)
  • Проект, который начинается впосле 1-го или апреля (таким образом, смещение +1 в FR) и с запасом прочности оно становится 25 марта (смещение +2)

в наших спецификациях это приведет к ошибке

project.starts_at               # => Mon, 01 Apr 2019 15:35:52 CEST +02:00
project.safety_margin_starts_at # => Mon, 25 Mar 2019 15:35:52 CET +01:00
# [Running our test]

expected 2019-03-25 13:35:52.357000000 +0000 to be within 0.1 of 2019-03-25 15:35:52 +0100
# The first figure is the one returned from the aggregation

Наше приложение соde фактически делает умное вычитание, где starts_at - safety_margin_duration становится тем же днем ​​/ тем же часом независимо от изменений летнего времени при вычитании дней.Можно ли воспроизвести это поведение в MongoDB?У вас есть несколько советов о том, как решить эти проблемы?

Может быть, это может быть решением, но я слышал о новом способе манипулирования временем в агрегации, может ли это решить мою проблему?

Ответы [ 2 ]

1 голос
/ 02 апреля 2019

Лучшая практика для обработки дат с часовыми поясами - выполнять всю математику в UTC.Это означает:

  • Если пользовательский ввод выполняется по местному времени, перед сохранением преобразуйте дату / время в UTC.
  • Выполните все операции в моменты времени UTC.
  • При рендеринге/ представление даты / времени пользователю, преобразование в любой часовой пояс, соответствующий времени (пользователь просматривает время или часовой пояс, в котором время было первоначально введено).

Существует такжеразница между следующими двумя операциями по местному (не UTC) времени:

  • Добавление 24 часов ко времени
  • Добавление 1 дня ко времени (время может измениться на 23,24 или 25 часов в зависимости от того, пересекается ли граница летнего времени)

Приложению должно быть понятно, какое из двух поведений оно хочет где.

Учитывая код, указанный в вопросе, если starts_at и safety_margin_starts_at являются полями модели, они должны хранить время в UTC.

В документации Mongoid есть дополнительные указания относительно часовых поясов: https://docs.mongodb.com/mongoid/master/tutorials/mongoid-configuration/#time-zones

0 голосов
/ 01 апреля 2019

В примере документа есть даты в формате UTC, у которых нет летнего времени, и вы можете безопасно вычесть миллис, как вы это сделали. Я полагаю, что-то в вашем коде вызывает ошибку.

ожидается 2019-03-25 13: 35: 52.357000000 +0000 будет в пределах 0,1 от 2019-03-25 15:35:52 + 0100

имеет разницу в 2 часа между 13:35 и 15:35, что выглядит неправильно.

Вот как вы получаете французское время в агрегации:

{ '$project': {
  safety_margin_starts_at: {
    '$subtract': ['$starts_at', safety_margin_duration]
  },
},
{ '$addFields': {
        safety_margin_starts_at_fr: { $dateToString: {
            date: "$d",
            format: "%Y-%m-%dT%H:%M:%S.%L%z",
            timezone: "Europe/Paris"
        } }    
} }
...