python datetime北京时间转UTC时间,差6分钟

1. 环境

python: 3.6.13
pytz 2021.1
django 2.1.7

2. 问题

在北京时间转UTC时间时,遇到了一些问题,总是相差6分钟,如下

import datetime
import pytz

>>> now = datetime.datetime.now()
>>> now # 无时区信息的北京时间
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920)
>>> cn_tz = pytz.timezone('Asia/Shanghai')
>>> cn_tz # 上海本地时间时区 LMT(Local Mean Time) STD,没有查到具体含义,猜测是与DST(Daily Saving Time)夏令时相对的标准时间
<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>
>>> shanghai_now = now.replace(tzinfo=cn_tz) # 给时间加上时区信息
>>> shanghai_now # 可以看到时区信息并不是标准的北京时间,而是上海本地时间
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)
>>> shanghai_now.astimezone(pytz.utc) # 可以看到把带有上海本地时区的时间转换成UTC时间时,并不是少了整整八小时,而是八小时零六分钟
datetime.datetime(2021, 4, 17, 20, 0, 9, 774920, tzinfo=<UTC>)

问题总结:即把不带时区信息的北京时间datetime对象now,以now.replace(tzinfo=cn_tz).astimezone(pytz.utc) 的方式转换成utc时间,会差6分钟

补充:对于datetime类的replace与astimezone方法,测试结果如下(部分变量承接上文代码)

>>> test_time = datetime.datetime.now(pytz.utc)
>>> test_time
datetime.datetime(2021, 4, 17, 20, 38, 0, 508177, tzinfo=<UTC>)
>>> test_time.replace(tzinfo=cn_tz) # replace只替换时区信息,时间仍然保持不变
datetime.datetime(2021, 4, 17, 20, 38, 0, 508177, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)

>>> test_time
datetime.datetime(2021, 4, 17, 20, 38, 0, 508177, tzinfo=<UTC>)
>>> test_time.astimezone(cn_tz) # astimezone则在替换时区信息的同时,把时间转换成目标时区的时间
datetime.datetime(2021, 4, 18, 4, 38, 0, 508177, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

>>> test_time.astimezone() # astimezone()时区参数省略时,会默认按照系统的时区转换,可以看到结果与test_time.astimezone(cn_tz)等价 
# CST(Central Standard Time (USA) UT-6:00 美国标准时间 Central Standard Time (Australia) UT+9:30 澳大利亚标准时间 China Standard Time UT+8:00 中国标准时间 Cuba Standard Time UT-4:00 古巴标准时间)
datetime.datetime(2021, 4, 18, 4, 38, 0, 508177, tzinfo=datetime.timezone(datetime.timedelta(0, 28800), 'CST'))

>>> now
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920)
>>> now.astimezone() # 被转换的时间对象无时区信息时, 则会认为该时间对象的时区为系统的时区,系统的时区时间转成系统的时区时间,所以结果不变
>>> now.astimezone(cn_tz)
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

总结:replace()方法只会无脑替换时区,不做时间转换,而astimezone()在替换时区的同时还会把时间转换成目标时区的时间

分析问题
仔细观察上述代码可以发现,now.replace(tzinfo=cn_tz)返回的时间对象时区信息是 tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>,而now.astimezone(cn_tz返回的时间对象时区信息是 tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>,相差6分钟的原因正是replace()方法设置的时区信息有问题。另外,通过datetime.datetime.now(cn_tz)生成的时间,时区信息也是正确的,同样为tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>。 所以要避免差6分钟的现象出现,就不能用replace()方法。

3. 解决方法

使用cn_tz.localize()给无时区的时间对象添加正确的时间信息,然后使用astimezone()方法进行不同时区的时间转换即可

>>> now
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920)
>>> cn_tz.localize(now)
datetime.datetime(2021, 4, 18, 4, 6, 9, 774920, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
>>> cn_tz.localize(now).astimezone(pytz.utc)
datetime.datetime(2021, 4, 17, 20, 6, 9, 774920, tzinfo=<UTC>)

4. 补充:Django中的时区问题

由于我并没有使用Django默认的数据库,,而是使用的MongoDB存数据,所以可能与默认数据的情况不同。Django默认数据库文章可以参考django时间的时区问题
在实际使用中发现MongoDB始终保存UTC时间,如果读写的时间没有时区信息,则数据库默认时区是UTC,直接按照该时间查询数据库或保存该时间; 如果带有时区信息,则数据库会把该时间转成UTC时间再查询或者保存。所以在读写数据库的时候,要么给时间附加正确的时区信息,如果时间没有时区信息则一定要转成UTC时间。
另外,可以使用django.utils.timezone.get_default_timezone返回settings.py TIME_ZONE对应的时区。

参考文章:
PYTHON中的时区处理,LMT差6分钟的问题解决
datetime astimezone和replace的区别。时区转换还是替换?
GMT、UTC、DST、CST时区代表的意义
django时间的时区问题

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值