In this guide, you can learn how to handle Python datetime
objects in PyMongo.
Python uses a dedicated data type, datetime.datetime
, to represent dates and times. MongoDB stores datetime
values in coordinated universal time (UTC), a global time standard local to London, England.
A datetime
value is naive when it doesn't include supplemental information about its UTC offset or time zone. The following is an example of a naive datetime
object:
datetime(2002, 10, 27, 14, 0, 0)
A datetime
value is aware when it includes a tzinfo
attribute. This attribute indicates the value's offset from UTC time, its time zone, and whether daylight saving time was in effect. The following is an example of an aware datetime
object:
datetime(2002, 10, 27, 6, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
Localization is the process of adding hours to, or subtracting hours from, a datetime
value to translate its value to another time zone. To localize a datetime
value, perform the following steps:
Use pip to install the pytz
library in your Python environment: pip install pytz
Create a pytz.timezone
object. Pass the target time zone to the constructor as a string.
Call the localize()
method on your timezone
object, passing in the datetime
value to localize.
The following code example localizes a datetime
value to the "US/Pacific"
time zone, an offset of eight hours:
from datetime import datetimefrom pytz import timezoneutc_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:00Local datetime: 2002-10-27 06:00:00-08:00
For a canonical list of time zone strings, see the Time Zone Database or its corresponding article on Wikipedia.
ImportantWith PyMongo, you can't save datetime.date
instances, because there is no BSON type for dates without times. Convert all date
objects to datetime
objects before saving them to MongoDB.
When you use PyMongo to retrieve a datetime
value, the driver can format it as a naive UTC, aware UTC, or localized value. The following sections describe how to retrieve each kind of value.
For these sections, assume that a MongoDB collection named sample_collection
contains the following document. The value of the "date"
field is a UTC datetime
value.
{"date": datetime(2002, 10, 27, 14, 0, 0)}
By default, PyMongo retrieves UTC datetime
values with no supplemental information. The following code example retrieves the sample document and prints the datetime
value. The printed value is identical to the one in the sample document. Select the Synchronous or Asynchronous tab to see the corresponding code:
from datetime import datetimecollection = 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:00datetime.tzinfo: None
from datetime import datetimecollection = 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:00datetime.tzinfo: None
To instruct PyMongo to retrieve an aware datetime
value, create a CodecOptions
object and pass tz_aware = True
to the constructor. Then, pass the CodecOptions
object to the get_collection()
method.
The following code example retrieves the datetime
value from the sample document as an aware datetime
. Select the Synchronous or Asynchronous tab to see the corresponding code:
from pymongo import MongoClientfrom datetime import datetimefrom bson.codec_options import CodecOptionsoptions = 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:00datetime.tzinfo: <bson.tz_util.FixedOffset object at 0x104db2b80>
from pymongo import MongoClientfrom datetime import datetimefrom bson.codec_options import CodecOptionsoptions = 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:00datetime.tzinfo: <bson.tz_util.FixedOffset object at 0x104db2b80>
If you plan to show datetime
values to the user, you can instruct PyMongo to automatically convert all times read from MongoDB to a specific time zone. To do so, create a timezone
object for the target time zone, as described in the Terminology section. Then, create a CodecOptions
object and pass the following arguments to the constructor:
tz_aware
: Set to True
.
tzinfo
: The timezone
object.
The following code example retrieves the sample document, but uses the tz_aware
and tzinfo
arguments to automatically localize the datetime
value to the "US/Pacific"
time zone. Select the Synchronous or Asynchronous tab to see the corresponding code:
from pymongo import MongoClientfrom datetime import datetimefrom bson.codec_options import CodecOptionsimport pytzfrom pytz import timezonepacific = 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:00datetime.tzinfo: US/Pacific
from pymongo import MongoClientfrom datetime import datetimefrom bson.codec_options import CodecOptionsimport pytzfrom pytz import timezonepacific = 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:00datetime.tzinfo: US/Pacific
Tip
The preceding example specifies codec options at the collection level. You can also specify codec options at the client or database level.
For consistency, store only UTC datetime
values to MongoDB. When you use PyMongo to create or update a field containing a datetime
value, the driver first checks whether the datetime
value is naive or aware:
If the datetime
is naive, PyMongo assumes the datetime
is in UTC and stores it to MongoDB unchanged.
If the datetime
is aware, PyMongo automatically converts the time to UTC before storing it to MongoDB.
The following code example inserts a document containing a datetime
value localized to the "US/Pacific"
time zone. PyMongo uses the attached time zone to convert the local time to UTC. When the document is retrieved from MongoDB, the datetime
value is in UTC. Select the Synchronous or Asynchronous tab to see the corresponding code:
from pymongo import MongoClientfrom datetime import datetimefrom pytz import timezoneutc_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:00datetime after storage: 2002-10-27 14:00:00
from pymongo import MongoClientfrom datetime import datetimefrom pytz import timezoneutc_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:00datetime after storage: 2002-10-27 14:00:00
Important datetime.now()
Avoid calling the datetime.now()
method with no arguments. This returns the current local time.
Instead, always call the datetime.now(tz=datetime.timezone.utc)
method, which returns the current time in UTC.
Python's datetime
class can represent only datetime
values between datetime.min
and datetime.max
(years 1-9999). You can represent a much greater range of dates and times by using BSON, which allows any 64-bit millisecond value from the Unix epoch.
To represent a BSON time with PyMongo, create a datetime_ms.DatetimeMS
object, a wrapper for Python's built-in int
type. You can manually create a DatetimeMS
object by passing in one of the following values:
An int
representing the number of milliseconds since the Unix epoch
A datetime
object
The following code example constructs a DatetimeMS
object by passing in an int
value that represents the year -146136543, a date outside of the datetime
range:
from bson.datetime_ms import DatetimeMSout_of_range = DatetimeMS(-(2**62))
You can also instruct PyMongo to automatically decode UTC datetime
values as DatetimeMS
objects. To do so, set the datetime_conversion
parameter of CodecOptions
to a value from the datetime_ms.DatetimeConversion
enum. The following sections describe these values.
DatetimeMS
objects support rich comparison methods against other instances of DatetimeMS
. You can also convert them to datetime
objects by using the DatetimeMS.to_datetime()
method.
DatetimeConversion.DATETIME
is the default value. This value causes PyMongo to raise an error when it tries to decode an out-of-range date, as shown in the following example:
from datetime import datetimefrom bson import encode, decodefrom bson.datetime_ms import DatetimeMSout_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 UsingCodecOptions(datetime_conversion=DATETIME_AUTO) orMongoClient(datetime_conversion='DATETIME_AUTO'))....
This value instructs PyMongo to return only DatetimeMS
objects, even if the date is within the range of datetime
:
from datetime import datetimefrom bson import encode, decodefrom bson.datetime_ms import DatetimeMSfrom bson.codec_options import CodecOptions, DatetimeConversionval = 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)}
This value instructs PyMongo to return datetime
objects if the value is within datetime
range, and return a DatetimeMS
object otherwise. The following code example encodes and decodes one datetime in the datetime
range and one outside the range:
from datetime import datetimefrom bson import encode, decodefrom bson.datetime_ms import DatetimeMSfrom bson.codec_options import CodecOptions, DatetimeConversionin_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)}
This value "clamps" the resulting datetime
objects, forcing them to be within the datetime
range (trimmed to 999,000 microseconds).
The following code example encodes and decodes one datetime earlier than the datetime
range and one datetime later than the datetime
range. The resulting values are at the beginning and end of the allowed range.
from datetime import datetimefrom bson import encode, decodefrom bson.datetime_ms import DatetimeMSfrom bson.codec_options import CodecOptions, DatetimeConversionbefore = 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 decodes BSON datetime
values to instances of Python's datetime.datetime
class. Instances of datetime.datetime
are limited to years between datetime.MINYEAR
(1) and datetime.MAXYEAR
(9999). Some MongoDB drivers can store BSON datetimes with year values far outside those supported by datetime.datetime
.
There are a few ways to work around this issue. Starting with PyMongo 4.3, bson.decode
can decode BSON datetime
values in one of four ways. You can specify the conversion method by using datetime_conversion
parameter of ~bson.codec_options.CodecOptions
.
The default conversion option is ~bson.codec_options.DatetimeConversion.DATETIME
, which will attempt to decode the value as a datetime.datetime
, allowing ~builtin.OverflowError
to occur for out-of-range dates. ~bson.codec_options.DatetimeConversion.DATETIME_AUTO
alters this behavior to instead return ~bson.datetime_ms.DatetimeMS
when representations are out-of-range, while returning ~datetime.datetime
objects as before. Select the Synchronous or Asynchronous tab to see the corresponding code:
from datetime import datetimefrom bson.datetime_ms import DatetimeMSfrom bson.codec_options import DatetimeConversionfrom pymongo import MongoClientclient = 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 datetimefrom bson.datetime_ms import DatetimeMSfrom bson.codec_options import DatetimeConversionfrom pymongo import MongoClientclient = 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)}
For other options, see the API documentation for the DatetimeConversion class.
Another option that does not involve setting datetime_conversion
is to filter out document values outside of the range supported by ~datetime.datetime
. Select the Synchronous or Asynchronous tab to see the corresponding code:
from datetime import datetimecoll = client.test.datescur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})
from datetime import datetimecoll = client.test.datescur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})
If you don't need the value of datetime
, you can filter out just that field. Select the Synchronous or Asynchronous tab to see the corresponding code:
cur = coll.find({}, projection={'dt': False})
cur = coll.find({}, projection={'dt': False})
For more information about working with dates and times in PyMongo, see the following API documentation:
datetime
at docs.python.org
pytz
at pypi.org
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4