datetime 与 timestamp 相互转换遇到的坑

发布于 2021-03-22 12:45:10 字数 2631 浏览 1404 评论 0

事情是这样的,今天遇到一个业务场景:按照比赛的时间 start_at 作为分页查询的条件获取赛程列表,首先初始化 20 条数据(数据库用的是 MongoDB):

第1条记录: start_at: 2015-08-15 01:13:17.330299
第2条记录: start_at: 2015-08-15 01:13:18.330299
第3条记录: start_at: 2015-08-15 01:13:19.330299
第4条记录: start_at: 2015-08-15 01:13:20.330299
第5条记录: start_at: 2015-08-15 01:13:21.330299
第6条记录: start_at: 2015-08-15 01:13:22.330299
第7条记录: start_at: 2015-08-15 01:13:23.330299
第8条记录: start_at: 2015-08-15 01:13:24.330299
第9条记录: start_at: 2015-08-15 01:13:25.330299
第10条记录: start_at: 2015-08-15 01:13:26.330299

客户端请求的时候通过 last_id 来确定下次从什么位置获取,第一次请求的时候不需要此参数,系统默认从第1条开始查询,此处假设page_size为3,每次获取3条。第1次请求完,服务端会返回一个 last_id 给客户端,那么这个 last_id 是怎么生成的呢?服务端会把第3条记录的start_at从datetime转换成timestamps,返回一个int类型的时间戳给客户端,第次一的查询条件是:

cursor = conn.matches.find({}).sort([('start_at', 1)]).limit(3)

返回前3条数据以及last_idlast_id是根据第三条数据数据的的start_at转换成时间戳之后的值)

last_id = time.mktime(start_at.timetuple())
>>> 1439572399.0  # 第三条记录 2015-08-15 01:13:19.330000 转换成时间戳的值

客户端发起第2次请求获取第2页的时候,把该数值传递到服务端,服务端接收到last_id=1439572399后,做一次转换,转换成datetime类型:

start_at = datetime.datetime.fromtimestamp(last_id)

第二次查询的条件是:

 cursor = conn.matches.find({'start_at': {'$gt': start_at}}).sort([('start_at', 1)]).limit(3)

于是碰到一个奇怪的问题:第二次查询返回的第一条数据和第一次查询返回的数据是一样的,感觉查询条件 $gt 变成了 gte,这太不科学了,开始根本找不到原因,于是把初始化数据修改了一下:

第1条记录: start_at: 2015-08-14 11:05:21
第2条记录: start_at: 2015-08-14 11:05:22
第3条记录: start_at: 2015-08-14 11:05:23
第4条记录: start_at: 2015-08-14 11:05:24
第5条记录: start_at: 2015-08-14 11:05:25
第6条记录: start_at: 2015-08-14 11:05:26
第7条记录: start_at: 2015-08-14 11:05:27
第8条记录: start_at: 2015-08-14 11:05:28
第9条记录: start_at: 2015-08-14 11:05:29
第10条记录: start_at: 2015-08-14 11:05:30

然后重复上面的查询,此时返回的结果是正常的。究其原因是什么呢?从两份数据的对比,唯一的区别就是前者的时间带有毫秒数,为啥带上毫秒就出问题了呢,于是开始一步步调试发现一处细节,timestamp转换成datetime的时候,最后的毫秒丢失了:

start_at = datetime.datetime.fromtimestamp(1439572399)
>>> 2015-08-15 01:13:19

于是问题就可以解释的通了,没有带毫秒的2015-08-15 01:13:19肯定是小于2015-08-15 01:13:19.330299的,因此第二次查询的时候把第一次的查询返回的最后一条数据也查出来了。问题定位到了,那么就好解决了,原来最终的bug就出现在datetime转成timestamp的埋下的。

last_id = time.mktime(start_at.timetuple())

转换的时候,会自动忽略掉毫秒级别的值,解决的办法就是把毫秒数加上:

time.mktime(start_at.timetuple()) + start_at.microsecond * 0.000001

整个世界都清静了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

已经忘了多久

文章 0 评论 0

15867725375

文章 0 评论 0

LonelySnow

文章 0 评论 0

走过海棠暮

文章 0 评论 0

轻许诺言

文章 0 评论 0

信馬由缰

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文