Docs Menu

Docs HomeDevelop ApplicationsPython DriversPyMongo

Dates and Times

On this page

  • Overview
  • Terminology
  • Naive Datetimes
  • Aware Datetimes
  • Localization
  • Reading datetimes
  • Naive UTC datetime
  • Aware UTC datetime
  • Localized datetime
  • Storing datetimes
  • Handling Out-of-Range datetimes
  • DatetimeConversion.DATETIME
  • DatetimeConversion.DATETIME_MS
  • DatetimeConversion.DATETIME_AUTO
  • DatetimeConversion.DATETIME_CLAMP
  • API Documentation

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:

  1. Use pip to install the pytz library in your Python environment: pip install pytz

  2. Create a pytz.timezone object. Pass the target time zone to the constructor as a string.

  3. 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 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

For a canonical list of time zone strings, see the Time Zone Database or its corresponding article on Wikipedia.

Important

With 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.

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

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:

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

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:

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

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.

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

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 DatetimeMS
out_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.

Tip

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

This value instructs PyMongo to return only DatetimeMS objects, even if the date is within the range of 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)}

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

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

For more information about working with dates and times in PyMongo, see the following API documentation:

← Custom Types