最近的项目需要根据用户所属时区制定一些特定策略,学习、应用了若干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 pytzIn [2]: from datetime import datetime, timedeltaIn [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+9In [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不同时区转换介绍与踩坑相关推荐

  1. python3中datetime库,time库以及pandas中的时间函数区别与详解

    1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...

  2. Python3中内置函数callable介绍

          Python3中的内置函数callable接受一个对象参数,如果此对象参数看起来可调用,则callable函数返回True,否则返回False.如果返回True,则调用仍有可能失败:但如果 ...

  3. html5中插入视频无效原,h5视频播放踩坑记录

    随着抖音.快手这类的视频类app的火爆,移动端h5视频类应用也随之兴起,使用video播放的场景也越来越多,本篇文章主要例举了移动端视频播放的一些场景和个人在开发过程中遇到的一些问题,希望在看过这篇文 ...

  4. Pandas Timestamp 和 python 中 datetime 的互相转换

    Pandas 的Timestamp 和 python 的 datetime,   这是两种不同的类型. 它们之间可以互相转换. refer to: https://www.jianshu.com/p/ ...

  5. Python3中参数*args和**kwargs介绍

    在Python中,我们可以使用两种特殊符号将可变数量的参数传递给函数:*args和**kwargs.你可以使用任何单词代替args和kwargs,但通常做法是使用args和kwargs.       ...

  6. python3中datetime模块当前时间多加一天、一小时、一分钟

    现在的时间减去2分钟31秒后得出的时间: >>>(datetime.datetime.now() - datetime.timedelta(minutes=2,seconds=31) ...

  7. vue中form表单支持回车键提交踩坑

    平时我们项目中的列表页面通常都有条件搜索,为了使用方便通常都会支持回车搜索. 此功能的实现普遍做法是:为form表单添加 @keydown.enter.native="自定义方法名称&quo ...

  8. 使用mybatis-generator自动生成代码的方法介绍及踩坑

    mybatis-geneator是一款mybatis自动代码生成工具,可以通过配置,快速生成mapper和xml文件. 一. 使用maven插件 在pom.xml中添加mybatis-generato ...

  9. layui中从上一个js模块中取参数_layui的引用js踩坑

    前言: 今天因为项目需要,需要使用layui,因为本身不多的前端经验,以为layui的用法和其他的前端框架,例如jquery,bootstrap等等,只需要直接引入layui.js,和layui.cs ...

最新文章

  1. Servlet基础(一) Servlet基础和关键的API介绍
  2. 实时获取ccd图像_四元数数控:CCD视觉检测定位系统在玻璃瓶缺陷的检测
  3. ElasticSearch 知识点整理(深入)
  4. qt-信号和槽的连接写法
  5. SecureCRT 7.3.4 安装以及破解
  6. rip java_JavasScripV1.0.htm
  7. STM32,你了解多少?(转载)
  8. 记忆碎片---搭建php+apache+eclipse中的问题
  9. java代码pingip_java代码ping一个IP地址
  10. 优酷1080P的KUX视频如何快速转换成MP4格式
  11. oracle 删除索引报错ORA-01418:sepecified index does not exist
  12. R语言 ROC曲线 截断值、特异性、敏感性和曲线下面积AUC值的计算和显示
  13. 国足3比1叙利亚,晋级世预赛12强赛,国足会在12强赛中取得怎样的成绩呢?
  14. 房贷又降,不买!还能降
  15. 微软edge浏览器安装包下载地址-Microsoft edge download
  16. react-native电影简介app,了解一下?(android端)
  17. 【POI 2006】 Tet-Tetris-3D
  18. 浅析Android五大布局
  19. 风控模型中的KS-指标
  20. 大学linux考试题,大学考试的LINUX试题参考答案(8)

热门文章

  1. Docker基础讲解狂神笔记:容器数据卷,docker compose,docker swarm(2/2)未修订版欢迎留言补漏
  2. influxdb内存消耗分析及性能优化「追踪篇」
  3. Facebook 是用什么编写
  4. Linux内核机制总结内存管理之连续内存分配器(二十七)
  5. 【React教程】一、React简介
  6. 微信接入探秘(三)——加密消息的处理
  7. Linux安装U盘启动制作
  8. 斜拉桥主梁的预应力混凝土横梁计算midas
  9. GitHub美化主页设计(保姆教程)
  10. ***S 2012 仪表 -- 关键绩效指标