【问题标题】:Why is there a difference between the stored and queried time in Mongo database using PyMongo client?为什么使用 PyMongo 客户端在 Mongo 数据库中存储时间和查询时间存在差异?
【发布时间】:2017-02-19 03:53:57
【问题描述】:

我有一个带有 foo 对象的 MongoDB 数据库。我想记录它们被添加到数据库的时间。为此,我的方法是存储一个时间,将foo 添加到数据库并设置开始时间。当我从数据库中获取foo 时,开始时间属性应该与我添加它的时间一致。

以下是我的测试,它反映了这一点。这些函数并不是非常重要,因为它们本质上是 db.collection.insertdb.collection.find_one 调用。

def test_set_foo_start_time(self):
    import datetime
    t = datetime.datetime.utcnow()
    foo_id = "42"
    print("\nAdding {0} to the database.".format(t))
    add_foo_to_database(foo_id)
    self.assertIsNotNone(get_foo_from_database(foo_id))
    set_foo_start_time(foo_id, t)
    time = get_foo_from_database(foo_id)['start_time']
    print("Extracting {0} from the database.".format(time))
    self.assertEqual(t, time)

当我运行测试时,我惊讶地发现它失败了:

test_set_foo_start_time (test_foo_misc.FooMiscellanyTestCase) ... 
Adding 2016-10-10 17:01:16.559332 to the database.
Extracting 2016-10-10 17:01:16.559000+00:00 from the database.
FAIL

断言错误在哪里:

Traceback (most recent call last):
  File "/Users/erip/Code/proj/tests/test_foo_misc.py", line 336, in test_set_foo_start_time
    self.assertEqual(t, time)
AssertionError: datet[36 chars], 559332) != datet[36 chars], 559000, tzinfo=<bson.tz_util.FixedOffset obj[15 chars]240>)

为什么存储和提取的时间不同?我可以添加一个增量来检查时间范围,但我更愿意存储确切的时间。如何修复我的测试以解决这些微秒差异?

【问题讨论】:

    标签: python python-3.x mongodb datetime pymongo


    【解决方案1】:

    根据BSON spec,日期精度限制为毫秒(强调我的),

    UTC 日期时间 - int64 是自 Unix 纪元以来的 UTC 毫秒

    有几种不同的方法可以解决这个问题,但这在很大程度上取决于您是否需要亚毫秒级的精度。如果你不这样做,那么你可以在毫秒级别进行舍入或截断:

    datetime.datetime.utcnow().replace(microsecond=0)
    

    您还需要确保没有携带时区信息(因为这会影响您的相等性检查)。

    如果你需要亚毫秒级的精度,你可以

    • 存储一个字符串编码的时间戳,您可以在查询时对其进行解析。

      datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")
      

      好处是您可以保持精度。

      这里明显的缺点是字符串仅限于字典排序,这增加了预插入和后查询的步骤。

    • 存储包含时间戳的 long 或 double。

      t = datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)
      milli = t.total_seconds() * 1000 # microseconds present here
      

      好处是您可以保持精度,现在可以正确排序。

      缺点是您又多了一个处理步骤。

    • 将日期存储为字典,其中包含时间戳作为 BSON UTC 日期时间(毫秒)加上亚毫秒作为双精度或长精度

      dt = datetime.datetime.utcnow()
      t = {
          "utc": dt,
          "micro": dt.microsecond
      }
      

      好处是您再次保持了精度,并且可以(索引和)对两个字段进行排序。

      缺点是处理步骤和解构的时间对象。

    【讨论】:

    • 可靠的答案。注意——由于datetime.datetime 是序列化/反序列化的方式(至少在我的示例中),有必要在find_one 调用中从返回的对象中删除tzinfodt.replace(tzinfo=None).
    猜你喜欢
    • 1970-01-01
    • 2011-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    相关资源
    最近更新 更多