Python datetime 如何处理时区信息


Posted in Python onSeptember 02, 2020

在 Python 常用日期处理 -- 内置模块 datetime 探讨了 Python 如何使用 datetime, 如果是一个跨时区的应用(Web 应用都是),就不能只存储一个时间而不带时区,如此,全球用户将会看到一个相同的时间字符串,白天黑夜就错乱了。比说用户信息的更新时间存储为 2020-07-07 13:46:08, 上海的用户和芝加哥的用户看到的是同一个时间字符串,实质上却相差好多个小时。

我们可以这么做,在服务端只存储一个 Timestamp 长整型值或 UTC 时间,Timestamp 是无关乎时区的,它总是相对于一个 UTC 时间的偏移值; 然后由客户端根据本地时区来显示当地时间。不过在服务端存储为 Timestamp 或 UTC 可读性就不强了,打开文件看到 Timestamp 整形值,大脑是无法直接转换为日期,UTC 时间略好一些。

另一种做法可在服务端存储为开发者便于理解的带时区的时间,如 2020-07-07T13:46:08.342+08:00, 客户获得该时间,因为带有时区信息也就能转换为客户端本地时间。

客户端请求时还可以把本地的时区信息传送给服务端,由服务端转换为相应的本地时间发送给客户端,但 HTTP 头信息默认不带时区信息,客户端必须主动发送它。

本人倾向于在服务端存为带时区的时间,2020-07-07T13:46:08.342+08:00 是一个标准的存储格式(ISO_OFFSET_DATE_TIME),客户端收到它再转换本地时间,JavaScript 一个很好的组件 moment 处理时间。

探索 Python 对时区的处理

Python 内置组件不能像 Java 的 ZoneId.of("Asia/Shanghai") 直接以时区名获得 Zone,而需要知道与标准时区的偏移,如上海是东八区,在 Python 中要用 timezone(timedelta(hours=+8)).

那么来看 Python 中输出带时区信息,以下是一些应用 Pytho 时区 timezone 的例子

from datetime import datetime, timezone, timedelta
 
tz = timezone(timedelta(hours=+8))
 
fmt = '%Y-%m-%dT%H:%M:%S.%f%z'
zoned_time1 = datetime.today().astimezone(tz)
print(1, zoned_time1.strftime(fmt))  # 2020-07-08T04:30:26.221450+0800
 
zoned_time2 = datetime.now(tz)
print(2, zoned_time2.strftime(fmt))  # 2020-07-08T04:30:26.221543+0800
 
zoned_time3 = datetime.utcnow()
print(3, zoned_time3.isoformat())   # 2020-07-07T20:30:26.221848
 
print(4, zoned_time2.strftime('%Y-%m-%dT%H:%M:%S.%f%Z')) # 2020-07-08T04:30:26.221543UTC+08:00
 
timestamp = datetime.today().timestamp()
print(5, timestamp)         # 1594153826.221895
print(6, datetime.fromtimestamp(timestamp, tz=tz)) # 2020-07-08 04:30:26.221895+08:00
 
zoned_time4 = datetime(2020, 7, 8, 4, 23, 53, 112, tzinfo=tz)
print(7, zoned_time4.isoformat())  # 2020-07-08T04:23:53.000112+08:00
 
print(8, zoned_time2.isoformat())  # 2020-07-08T04:30:26.221543+08:00

输出为, 已加到上面源代码中

1 2020-07-08T04:30:26.221450+0800
2 2020-07-08T04:30:26.221543+0800
3 2020-07-07T20:30:26.221848
4 2020-07-08T04:30:26.221543UTC+08:00
5 1594153826.221895
6 2020-07-08 04:30:26.221895+08:00
7 2020-07-08T04:23:53.000112+08:00
8 2020-07-08T04:30:26.221543+08:00

时间字符串中要带有时区信息,首先时间要转换为带时区的,如用

datetime.astimezone(tz)          # 已有时间转换为带时区的
datetime.fromtimestamp(timestamp, tz=tz) # 从 timestamp 构建 datetime 时加上时区

找到 Python 输出标准格式的方法

从上面的输出结果看第 8 行 2020-07-08T04:30:26.221543+08:00 接近于 Java 的 ISO_OFFSET_DATE_TIME 格式,只是毫秒段 Python 用了 6 位数字,参考 strftime-strptime-behavior 的 Python datetime 格式字符串定义找不到如何把毫秒段收缩为 3 位。

不过注意到 datetime.isoformat() 方法还有一个 timespec 可用,执行下面的代码

from datetime import datetime, timezone, timedelta
 
tz = timezone(timedelta(hours=+8))
now = datetime.now(tz)
print(now.isoformat(timespec='milliseconds'))

输出为

2020-07-08T04:41:10.793+08:00

这正式我们想要的。还不仅仅是,继续往下读,我们还需要让 Python 支持夏令时,否则对于芝加哥时间夏天和冬天看到的都是 -5, 那是不对的。

pytz 组件构建时区

Python 也有一个通过时区名称获得 timezone 的组件,那就是 pytz - Python TimeZone

$ pip install pytz

测试 pytz

from datetime import datetime
from pytz import timezone
 
tz_shanghai = timezone('Asia/Shanghai')
tz_chicago = timezone('America/Chicago')
 
print(datetime.now(tz=tz_shanghai).isoformat(timespec='milliseconds'))
print(datetime.now(tz=tz_chicago).isoformat(timespec='milliseconds'))

输出为

2020-07-08T04:55:29.699+08:00
2020-07-07T15:55:29.699-05:00

关于夏令时与冬令时

国内实行夏令时制还是很多年前的事了,80 后初期生人或许还有些印象,就是下午放了学走到街上就能看到《新闻联播》。为了达成一切形式的统一,我们不再实行夏令时制了,避免了造成可能的分裂。但其他国家仍然有下令时,这会造成同一个地方在一年中产生两个时区。

例如芝加哥,在夏季时 timezone 是 -05:00, 冬季时是 -06:00.

现在就来看一下 Python 是否能正确的处理夏令时(Date Saving Time)与冬令时(Night Saving Time)。回看上面代码是在 7 月份执行的结果,此时如果把本地时间改为 12 月份,再看输出

2020-12-08T06:10:27.862+08:00
2020-12-07T16:10:27.862-06:00

上海的时区仍然为 +08:00, 而芝加哥的时区变成了 -06:00

Python 本身不支持对时令的支持,Python 只知道与 UTC 标准时区的偏移,timezone(timedelta(hours=-5),夏天冬天它的偏移都是 -5,实现夏令冬令时是由 pytz 达成的,同样的 tz = timezone('America/Chiago')

夏天的结果是 Python 的 timezone(timedelta(hours=-5))
冬天的结果是 Python 的 timezone(timedelta(hours=-6))

对比 Java 对时区的处理

不妨看下隔壁 Java 是如何对时区处理的,分别测试了新旧时间 API

Date today = new Date();
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(today));
 
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(now.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));

2020-07-07T14:53:55.017-05:00
2020-07-08T03:53:55.031+08:00

小结一下

时间用 Timestamp(长整形值) 或统一的 UTC 时间存储和传输,在显示时转换为本地时间,但存储介质上可读性差
用 timezone(timedelta(hours=-5)) 应用时区来存储,可读性增强,但会有夏/冬令时间误差问题
用 pytz 的 timezone('America/Chicago') 由时区名来构造 timezone 很好的解决了时区和夏/冬令时的问题

以上就是Python datetime 如何处理时区信息的详细内容,更多关于Python datetime 处理时区信息的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
浅谈Python 中整型对象的存储问题
May 16 Python
Python3连接SQLServer、Oracle、MySql的方法
Jun 28 Python
Python批处理删除和重命名文件夹的实例
Jul 11 Python
使用memory_profiler监测python代码运行时内存消耗方法
Dec 03 Python
Django2.1集成xadmin管理后台所遇到的错误集锦(填坑)
Dec 20 Python
pymysql模块的使用(增删改查)详解
Sep 09 Python
Python超越函数积分运算以及绘图实现代码
Nov 20 Python
Python 获取命令行参数内容及参数个数的实例
Dec 20 Python
python3.8.3安装教程及环境配置的详细教程(64-bit)
Nov 28 Python
关于django python manage.py startapp 应用名出错异常原因解析
Dec 15 Python
Python中tkinter的用户登录管理的实现
Apr 22 Python
python超详细实现完整学生成绩管理系统
Mar 17 Python
浅析python中的del用法
Sep 02 #Python
浅析NumPy 切片和索引
Sep 02 #Python
详解Python 函数参数的拆解
Sep 02 #Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
Sep 02 #Python
python 常用日期处理-- datetime 模块的使用
Sep 02 #Python
详解Python中的路径问题
Sep 02 #Python
python dict如何定义
Sep 02 #Python
You might like
php XMLWriter类的简单示例代码(RSS输出)
2011/09/30 PHP
PHP 如何利用phpexcel导入数据库
2013/08/24 PHP
php使用strtotime和date函数判断日期是否有效代码分享
2013/12/25 PHP
php rsa 加密,解密,签名,验签详解
2016/12/06 PHP
php获取指定数量随机字符串的方法
2017/02/06 PHP
PHP调用API接口实现天气查询功能的示例
2017/09/21 PHP
Node.js:Windows7下搭建的Node.js服务(来玩玩服务器端的javascript吧,这可不是前端js插件)
2011/06/27 Javascript
Jquery焦点与失去焦点示例应用
2014/06/10 Javascript
教你如何自定义百度分享插件以及bshare分享插件的分享按钮
2014/06/20 Javascript
通用javascript代码判断版本号是否在版本范围之间
2015/11/29 Javascript
关于JS中二维数组的声明方法
2016/09/24 Javascript
AJAX和jQuery动态加载数据的实现方法
2016/12/05 Javascript
JavaScript实现计数器基础方法
2017/10/10 Javascript
详解Vue2 添加对scss的支持
2019/01/02 Javascript
layer.alert自定义关闭回调事件的方法
2019/09/27 Javascript
JS this关键字在ajax中使用出现问题解决方案
2020/07/17 Javascript
vuex存取值和映射函数使用说明
2020/07/24 Javascript
ant design vue中日期选择框混合时间选择器的用法说明
2020/10/27 Javascript
[00:10]DOTA2 TI9勇士令状明日上线
2019/05/07 DOTA
Python入门_学会创建并调用函数的方法
2017/05/16 Python
python爬虫获取多页天涯帖子
2018/02/23 Python
Python爬虫信息输入及页面的切换方法
2018/05/11 Python
python从入门到精通 windows安装python图文教程
2019/05/18 Python
Python 读取串口数据,动态绘图的示例
2019/07/02 Python
SpringBoot实现登录注册常见问题解决方案
2020/03/04 Python
2019年Java面试必问之经典试题
2012/09/12 面试题
C语言怎样定义和声明全局变量和函数最好
2013/11/26 面试题
一套比较完整的软件测试人员面试题
2012/05/13 面试题
优秀大学生推荐信范文
2013/11/28 职场文书
司机检讨书
2014/02/13 职场文书
幼儿园的门卫岗位职责
2014/04/10 职场文书
仓库统计员岗位职责
2015/04/14 职场文书
慈善献爱心倡议书
2015/04/27 职场文书
酒会开场白大全
2015/06/01 职场文书
2016年9月份红领巾广播稿
2015/12/21 职场文书
导游词之江南园林狮子林
2019/09/16 职场文书