Python datetime 如何处理时区信息
在 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 with 上下文管理器
下一篇: python 日期处理