日期时间和时区#

这些示例演示了如何处理Python datetime.datetime 对象在PyMongo中正确。

基本用法#

PyMongo使用 datetime.datetime 对象,用于表示MongoDB文档中的日期和时间。因为MongoDB假设日期和时间是UTC格式,所以应该注意确保写入数据库的日期和时间反映UTC。例如,以下代码将当前UTC日期和时间存储到MongoDB中:

>>> result = db.objects.insert_one(
...     {"last_modified": datetime.datetime.now(tz=datetime.timezone.utc)}
... )

始终使用 datetime.datetime.now(tz=datetime.timezone.utc)() ,它以UTC为单位显式返回当前时间,而不是 datetime.datetime.now() ,不带参数,返回当前本地时间。避免这样做:

>>> result = db.objects.insert_one({"last_modified": datetime.datetime.now()})

价值 last_modified 这两个示例之间的差异非常大,尽管这两个文档都存储在大约相同的本地时间。这将使读取它们的应用程序感到困惑:

>>> [doc["last_modified"] for doc in db.objects.find()]  
[datetime.datetime(2015, 7, 8, 18, 17, 28, 324000),
 datetime.datetime(2015, 7, 8, 11, 17, 42, 911000)]

bson.codec_options.CodecOptions 有一个 tz_aware 启用“感知”的选项 datetime.datetime 对象,即知道它们所在时区的日期时间。默认情况下,PyMongo检索朴素的日期时间:

>>> result = db.tzdemo.insert_one({"date": datetime.datetime(2002, 10, 27, 6, 0, 0)})
>>> db.tzdemo.find_one()["date"]
datetime.datetime(2002, 10, 27, 6, 0)
>>> options = CodecOptions(tz_aware=True)
>>> db.get_collection("tzdemo", codec_options=options).find_one()["date"]  
datetime.datetime(2002, 10, 27, 6, 0,
                  tzinfo=<bson.tz_util.FixedOffset object at 0x10583a050>)

使用时区保存日期时间#

储存时 datetime.datetime 指定时区的对象(例如,它们具有 tzinfo 不是的财产 None ),PyMongo将自动将这些日期时间转换为UTC:

>>> import pytz
>>> pacific = pytz.timezone("US/Pacific")
>>> aware_datetime = pacific.localize(datetime.datetime(2002, 10, 27, 6, 0, 0))
>>> result = db.times.insert_one({"date": aware_datetime})
>>> db.times.find_one()["date"]
datetime.datetime(2002, 10, 27, 14, 0)

阅读时间#

如前所述,默认情况下 datetime.datetime PyMongo返回的对象将是朴素的,但会反映UTC(即存储在MongoDB中的时间)。通过设置 tz_aware 选择权 CodecOptionsdatetime.datetime 对象将识别时区并具有 tzinfo 属性,该属性反映UTC时区。

Pymongo3.1引入了一个 tzinfo 可以在上设置的属性 CodecOptions 转换 datetime.datetime 对象自动设置为本地时间。例如,如果我们想读取美国/太平洋时间中MongoDB的所有时间:

>>> from bson.codec_options import CodecOptions
>>> db.times.find_one()['date']
datetime.datetime(2002, 10, 27, 14, 0)
>>> aware_times = db.times.with_options(codec_options=CodecOptions(
...     tz_aware=True,
...     tzinfo=pytz.timezone('US/Pacific')))
>>> result = aware_times.find_one()
datetime.datetime(2002, 10, 27, 6, 0,  
                  tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)

处理超出范围的日期时间#

Python 的 datetime 只能表示在允许的范围内的日期时间 minmax ,而BSON中允许的日期时间范围可以表示从Unix纪元开始的任何64位毫秒数。要处理此问题,我们可以使用 bson.datetime_ms.DatetimeMS 对象,该对象是 int 内置的。

将UTC日期时间值解码为 DatetimeMSCodecOptions 应该有它的 datetime_conversion 参数设置为中提供的选项之一 bson.datetime_ms.DatetimeConversion 。这些措施包括 DATETIMEDATETIME_MSDATETIME_AUTODATETIME_CLAMPDATETIME 是默认选项,并且具有引发 OverflowError 在尝试对超出范围的日期进行解码时。 DATETIME_MS 只会回来 DatetimeMS 对象,而不管表示的DateTime是在范围内还是在范围外:

>>> from datetime import datetime
>>> from bson import encode, decode
>>> from bson.datetime_ms import DatetimeMS
>>> from bson.codec_options import CodecOptions, DatetimeConversion
>>> x = encode({"x": datetime(1970, 1, 1)})
>>> codec_ms = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_MS)
>>> decode(x, codec_options=codec_ms)
{'x': DatetimeMS(0)}

DATETIME_AUTO 会回来的 datetime 如果基础UTC日期时间在范围内,则为 DatetimeMS 如果基础DateTime不能使用内置的 datetime

>>> x = encode({"x": datetime(1970, 1, 1)})
>>> y = encode({"x": DatetimeMS(-(2**62))})
>>> codec_auto = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_AUTO)
>>> decode(x, codec_options=codec_auto)
{'x': datetime.datetime(1970, 1, 1, 0, 0)}
>>> decode(y, codec_options=codec_auto)
{'x': DatetimeMS(-4611686018427387904)}

DATETIME_CLAMP 将夹紧所产生的 datetime 要在其中的对象 minmax (修剪为 999000 微秒):

>>> x = encode({"x": DatetimeMS(2**62)})
>>> y = encode({"x": DatetimeMS(-(2**62))})
>>> codec_clamp = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_CLAMP)
>>> decode(x, codec_options=codec_clamp)
{'x': datetime.datetime(9999, 12, 31, 23, 59, 59, 999000)}
>>> decode(y, codec_options=codec_clamp)
{'x': datetime.datetime(1, 1, 1, 0, 0)}

DatetimeMS 对象支持与其他实例的丰富比较方法 DatetimeMS 。还可以将它们转换为 datetime 对象具有 to_datetime()