Python3中datetime时区转换介绍与踩坑
最近的项目需要根据用户所属时区制定一些特定策略,学习、应用了若干python3的时区转换相关知识,这里整理一部分记录下来。
下面涉及的几个概念及知识点:
GMT时间:Greenwich Mean Time, 格林尼治平均时间
UTC时间:Universal Time Coordinated 世界协调时,可以认为是更精准的GMT时间,但两者误差极小,在1s以内,一般可视为等同
LMT:Local Mean Time, 当地标准时间
Python中的北京时间:Python的标准timezone中信息中并没有Asia/Beijing,原因要追溯到国民政府期间上报给国际标准的五个时区城市没有北京,因此一般使用Asia/Shanghai获取东8区时间
Python使用到的时间相关函数及概念:
包含时区信息的datetime称为: offset-aware datetime,反之称为offset-naive datetime
pytz.timezone(x): pytz package中预定义的时区相关对象, pytz可通过 python3 -m pip install pytz 安装
datetime(...) : 直接指定year/month/day/hour/second生成naive datetime
datetime(...tzinfo=tz) : 直接指定year/month/day/hour/second+时区信息生成offset-aware datetime
datetime.now(): 生成当前默认时区的 naive datetime
datetime.now(tzinfo=tz): 生成指定时区的offset-aware datetime
datetime.strptime(string, format) : 生成当前默认时区的string、format表示的 naive datetime
datetime.replace(tzinfo=tz): 直接替换datetime 时区信息为tz时区offset-aware datetime--不针对时区进行任何转换
datetime.astimezone(tz): 将时间转换为新的tz时区的offset-aware datetime
下述代码示例中,由于云主机位于日本,所以默认时区为东9区(Asia/Tokyo)
Python中获取当前时刻时间:
In [1]: import pytz In [2]: from datetime import datetime, timedelta In [3]: datetime.now() # 默认时区当前时间
Out[3]: datetime.datetime(2021, 8, 1, 18, 36, 8, 352873) In [4]: datetime.now(pytz.timezone('Asia/Tokyo')) # 指定Tokyo时区当前时间
Out[4]: datetime.datetime(2021, 8, 1, 18, 36, 25, 421048, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)
可以看到,datetime.now()未指定时区时,获取到的对象是offset-navie datetime,而指定时区后则是offset-aware datetime,naive和aware的datetime是不可以执行比较、相减相关操作的,只有同类型的datetime才能求时间差值、比较大小,如下:
In [5]: datetime.now() - datetime.now(pytz.timezone('Asia/Tokyo'))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-8b6c111dc5de> in <module>
----> 1 datetime.now() - datetime.now(pytz.timezone('Asia/Tokyo'))
TypeError: can't subtract offset-naive and offset-aware datetimes
In [6]: datetime.now() - datetime.now() # 只有同样的offset-naive datetime才能求差值
Out[6]: datetime.timedelta(days=-1, seconds=86399, microseconds=999991)
In [8]: datetime.now(pytz.timezone('Asia/Tokyo')) - datetime.now(pytz.timezone('Asia/Tokyo')) # 同样的offset-aware datetime才能求差值
Out[8]: datetime.timedelta(days=-1, seconds=86399, microseconds=999976)
这里碰到了第一个坑,比如我们想获得北京时间2021年1月1日0点的datetime,然后将其转换为东京时间,直觉上我们很可能这么写:
In [19]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Asia/Shanghai')) # 这里获取北京时间20210101 0点的datetime
Out[19]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>) # 注意获取的是LMT时间
In [21]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Asia/Shanghai')).astimezone(pytz.timezone('Asia/Tokyo')) # 将北京时转换为东京时间
Out[21]: datetime.datetime(2021, 1, 1, 0, 54, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>) # 获取的是日本标准时间JST+9
In [22]: datetime.now(pytz.timezone('Asia/Shanghai')) # 示例获取当前时刻北京时间
Out[22]: datetime.datetime(2021, 8, 1, 18, 11, 6, 706727, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>) # 获取的是中国标准时间(CST+8)
仔细一看,北京时间的0点转化为东京时间却是0:54,相差是54分钟,而不是1个小时,这就奇怪了,仔细一看tzinfo中的信息是LMT+8:06:00 STD,表示这是LMT时间,相比UTC快8小时6分钟,而不是东8区标准时间,而通过astimezone方法转换后得到的就是日本标准时间(东9区),所以两者之前的差值并不是1小时整。
第一个坑究其原因,通过datetime(..tzinfo=..)指定时区获取的是LMT,而datetime.now(tz)、datetime.astimezone(tz) 获取的却是UTC(GMT)标准时间,LMT和GMT标准时间可能会有甚至十分钟级的差值,这已经足够影响到程序的正常逻辑了。
所以如果要保证获取标准时区的时间,建议避免使用Asia/Shanghai、Asia/Tokyo这类大洲/城市 字符串表示时间,而使用GMT、UTC这些无歧义的标准时区,如下:
In [45]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-9'))
Out[45]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-9'>) # 东9区应使用GMT-9
这里第二个坑出现了,由于历史原因,Python中timezone的表示中,时区偏移以西为正,以东为负,和我们熟悉的ISO标准刚好相反,所以东9区应该表示为Etc/GMT-9, 而Etc/GMT+9表示的其实是西9区,如下可以验证GMT-9与JST相差0, GMT+9与JST相差18小时(64800s):
In [50]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-9')) - datetime(2021, 1, 1).astimezone(pytz.timezone('Asia/Tokyo'))
Out[50]: datetime.timedelta(0)
In [51]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT+9')) - datetime(2021, 1, 1).astimezone(pytz.timezone('Asia/Tokyo'))
Out[51]: datetime.timedelta(seconds=64800)
最后,获取指定时区2021年1月1日datetime的方式,以北京时间为例:
In [56]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-8'))
Out[56]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-8'>)
In [58]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT-8')).astimezone(pytz.timezone('Asia/Shanghai'))
Out[58]: datetime.datetime(2021, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>) # 可见GMT-8和东八区标准时间(CST+8)一致
进一步如果要获取指定时区零点的时间戳就很简单了:
In [44]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')).timestamp() # 获取格林尼治时区2021年1月1日0点时间戳
Out[44]: 1609459200.0
另外两种获取指定时区时刻的方法,此三种方式彼此等价:
In [51]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')) == datetime(2021, 1, 1).replace(tzinfo=pytz.timezone('Etc/GMT0'))
Out[51]: True
In [53]: datetime(2021, 1, 1, tzinfo=pytz.timezone('Etc/GMT0')) == datetime.strptime('20210101', '%Y%m%d').replace(tzinfo=pytz.timezone('Etc/GMT0'))
Python3中datetime时区转换介绍与踩坑的更多相关文章
- java中的时区转换
目录 java中的时区转换 一.时区的说明 二.时间的表示 三.时间戳 四.Date类和时间戳 五.java中的时区转换 java中的时区转换 一.时区的说明 地球表面按经线从东到西,被划成一个个区域 ...
- datetime时区转换
http://www.dannysite.com/blog/122/ Python标准库中的datetime模块提供了各种对日期和时间的处理方法.从本文的主题着手,首先利用datetime中提供的ut ...
- python datetime时区转换
from pytz import timezone def datetime_as_timezone(date_time, time_zone): tz = timezone(time_zone) u ...
- 彻底解决Odoo8.0单时区应用中的时区问题
原文地址:http://shine-it.net/index.php/topic,17001.0.html 由于数据库中存储的是UTC时区,默认情况下数据导出和group by都存在时区问题.彻底解决 ...
- [置顶]
xamarin android toolbar(踩坑完全入门详解)
网上关于toolbar的教程有很多,很多新手,在使用toolbar的时候踩坑实在太多了,不好好总结一下,实在浪费.如果你想学习toolbar,你肯定会去去搜索androd toolbar,既然你能看到 ...
- C#中UnixTime和DateTime的转换(转载)
由于在API请求中返回回来的时间格式为UNIX形式,需要转换成正常的显示方式,在网上找到了这么一个例子. 使用是在C#中使用的,所以WP8开发应该也可以. 转载源地址:http://blog.linu ...
- Python中模块之time&datetime的功能介绍
time&datetime的功能介绍 1. time模块 1. 时间的分类 1. 时间戳:以秒为单位的整数 2. 时间字符格式化:常见的年月日时分秒 3. 时间元祖格式:9大元素,每个元素对应 ...
- SQL中DateTime转换成Varchar样式
SQL中DateTime转换成Varchar样式语句及查询结果:Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AMSelect ...
- Python3中内置类型bytes和str用法及byte和string之间各种编码转换,python--列表,元组,字符串互相转换
Python3中内置类型bytes和str用法及byte和string之间各种编码转换 python--列表,元组,字符串互相转换 列表,元组和字符串python中有三个内建函数:,他们之间的互相转换 ...
随机推荐
- 使用 Docker 部署 Node 应用 - 镜像文件尺寸的优化
前面 使用 Docker 部署 Node 应用 一文中完成了镜像的创建和运行,不过生成的镜像还有些粗糙,需要进一步优化. 镜像的优化 通过 docker images 看到简单的一个 node 服务端 ...
- 「csp-s模拟测试(9.18)」Set·Read·Race
昨天考试考得有点迷??? 一看内存限制,T1 64MB T2 16MB 当场懵比......... T1 set 考场打的背包问题和随机化,其实能randA掉,但不小心数组开小了????(长记性!!! ...
- 【模拟8.05】优美序列(线段树 分块 ST算法)
如此显然的线段树,我又瞎了眼了 事实上跟以前的奇袭很像....... 只要满足公式maxn-minn(权值)==r-l即可 所以可以考虑建两颗树,一棵节点维护位置,一棵权值, 每次从一棵树树上查询信息 ...
- 【Javascript + Vue】实现随机生成迷宫图片
前言 成品预览:https://codesandbox.io/s/maze-vite-15-i7oik?file=/src/maze.js 不久前写了一篇文章介绍了如何解迷宫:https://www. ...
- 地图可视化神器keplergl新增对jupyter lab 3.0的支持
就在几天前,地图可视化神器kepler.gl面向Python的接口库keplergl迎来了新的0.3.0版本更新. 虽然官方文档还并未及时更新相关的内容说明,但我在快速地试用之后发现,现在的keple ...
- jenkins在aws eks中的CI/CD及slave
本文档不讲解jenkins的基础用法,主要讲解配合k8s的插件,和pipeline的用途和k8s ci/cd的流程. 以及部署在k8s集群内和集群外的注意事项. 1.准备工作 以下在整个CI/CD流程 ...
- 49、django工程(cookie+session)
49.1.介绍: 1.cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要"保持状态",因此cookie就是在这样一个场景下诞生. cooki ...
- buu crackRTF
一.无壳,拖入ida,静态编译一下 整体逻辑还是很清晰,这里我的盲区是那个加密函数,是md5,没看出来,内存地址看错了,之前用黑盒动调一下,发现猜不出来,看某位wp发现有的老哥,直接sha1爆破的,狠 ...
- Docker部署Redis集群(主从复制 高可用)
环境 vmware12+centos7 关于环境安装可以参考我的另一篇博客 https://www.cnblogs.com/pengboke/p/13063168.html 1.清理环境 我这里用的虚 ...
- 使用oss来存取及优化图片资源
目录 1. 开通阿里云OSS,并创建存储空间 2. 图片上传及处理 2.1 图片上传 2.2 图片处理 前言: 在日常开发中,不免会遇到需要实现图片上传与展示的需求.比如一个文章发布系统,我们通常会开 ...