$dateSubtract (agregação)
Nesta página
Definição
$dateSubtract
Novidades na versão 5.0.
Reduz um objeto dedata por um número específico de unidades de tempo.
A expressão
$dateSubtract
tem a seguinte sintaxe:{ $dateSubtract: { startDate: <Expression>, unit: <Expression>, amount: <Expression>, timezone: <tzExpression> } } Retorna uma data. O
startDate
pode ser qualquer expressão que resolva o tipo Date, Timestamp ou ObjectId. Não importa qual tipo de dados é usado como entrada, o valor retornado será um objeto de data .CampoObrigatório/OpcionalDescriçãostartDate
ObrigatórioA data de início, em UTC, para a operação de subtração. OstartDate
pode ser qualquer expressão que resolva para uma Data, um Timestamp ou um ObjectID.unit
ObrigatórioO
unit
usado para medir oamount
de tempo subtraído dostartDate
. Ounit
é uma expressão que se resolve em uma das seguintes strings:year
quarter
week
month
day
hour
minute
second
millisecond
amount
ObrigatórioO número deunits
subtraído destartDate
. Oamount
é uma expressão que resolve para um inteiro ou longo. Oamount
também pode resolver para um decimal integral e ou um double se esse valor puder ser convertido em um longo sem perda de precisão.timezone
OpcionalO fuso horário para realizar a operação.
<tzExpression>
deve ser uma expressão válida que resolva para uma string formatada como um identificador de fuso horário Olson ou um UTC Offset. Se nenhumtimezone
for fornecido, o resultado será exibido emUTC
.FormatarExemplosIdentificador de fuso horário Olson"America/New_York" "Europe/London" "GMT" UTC Offset+/-[hh]:[mm], e.g. "+04:45" +/-[hh][mm], e.g. "-0530" +/-[hh], e.g. "+03" Para mais informações sobre expressão e BSON types, consulte expressão e BSON types.
Comportamento
Medição de tempo
O MongoDB segue o uso predominante do banco de dados e trabalha com o tempo em UTC. A expressão dateSubtract
sempre pega um startDate
em UTC e retorna um resultado em UTC. Se o timezone
for especificado, o cálculo será feito utilizando o timezone
especificado. O fuso horário é especialmente importante quando um cálculo envolve Horário de Verão (DST).
Se unit
for month
ou maior, a operação será ajustada para contabilizar o último dia do mês. Subtraindo um month
no último dia de março, por exemplo, demonstra o ajuste de "último dia do mês".
{ $dateSubtract: { startDate: ISODate("2021-03-31T12:10:05Z"), unit: "month", amount: 1 } }
Observe que a data de regresso, ISODate("2021-02-28T12:10:05Z")
, é a e não a desde fevereiro tem menos dias do que março.
Fuso horário
Ao usar um Identificador de Fuso Horário Olson no campo <timezone>
, o MongoDB aplica o deslocamento de horáriode verão , se aplicável, para o fuso horário especificado.
Por exemplo, considere uma collection sales
com o seguinte documento:
{ "_id" : 1, "item" : "abc", "price" : 20, "quantity" : 5, "date" : ISODate("2017-05-20T10:24:51.303Z") }
A seguinte agregação ilustra como o MongoDB lida com o deslocamento DST para o Identificador de fuso horário Olson. O exemplo utiliza os operadores $hour
e $minute
para retornar as partes correspondentes do campo date
:
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" } } } }])
A operação retorna o seguinte resultado:
{ "_id": 1, "nycHour" : 5, "nycMinute" : 24, "gmtHour" : 10, "gmtMinute" : 24, "nycOlsonHour" : 6, "nycOlsonMinute" : 24 }
Exemplos
Subtrair uma quantia fixa
Considere uma coleção de tempos de conexão do sistema como estes:
db.connectionTime.insertMany( [ { custId: 457, login: ISODate("2020-12-25T19:04:00"), logout: ISODate("2020-12-28T09:04:00") }, { custId: 457, login: ISODate("2021-01-27T05:12:00"), logout: ISODate("2021-01-28T13:05:00") }, { custId: 458, login: ISODate("2021-01-22T06:27:00"), logout: ISODate("2021-01-31T11:00:00") }, { custId: 459, login: ISODate("2021-02-14T20:14:00"), logout: ISODate("2021-02-17T16:05:00") }, { custId: 460, login: ISODate("2021-02-26T02:44:00"), logout: ISODate("2021-02-18T14:13:00") } ] )
Devido a um problema de serviço, você precisa subtrair 3 horas de cada um dos horários de logout de janeiro de 2021. Você pode usar $dateSubtract
em um aggregation pipeline para diminuir o logoutTime
.
db.connectionTime.aggregate( [ { $match: { $expr: { $eq: [ { $year: "$logout" }, 2021 ] }, $expr: { $eq: [ { $month: "$logout" }, 1 ] } } }, { $project: { logoutTime: { $dateSubtract: { startDate: "$logout", unit: "hour", amount: 3 } } } }, { $merge: "connectionTime" } ] )
Duas comparações semelhantes são feitas no estágio $match
. Primeiro, os operadores $year
e $month
extraem o ano e o mês, respectivamente, do objeto Data logoutTime
. Em seguida, o mês e o ano são verificados para ver se correspondem às metas de seleção. Como "janeiro" é codificado como "1", $expr
é verdadeiro quando o ano e o mês são iguais ($eq
) a "2021" e "1".
O estágio $project
utiliza $dateSubtract
para subtrair 3 horas do logoutTime
de cada dcoumento selecionado.
O estágio $merge
atualiza a coleção, escrevendo o novo logoutTime
para os documentos modificados.
Observação
Ao contrário $out
, o estágio $merge
apenas atualiza os documentos correspondentes e preserva o restante da coleção. Para mais detalhes, consulte: $out em comparação com $merge.
Os documentos resultantes têm a seguinte aparência:
{ "_id" : ObjectId("603dd94b044b995ad331c0b5"), "custId" : 457, "login" : ISODate("2020-12-25T19:04:00Z"), "logout" : ISODate("2020-12-28T09:04:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b6"), "custId" : 457, "login" : ISODate("2021-01-27T05:12:00Z"), "logout" : ISODate("2021-01-28T13:05:00Z"), "logoutTime" : ISODate("2021-01-28T10:05:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b7"), "custId" : 458, "login" : ISODate("2021-01-22T06:27:00Z"), "logout" : ISODate("2021-01-31T11:00:00Z"), "logoutTime" : ISODate("2021-01-31T08:00:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b8"), "custId" : 459, "login" : ISODate("2021-02-14T20:14:00Z"), "logout" : ISODate("2021-02-17T16:05:00Z") } { "_id" : ObjectId("603dd94b044b995ad331c0b9"), "custId" : 460, "login" : ISODate("2021-02-26T02:44:00Z"), "logout" : ISODate("2021-02-18T14:13:00Z") }
Filtrar por datas relativas
Você deseja enviar uma pesquisa para clientes que usaram seu serviço na última semana. A expressão $dateSubtract
pode criar um filtro de intervalo relativo ao tempo em que a consulta é executada.
db.connectionTime.aggregate( [ { $match: { $expr: { $gt: [ "$logoutTime", { $dateSubtract: { startDate: "$$NOW", unit: "week", amount: 1 } } ] } } }, { $project: { _id: 0, custId: 1, loggedOut: { $dateToString: { format: "%Y-%m-%d", date: "$logoutTime" } } } } ] )
A variável de agregação incorporada $$NOW
retorna a data/hora atual no formato ISODate. O estágio $match
usa o valor em $$NOW
para obter a data de hoje. Em seguida, a expressão de comparação ($expr
) filtra a coleção usando maior que ($gt
) e $dateSubtract
para combinar documentos que tiveram logoutTime
na semana passada.
O estágio $project
utiliza a expressão $dateToString
para converter as datas em um formato mais legível. Sem a conversão, o MongoDB retornaria a data no formato ISODate. O resultado mostra que dois clientes se desconectaram na última semana.
{ "custId" : 459, "loggedOut" : "2021-02-17" } { "custId" : 460, "loggedOut" : "2021-02-18" }
Ajustar para o horário de verão
Todas as datas são armazenadas internamente no horário UTC. Quando um timezone
é especificado, o $dateSubtract
utiliza a hora local para executar os cálculos. Os resultados são exibidos em UTC.
Você tem clientes em vários fusos horários e deseja ver o efeito que o horário de verão pode ter em seus períodos de faturamento se você faturar por day
ou por hour
.
Criar esta coleção de tempos de conexão:
db.billing.insertMany( [ { location: "America/New_York", login: ISODate("2021-03-14T10:00:00-0500"), logout: ISODate("2021-03-14T18:00:00-0500") }, { location: "America/Mexico_City", login: ISODate("2021-03-14T10:00:00-00:00"), logout: ISODate("2021-03-15T08:00:00-0500") } ] )
Primeiro subtraia 1 dia e, em seguida, subtraia 24 horas das login
datas em cada documento.
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: { $dateSubtract: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } } } }, hours: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { 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: { $dateSubtract: { startDate: "$login", unit: "day", amount: 1, timezone: "$location" } }, timezone: "$location" } }, hoursTZInfo: { $dateToString: { format: "%Y-%m-%d %H:%M", date: { $dateSubtract: { startDate: "$login", unit: "hour", amount: 24, timezone: "$location" } }, timezone: "$location" } }, } } ] ).pretty()
A expressão $dateToString
reformata a saída para legibilidade. Os resultados são resumidos aqui:
Campo | New York | Cidade do México |
---|---|---|
Iniciar | 2021-03-14 15:00 | 2021-03-14 15:00 |
Início, TZ Info | 2021-03-14 11:00 | 2021-03-14 04:00 |
1 dia | 2021-03-13 16:00 | 2021-03-13 15:00 |
1 dias, TZInfo | 2021-03-13 11:00 | 2021-03-13 09:00 |
24 horas | 2021-03-13 15:00 | 2021-03-13 15:00 |
24 horas, TZInfo | 2021-03-13 10:00 | 2021-03-13 09:00 |
O gráfico destaca vários pontos:
As datas não formatadas retornam em UTC. O
$login
para Nova York é UTC -5, no entanto, as linhasstart
,days
ehours
exibem o tempo em UTC.de março é o início do DST em Nova York, mas não no México. O tempo calculado é ajustado quando um local muda para DST e cruza de um
day
para o outro.O horário de verão modifica o comprimento do
day
, não dohour
. Não há alteração de DST parahours
. Só há um ajuste para o horário de verão quando a medidaunit
éday
ou maior e o cálculo ultrapassa uma mudança de relógio notimezone
especificado.