Visão geral
Neste guia, você pode aprender como lidar com objetos Python datetime no PyMongo.
Terminologia
O Python utiliza um tipo de dados dedicado, datetime.datetime, para representar datas e horas. O MongoDB armazena valores datetime no tempo universal coordenado (UTC), um padrão de hora global local para Londres, Inglaterra.
Horários ingênuos
Um valor datetime é ingênuo quando não inclui informações adicionais sobre o deslocamento UTC ou fuso zona. Veja a seguir um exemplo de um objeto datetime ingênuo :
datetime(2002, 10, 27, 14, 0, 0)
Horários cientes
Um valor datetime está ciente quando inclui um atributo tzinfo . Este atributo indica o deslocamento do valor em relação à hora UTC, seu fuso horário e se o horário de verão estava em vigor. Veja a seguir um exemplo de um objeto datetime com reconhecimento:
datetime(2002, 10, 27, 6, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
Localização
A localização é o processo de adicionar horas ou subtrair horas de um valor de datetime para traduzir seu valor para outro fuso zona. Para localizar um valor datetime , execute as seguintes etapas:
Use pip para instalar a biblioteca do
pytzem seu ambiente Python:pip install pytzCrie um objeto
pytz.timezone. Passe o fuso zona de destino para o construtor como uma string.Ligue para o método
localize()em seu objetotimezone, passando no valordatetimepara localizar.
O seguinte exemplo de código localiza um valor datetime para o fuso horário "US/Pacific" , um deslocamento de oito 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 obter uma lista canônica de strings de zona horário, consulte o Time Zone Database ou seu artigo correspondente na Wikipedia.
Importante
Com o PyMongo, você não pode salvar instâncias datetime.date , porque não há tipo BSON para datas sem horários. Converta todos os objetos date em objetos datetime antes de salvá-los no MongoDB.
Lendo horários
Quando você usa o PyMongo para recuperar um valor datetime , o driver pode formatá-lo como um UTC ingênuo, UTC ciente ou valor localizado. As seções a seguir descrevem como recuperar cada tipo de valor.
Para estas seções, suponha que uma coleção MongoDB denominada sample_collection contenha o seguinte documento. O valor do campo "date" é um valor UTC datetime .
{"date": datetime(2002, 10, 27, 14, 0, 0)}
Data/hora UTC ingnua
Por padrão, o PyMongo recupera valores UTC datetime sem informações adicionais. O exemplo de código a seguir recupera o documento de amostra e imprime o valor datetime. O valor impresso é idêntico ao do documento de amostra . Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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
Data hora UTC ciente
Para instruir o PyMongo a recuperar um valor de datetime ciente, crie um objeto CodecOptions e passe tz_aware = True para o construtor. Em seguida, passe o objeto CodecOptions para o método get_collection() .
O exemplo de código a seguir recupera o valor datetime do documento de exemplo como um datetime ciente. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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>
Data e hora localizada
Se você planeja mostrar valores datetime para o usuário, poderá instruir o PyMongo a converter automaticamente todos os horários lidos do MongoDB para um fuso horário específico. Para fazer isso, crie um objeto timezone para o fuso horário de destino, conforme descrito na seção Terminologia . Em seguida, crie um objeto CodecOptions e passe os seguintes argumentos para o construtor:
tz_aware: Defina comoTrue.tzinfo: O objetotimezone.
O exemplo de código a seguir recupera o documento de amostra, mas usa os argumentos tz_aware e tzinfo para localizar automaticamente o valor datetime para o zona horário "US/Pacific". Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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
Dica
O exemplo anterior especifica as opções de codec no nível da collection. Você também pode especificar opções de codec no nível do cliente ou do banco de dados.
Armazenar data/hora
Para consistência, armazene somente valores UTC datetime no MongoDB. Quando você usa o PyMongo para criar ou atualizar um campo contendo um valor datetime , o driver primeiro verifica se o valor datetime é ingênuo ou ciente:
Se o
datetimefor ingênuo, o PyMongo pressupõe que odatetimeestá em UTC e o armazena no MongoDB inalterado.Se o
datetimeestiver ciente, o PyMongo converte automaticamente o tempo para UTC antes de armazená-lo no MongoDB.
O exemplo de código a seguir insere um documento contendo um valor datetime localizado no zona horário "US/Pacific" . O PyMongo usa o zona horário anexado para converter a hora local em UTC. Quando o documento é recuperado do MongoDB, o valor datetime está em UTC. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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
datetime. now()
Evite ligar para o método datetime.now() sem argumentos. Isso retorna a hora local atual.
Em vez disso, sempre chame o método datetime.now(tz=datetime.timezone.utc) , que retorna a hora atual em UTC.
Tratamento de datas e hora fora do intervalo
A classe datetime do Python pode representar apenas datetime valores entre datetime.min e datetime.max (anos 1-9999). Você pode representar um intervalo muito maior de datas e horas usando BSON, que permite qualquer valor de milissegundo de 64bits da época do Unix.
Para representar um horário BSON com o PyMongo, crie um objeto datetime_ms.DatetimeMS , um wrapper para o tipo int integrado do Python. Você pode criar manualmente um objeto DatetimeMS passando um dos seguintes valores:
Um
intrepresentando o número de milissegundos desde a época do UnixUm objeto
datetime
O exemplo de código a seguir constrói um objeto DatetimeMS passando um valor int que representa o ano -146136543, uma data fora do intervalo datetime :
from bson.datetime_ms import DatetimeMS out_of_range = DatetimeMS(-(2**62))
Você também pode instruir o PyMongo a decodificar automaticamente valores UTC datetime como objetos DatetimeMS . Para fazer isso, defina o parâmetro datetime_conversion de CodecOptions para um valor do enum datetime_ms.DatetimeConversion . As seções a seguir descrevem esses valores.
Dica
DatetimeMS os objetos suportam métodos de comparação avançados contra outras instâncias de DatetimeMS. Você também pode convertê-los em objetos datetime usando o método DatetimeMS.to_datetime() .
DatetimeConversion.DATETIME
DatetimeConversion.DATETIME é o valor padrão. Esse valor faz com que o PyMongo gere um erro ao tentar decodificar uma data fora do intervalo, como mostrado no exemplo a seguir:
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')). ...
DatetimeConversion.DATETIME_MS
Esse valor instrui o PyMongo a retornar somente DatetimeMS objetos, mesmo que a data esteja dentro do intervalo 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)}
DatetimeConversion.DATETIME_AUTO
Esse valor instrui o PyMongo a retornar objetos datetime se o valor estiver dentro do intervalo datetime e a retornar um objeto DatetimeMS caso contrário. O seguinte exemplo de código codifica e decodifica uma data/hora no intervalo datetime e uma fora do intervalo:
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)}
DatetimeConversion.DATETIME_CLAMP
Esse valor "grampeia" os objetos datetime resultantes, forçando-os a estar dentro do intervalo datetime (cortado para 999 000 microssegundos).
O exemplo de código a seguir codifica e decodifica uma data/hora anterior ao intervalo datetime e uma data/hora posterior ao intervalo datetime . Os valores resultantes estão no início e no final do intervalo 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)}
Solução de problemas
OverflowError ao decodificar datas armazenadas pelo driver de outro idioma
O PyMongo decodifica valores BSON datetime para instâncias da classe datetime.datetime do Python. As instâncias de datetime.datetime estão limitadas a anos entre datetime.MINYEAR (1) e datetime.MAXYEAR (9999). Alguns drivers do MongoDB podem armazenar datas BSON com valores de ano muito fora dos suportados por datetime.datetime.
Existem algumas maneiras de contornar esse problema. Começando com o PyMongo 4.3, bson.decode pode decodificar valores BSON datetime de quatro maneiras. Você pode especificar o método de conversão usando o parâmetro datetime_conversion de ~bson.codec_options.CodecOptions.
A opção de conversão padrão é ~bson.codec_options.DatetimeConversion.DATETIME, que tentará decodificar o valor como datetime.datetime, permitindo que ~builtin.OverflowError ocorra em datas fora do intervalo. ~bson.codec_options.DatetimeConversion.DATETIME_AUTO altera esse comportamento para retornar ~bson.datetime_ms.DatetimeMS quando as representações estão fora do intervalo, enquanto retorna objetos ~datetime.datetime como antes. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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 outras opções, consulte a documentação API para a classe DatetimeConversion.
Outra opção que não envolve a configuração datetime_conversion é filtrar valores de documento fora do intervalo suportado por ~datetime.datetime. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
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}})
Se você não precisar do valor de datetime, poderá filtrar apenas esse campo. Selecione a aba Synchronous ou Asynchronous para ver o código correspondente:
cur = coll.find({}, projection={'dt': False})
cur = coll.find({}, projection={'dt': False})
Documentação da API
Para obter mais informações sobre como trabalhar com datas e horários no PyMongo, consulte a seguinte documentação da API:
datetimeem docs.python.orgpytzem pypi.org