Overview
En esta guía, puedes aprender a manejar Python datetime objetos en PyMongo.
Terminología
Python usa un tipo de dato especializado, datetime.datetime, para representar fechas y horas. MongoDB almacena los valores de datetime en tiempo universal coordinado (UTC), un estándar global de tiempo local en Londres, Inglaterra.
Fechas y horas ingenuas
Un datetime valor es ingenuo cuando no incluye información adicional sobre su diferencia horaria UTC o zona horaria. El siguiente es un ejemplo de un datetime objeto ingenuo:
datetime(2002, 10, 27, 14, 0, 0)
Fechas y horas conscientes
Un datetime valor es consciente cuando incluye un tzinfo atributo. Este atributo indica la diferencia del valor con respecto a la hora UTC, su zona horaria y si el horario de verano estaba vigente. El siguiente es un ejemplo de un datetime objeto consciente:
datetime(2002, 10, 27, 6, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
Importante
Fechas y horas ingenuas y UTC
PyMongo trata los valores ingenuos datetime como valores UTC. PyMongo no convierte los valores ingenuos a UTC; simplemente los interpreta como UTC para su almacenamiento y recuperación.
Para evitar ambigüedades y posibles errores, utilice valores datetime con información de zona horaria explícita. Usar fechas y horas con información de zona horaria le ayudará a evitar discrepancias horarias.
Localización
Lalocalización es el proceso de sumar o restar horas a un datetime valor para traducirlo a otra zona horaria. Para localizar un datetime valor, siga estos pasos:
Utilice pip para instalar la biblioteca
pytzen su entorno Python:pip install pytzCrea un objeto
pytz.timezone. Pasa la zona horaria de destino al constructor como una cadena.Llame al método
localize()en su objetotimezone, pasando el valordatetimea localizar.
El siguiente ejemplo de código localiza un valor datetime en la zona horaria "US/Pacific", con una diferencia de ocho horas:
from datetime import datetime from pytz import timezone utc_datetime = datetime(2002, 10, 27, 6, 0, 0) pacific = timezone("US/Pacific") local_datetime = pacific.localize(utc_datetime) print(f"UTC datetime: {utc_datetime}") print(f"Local datetime: {local_datetime}")
UTC datetime: 2002-10-27 06:00:00 Local datetime: 2002-10-27 06:00:00-08:00
Para obtener una lista canónica de cadenas de zona horaria, consulte la Base dedatos de zonas horarias o su artículo correspondiente en Wikipedia.
Importante
Con PyMongo, no se pueden guardar instancias datetime.date, ya que no existe un tipo BSON para fechas sin horas. Convierta todos los objetos date en objetos datetime antes de guardarlos en MongoDB.
Lectura de fechas y horas
Al usar PyMongo para recuperar un valor datetime, el controlador puede formatearlo como UTC ingenuo, UTC consciente o valor localizado. Las siguientes secciones describen cómo recuperar cada tipo de valor.
Para estas secciones, supongamos que una colección de MongoDB llamada sample_collection contiene el siguiente documento. El valor del campo "date" es un valor UTC datetime.
{"date": datetime(2002, 10, 27, 14, 0, 0)}
Fecha y hora UTC ingenua
De forma predeterminada, PyMongo recupera valores UTC datetime sin información adicional. El siguiente ejemplo de código recupera el documento de muestra e imprime el valor datetime. El valor impreso es idéntico al del documento de muestra. Seleccione Synchronous o pestaña Asynchronous para ver el código correspondiente:
from datetime import datetime collection = database["sample_collection"] find_result = collection.find_one()["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 14:00:00 datetime.tzinfo: None
from datetime import datetime collection = database["sample_collection"] find_result = (await collection.find_one())["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 14:00:00 datetime.tzinfo: None
Fecha y hora UTC conscientes
Para indicar a PyMongo que recupere un valor datetime consciente, cree un objeto CodecOptions y pase tz_aware = True al constructor. Luego, pase el objeto CodecOptions al método get_collection().
El siguiente ejemplo de código recupera el valor datetime del documento de muestra como un datetime consciente. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente:
from pymongo import MongoClient from datetime import datetime from bson.codec_options import CodecOptions options = CodecOptions(tz_aware = True) collection = database.get_collection("sample_collection", options) find_result = collection.find_one()["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 14:00:00+00:00 datetime.tzinfo: <bson.tz_util.FixedOffset object at 0x104db2b80>
from pymongo import MongoClient from datetime import datetime from bson.codec_options import CodecOptions options = CodecOptions(tz_aware = True) collection = database.get_collection("sample_collection", options) find_result = (await collection.find_one())["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 14:00:00+00:00 datetime.tzinfo: <bson.tz_util.FixedOffset object at 0x104db2b80>
Fecha y hora localizadas
Si planea mostrar datetime valores al usuario, puede indicarle a PyMongo que convierta automáticamente todas las horas leídas de MongoDB a una zona horaria específica. Para ello, cree un timezone objeto para la zona horaria de destino, como se describe en la sección Terminología. A continuación, cree un CodecOptions objeto y pase los siguientes argumentos al constructor:
tz_aware:Establecer enTrue.tzinfo:El objetotimezone.
El siguiente ejemplo de código recupera el documento de muestra, pero usa los argumentos tz_aware y tzinfo para localizar automáticamente el valor datetime a la zona horaria "US/Pacific". Selecciona la pestaña Synchronous o Asynchronous para ver el código correspondiente:
from pymongo import MongoClient from datetime import datetime from bson.codec_options import CodecOptions import pytz from pytz import timezone pacific = timezone("US/Pacific") options = CodecOptions(tz_aware = True, tzinfo = pacific) collection = database.get_collection("sample_collection", options) find_result = collection.find_one()["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 06:00:00-08:00 datetime.tzinfo: US/Pacific
from pymongo import MongoClient from datetime import datetime from bson.codec_options import CodecOptions import pytz from pytz import timezone pacific = timezone("US/Pacific") options = CodecOptions(tz_aware = True, tzinfo = pacific) collection = database.get_collection("sample_collection", options) find_result = (await collection.find_one())["date"] print(f"datetime: {find_result}") print(f"datetime.tzinfo: {find_result.tzinfo}")
datetime: 2002-10-27 06:00:00-08:00 datetime.tzinfo: US/Pacific
Tip
El ejemplo anterior especifica las opciones de códec a nivel de colección. También puede especificarlas a nivel de cliente o de base de datos.
Almacenamiento de fechas y horas
Para mantener la coherencia, almacene solo valores UTC datetime en MongoDB. Al usar PyMongo para crear o actualizar un campo que contiene un valor datetime, el controlador primero comprueba si el valor datetime es ingenuo o consciente:
Si
datetimees ingenuo, PyMongo asume quedatetimeestá en UTC y lo almacena en MongoDB sin cambios.Si
datetimees consciente, PyMongo convierte automáticamente la hora a UTC antes de almacenarla en MongoDB.
El siguiente ejemplo de código inserta un documento que contiene un valor datetime localizado en la zona horaria "US/Pacific". PyMongo utiliza la zona horaria adjunta para convertir la hora local a UTC. Al recuperar el documento de MongoDB, el valor datetime está en UTC. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente:
from pymongo import MongoClient from datetime import datetime from pytz import timezone utc_datetime = datetime(2002, 10, 27, 6, 0, 0) pacific = timezone("US/Pacific") local_datetime = pacific.localize(utc_datetime) print(f"datetime before storage: {local_datetime}") collection.insert_one({"date": local_datetime}) find_result = collection.find_one()["date"] print(f"datetime after storage: {find_result}")
datetime before storage: 2002-10-27 06:00:00-08:00 datetime after storage: 2002-10-27 14:00:00
from pymongo import MongoClient from datetime import datetime from pytz import timezone utc_datetime = datetime(2002, 10, 27, 6, 0, 0) pacific = timezone("US/Pacific") local_datetime = pacific.localize(utc_datetime) print(f"datetime before storage: {local_datetime}") await collection.insert_one({"date": local_datetime}) find_result = (await collection.find_one())["date"] print(f"datetime after storage: {find_result}")
datetime before storage: 2002-10-27 06:00:00-08:00 datetime after storage: 2002-10-27 14:00:00
Importante
fecha y hora.ahora()
Evite llamar al método datetime.now() sin argumentos. Esto devuelve la hora local actual.
En su lugar, siempre llama al método datetime.now(tz=datetime.timezone.utc), que devuelve el tiempo actual en UTC.
Manejo de fechas y horas fuera de rango
La clase datetime de Python solo puede representar valores de datetime entre datetime.min y datetime.max (años 1-9999). Puedes representar un rango mucho mayor de fechas y horas utilizando BSON, que permite cualquier valor de milisegundo de 64bits desde la Unix epoch.
Para representar una hora BSON con PyMongo, cree un objeto datetime_ms.DatetimeMS, un contenedor para el tipo int integrado de Python. Puede crear manualmente un objeto DatetimeMS pasando uno de los siguientes valores:
Un
intque representa el número de milisegundos desde la época de UnixUn objeto
datetime
El siguiente ejemplo de código construye un objeto DatetimeMS pasando un valor int que representa el año -146136543, una fecha fuera del rango datetime:
from bson.datetime_ms import DatetimeMS out_of_range = DatetimeMS(-(2**62))
También puedes indicarle a PyMongo que decodifique automáticamente los valores UTC datetime como objetos DatetimeMS. Para hacerlo, establece el parámetro datetime_conversion de CodecOptions a un valor del enum datetime_ms.DatetimeConversion. Las siguientes secciones describen estos valores.
Tip
DatetimeMS Los objetos admiten métodos de comparación enriquecidos con otras instancias de DatetimeMS. También puede convertirlos en objetos datetime mediante el método DatetimeMS.to_datetime().
Conversión de fecha y hora.DATETIME
DatetimeConversion.DATETIME Es el valor predeterminado. Este valor provoca que PyMongo genere un error al intentar decodificar una fecha fuera de rango, como se muestra en el siguiente ejemplo:
from datetime import datetime from bson import encode, decode from bson.datetime_ms import DatetimeMS out_of_range = DatetimeMS(-(2**62)) val = encode({"date": out_of_range}) decoded = decode(val) print(decoded)
... bson.errors.InvalidBSON: year -146136543 is out of range (Consider Using CodecOptions(datetime_conversion=DATETIME_AUTO) or MongoClient(datetime_conversion='DATETIME_AUTO')). ...
Conversión de fecha y hora.DATETIME_MS
Este valor le indica a PyMongo que devuelva solo DatetimeMS objetos, incluso si la fecha está dentro del rango de datetime:
from datetime import datetime from bson import encode, decode from bson.datetime_ms import DatetimeMS from bson.codec_options import CodecOptions, DatetimeConversion val = encode({"date": datetime(1970, 1, 2)}) codec_ms = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_MS) decoded = decode(val, codec_options=codec_ms) print(decoded)
{"date": DatetimeMS(86400000)}
Conversión de fecha y hora.DATETIME_AUTO
Este valor indica a PyMongo que devuelva objetos datetime si el valor está dentro del rango datetime y, en caso contrario, un objeto DatetimeMS. El siguiente ejemplo de código codifica y decodifica una fecha y hora dentro del rango datetime y otra fuera del rango:
from datetime import datetime from bson import encode, decode from bson.datetime_ms import DatetimeMS from bson.codec_options import CodecOptions, DatetimeConversion in_range = encode({"date": datetime(1970, 1, 1)}) out_of_range = encode({"date": DatetimeMS(-(2**62))}) codec_auto = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_AUTO) in_decoded = decode(in_range, codec_options=codec_auto) out_decoded = decode(out_of_range, codec_options=codec_auto) print(f"in-range date: {in_decoded}") print(f"out-of-range date: {out_decoded}")
in-range date: {"date": datetime.datetime(1970, 1, 1, 0, 0)} out-of-range date: {'x': DatetimeMS(-4611686018427387904)}
Conversión de fecha y hora.DATETIME_CLAMP
Este valor "bloquea" los objetos datetime resultantes, obligándolos a estar dentro del rango datetime (recortado a 999,000 microsegundos).
El siguiente ejemplo de código codifica y decodifica una fecha y hora anterior al rango datetime y una fecha y hora posterior al rango datetime. Los valores resultantes se encuentran al principio y al final del rango permitido.
from datetime import datetime from bson import encode, decode from bson.datetime_ms import DatetimeMS from bson.codec_options import CodecOptions, DatetimeConversion before = encode({"date": DatetimeMS(-(2**62))}) after = encode({"date": DatetimeMS(2**62)}) codec_clamp = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_CLAMP) before_decoded = decode(before, codec_options=codec_clamp) after_decoded = decode(after, codec_options=codec_clamp) print(f"datetime before the range: {before_decoded}") print(f"datetime after the range: {after_decoded}")
datetime before the range: {"date": datetime.datetime(1, 1, 1, 0, 0)} datetime after the range: {"date": datetime.datetime(9999, 12, 31, 23, 59, 59, 999000)}
Solución de problemas
Error de desbordamiento al decodificar fechas almacenadas por el controlador de otro idioma
PyMongo decodifica valores BSON datetime en instancias de la clase datetime.datetime de Python. Las instancias de datetime.datetime están limitadas a años entre datetime.MINYEAR (1) y datetime.MAXYEAR (9999). Algunos controladores de MongoDB pueden almacenar valores de fecha y hora BSON con años muy diferentes a los admitidos por datetime.datetime.
Hay varias maneras de solucionar este problema. A partir de PyMongo 4.3, bson.decode puede decodificar valores BSON datetime de cuatro maneras. Puede especificar el método de conversión usando el parámetro datetime_conversion de ~bson.codec_options.CodecOptions.
La opción de conversión predeterminada es ~bson.codec_options.DatetimeConversion.DATETIME, que intentará decodificar el valor como datetime.datetime, lo que permite que ~builtin.OverflowError aparezca para fechas fuera de rango. ~bson.codec_options.DatetimeConversion.DATETIME_AUTO modifica este comportamiento para devolver ~bson.datetime_ms.DatetimeMS cuando las representaciones están fuera de rango, mientras que devuelve objetos ~datetime.datetime como antes. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente:
from datetime import datetime from bson.datetime_ms import DatetimeMS from bson.codec_options import DatetimeConversion from pymongo import MongoClient client = MongoClient(datetime_conversion=DatetimeConversion.DATETIME_AUTO) client.db.collection.insert_one({"x": datetime(1970, 1, 1)}) client.db.collection.insert_one({"x": DatetimeMS(2**62)}) for x in client.db.collection.find(): print(x)
{'_id': ObjectId('...'), 'x': datetime.datetime(1970, 1, 1, 0, 0)} {'_id': ObjectId('...'), 'x': DatetimeMS(4611686018427387904)}
from datetime import datetime from bson.datetime_ms import DatetimeMS from bson.codec_options import DatetimeConversion from pymongo import MongoClient client = AsyncMongoClient(datetime_conversion=DatetimeConversion.DATETIME_AUTO) await client.db.collection.insert_one({"x": datetime(1970, 1, 1)}) await client.db.collection.insert_one({"x": DatetimeMS(2**62)}) async for x in client.db.collection.find(): print(x)
{'_id': ObjectId('...'), 'x': datetime.datetime(1970, 1, 1, 0, 0)} {'_id': ObjectId('...'), 'x': DatetimeMS(4611686018427387904)}
Para otras opciones, consulta la documentación de la API para la clase DatetimeConversion.
Otra opción que no implica configurar datetime_conversion es filtrar los valores del documento fuera del rango admitido por ~datetime.datetime. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente:
from datetime import datetime coll = client.test.dates cur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})
from datetime import datetime coll = client.test.dates cur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})
Si no necesita el valor datetime, puede filtrar solo ese campo. Seleccione la pestaña Synchronous o Asynchronous para ver el código correspondiente:
cur = coll.find({}, projection={'dt': False})
cur = coll.find({}, projection={'dt': False})
Documentación de la API
Para obtener más información sobre cómo trabajar con fechas y horas en PyMongo, consulte la siguiente documentación de API:
datetimeen docs.python.orgpytzen pypi.org