日期时间和时区#
这些示例演示了如何处理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 选择权 CodecOptions
, datetime.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
只能表示在允许的范围内的日期时间 min
和 max
,而BSON中允许的日期时间范围可以表示从Unix纪元开始的任何64位毫秒数。要处理此问题,我们可以使用 bson.datetime_ms.DatetimeMS
对象,该对象是 int
内置的。
将UTC日期时间值解码为 DatetimeMS
, CodecOptions
应该有它的 datetime_conversion
参数设置为中提供的选项之一 bson.datetime_ms.DatetimeConversion
。这些措施包括 DATETIME
, DATETIME_MS
, DATETIME_AUTO
, DATETIME_CLAMP
。 DATETIME
是默认选项,并且具有引发 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
要在其中的对象 min
和 max
(修剪为 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()
。