Docs Menu
Docs Home
/ /

Fechas y horas

En esta guía, puedes aprender a manejar Python datetime objetos en PyMongo.

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.

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)

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.

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:

  1. Utilice pip para instalar la biblioteca pytz en su entorno Python: pip install pytz

  2. Crea un objeto pytz.timezone. Pasa la zona horaria de destino al constructor como una cadena.

  3. Llame al método localize() en su objeto timezone, pasando el valor datetime a 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.

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)}

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

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>

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 en True.

  • tzinfo:El objeto timezone.

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.

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 datetime es ingenuo, PyMongo asume que datetime está en UTC y lo almacena en MongoDB sin cambios.

  • Si datetime es 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.

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 int que representa el número de milisegundos desde la época de Unix

  • Un 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().

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')).
...

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)}

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)}

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)}

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})

Para obtener más información sobre cómo trabajar con fechas y horas en PyMongo, consulte la siguiente documentación de API:

Volver

Tipos de códecs