Definición
$dateSubtractNuevo en la versión 5.0.
Decrementa un
Date()objeto por un número especificado de unidades de tiempo.La
$dateSubtractLa expresión tiene la siguiente sintaxis:{ $dateSubtract: { startDate: <Expression>, unit: <Expression>, amount: <Expression>, timezone: <tzExpression> } } Retorna un
Date(). ElstartDatepuede ser cualquier expresión que resuelva a un tipo Date, Timestamp u ObjectId. No importa qué tipo de datos se haya utilizado como entrada, el valor retornado será un objetoDate().CampoObligatorio/OpcionalDescripciónstartDateRequerido
La fecha de inicio, en UTC, para la operación de resta. El
startDatepuede ser cualquier expresión que resuelva en una Fecha, un Sello de tiempo o un ObjectID.unitRequerido
El
unitutilizado para medir elamountdel tiempo restado delstartDate. Elunites una expresión que se resuelve en una de las siguientes cadenas:yearquarterweekmonthdayhourminutesecondmillisecond
amountRequerido
El número de
unitsrestado destartDate. Elamountes una expresión que se resuelve a un entero o long. Elamounttambién puede resolverse a un decimal integral y/o a un double si ese valor puede convertirse a un long sin pérdida de precisión.timezoneOpcional
La zona horaria para llevar a cabo la operación.
<tzExpression>debe ser una expresión válida que se resuelva en una string formateada como un Identificador de zona horaria Olson o un Diferencia UTC. Si no se proporcionatimezone, el resultado se muestra enUTC.FormatoEjemplosIdentificador de zona horaria Olson
"America/New_York" "Europe/London" "GMT" Desplazamiento UTC
+/-[hh]:[mm], e.g. "+04:45" +/-[hh][mm], e.g. "-0530" +/-[hh], e.g. "+03" Para obtener más información sobre expresiones y tipos consulta Expresiones y BSON types.
Comportamiento
Medición del tiempo
MongoDB sigue el uso prevalente de bases de datos y trabaja con el tiempo en UTC. La expresión dateSubtract siempre toma un startDate en UTC y devuelve un resultado en UTC. Si se especifica el timezone, el cálculo se realizará utilizando el timezone especificado. La zona horaria es especialmente importante cuando un cálculo implica el horario de verano (DST).
Si el unit es un month, o mayor, la operación se ajusta para tener en cuenta el último día del mes. Restar una month el último día de marzo, por ejemplo, demuestra el ajuste "último día del mes".
{ $dateSubtract: { startDate: ISODate("2021-03-31T12:10:05Z"), unit: "month", amount: 1 } }
Tenga en cuenta que la fecha devuelta, ISODate("2021-02-28T12:10:05Z"), es la 28 y no la 31, ya que febrero tiene menos días que marzo.
Zona horaria
Al utilizar un identificador de zona horaria de Olson en el campo <timezone>, MongoDB aplica el Desplazamiento del horario de verano, si corresponde para la zona horaria especificada.
Por ejemplo, considera una colección sales con el siguiente documento:
db.sales.insertOne( { "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:15:39.736Z") } )
La siguiente agregación ilustra cómo MongoDB gestiona el ajuste de DST para el identificador de zona horaria Olson. El ejemplo usa los operadores $hour y $minute para devolver las partes correspondientes del 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" } } } }])
La operación devuelve el siguiente resultado:
{ "_id": 1, "nycHour" : 5, "nycMinute" : 24, "gmtHour" : 10, "gmtMinute" : 24, "nycOlsonHour" : 6, "nycOlsonMinute" : 24 }
Ejemplos
Restar una cantidad fija
Considera una colección de tiempos de conexión del sistema como estos:
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") } ] )
Debido a un problema de servicio, necesitas restar 3 horas de cada uno de los horarios de cierre de sesión de enero de 2021. Puedes usar $dateSubtract en un pipeline de agregación para decrementar el 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" } ] )
Se hacen dos comparaciones similares en la etapa $match. Primero, los operadores $year y $month extraen el año y el mes, respectivamente, del objeto de fecha logoutTime. Luego, se comprueba el mes y el año para ver si coinciden con los objetivos de selección. Dado que "enero" está codificado como "1", $expr es verdadero cuando el año y el mes son iguales ($eq) a "2021" y "1".
La etapa $project usa $dateSubtract para restar 3 horas del logoutTime de cada documento seleccionado.
Finalmente, la etapa $merge actualiza la colección, escribiendo el nuevo logoutTime para los documentos modificados.
Nota
A diferencia de $out, la etapa $merge solo actualiza los documentos coincidentes y conserva el resto de la colección. Para más detalles, consulta: $out en comparación con $merge.
Los documentos resultantes se ven así:
{ "_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 fechas relativas
Desea enviar una encuesta a los clientes que hayan utilizado su servicio en la última semana. La expresión $dateSubtract puede crear un filtro de rango relativo al momento en que se ejecuta la query.
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" } } } } ] )
La variable de agregación integrada$$NOWdevuelve la fecha y hora actual en formato ISODate. La etapa$matchutiliza el valor de$$NOWpara obtener la fecha actual. A continuación, la expresión de comparación ($expr) filtra la colección utilizando valores mayores que ($gt) y $dateSubtract para encontrar los documentos con un valor de logoutTime en la última semana.
La etapa $project utiliza la expresión para convertir las fechas a un formato más $dateToString legible. Sin la conversión, MongoDB devuelve la fecha en formato ISODate y asume la zona horaria UTC.
La salida muestra que dos clientes han cerrado sesión en la última semana.
{ "custId" : 459, "loggedOut" : "2021-02-17" } { "custId" : 460, "loggedOut" : "2021-02-18" }
Ajusta para el horario de verano
Todas las fechas se almacenan internamente en hora UTC. Cuando se especifica timezone, $dateSubtract utiliza la hora local para realizar los cálculos. Los resultados se muestran en UTC.
Tienes clientes en varias zonas horarias y quieres ver qué efecto podría tener el horario de verano en tus períodos de facturación si facturas por day o por hour.
Crea esta colección de tiempos de conexión:
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") } ] )
Primero reste 1 día, luego reste 24 horas de las fechas login en 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()
La expresión reformatea la salida para facilitar su lectura. Los resultados se resumen a $dateToString continuación:
Campo | New York | Ciudad de México |
|---|---|---|
Inicio | 2021-03-14 15:00 | 2021-03-14 15:00 |
Comienzo, Info TZ | 2021-03-14 11:00 | 2021-03-14 04:00 |
1 día | 2021-03-13 16:00 | 2021-03-13 15:00 |
1 día, 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 |
La gráfica destaca varios puntos:
Las fechas sin formato se devuelven en UTC. El
$loginde Nueva York es UTC -5, sin embargo las filasstart,daysyhoursmuestran la hora en UTC.El 14 de marzo es el inicio del horario de verano en Nueva York, pero no en México. El tiempo calculado se ajusta cuando una ubicación cambia al horario de verano y cruza de uno
dayal siguiente.El horario de verano (DST) modifica la duración de
day, no dehour. No hay horario de verano (DST) parahours. Solo hay un ajuste por horario de verano (DST) cuando la mediciónunites dedayo más y el cálculo cruza un cambio de horario en eltimezoneespecificado.