用datetime和pytz来转换时区
admin
2023-07-31 00:38:32
0

Python标准库里提供了time、datetime和calendar这3个模块来进行时间和日期的处理,其中应用最广的是datetime,而转换时区也是靠它来做的。

时区这个玩意非常抽象,处理它时经常弄得我头晕,只好记录下来,免得以后再犯晕。

首先要知道时区之间的转换关系,其实这很简单:把当地时间减去当地时区,剩下的就是格林威治时间了。
例如北京时间的18:00就是18:00+08:00,相减以后就是10:00+00:00,因此就是格林威治时间的10:00。

而把格林威治时间加上当地时区,就能得到当地时间了。
例如格林威治时间的10:00是10:00+00:00,转换成太平洋标准时间就是加上-8小时,因此是02:00-08:00。

而太平洋标准时间转换成北京时间转换也一样,时区相减即可。
例如太平洋标准时间的02:00-08:00,与北京时间相差-16小时,因此结果是18:00+08:00。

而Python的datetime可以处理2种类型的时间,分别为offset-naive和offset-aware。前者是指没有包含时区信息的时间,后者是指包含时区信息的时间,只有同类型的时间才能进行减法运算和比较。

不幸的是datetime模块的函数在默认情况下都只生成offset-naive类型的datetime对象,例如now()、utcnow()、fromtimestamp()、utcfromtimestamp()和strftime()。
其中now()和fromtimestamp()可以接受一个tzinfo对象来生成offset-aware类型的datetime对象,但是标准库并不提供任何已实现的tzinfo类,只能自己动手丰衣足食了…
下面就是实现格林威治时间和北京时间的tzinfo类的例子:

12345678910111213141516171819 ZERO_TIME_DELTA = timedelta(0)LOCAL_TIME_DELTA = timedelta(hours=8) # 本地时区偏差 class UTC(tzinfo): def utcoffset(self, dt): return ZERO_TIME_DELTA  def dst(self, dt): return ZERO_TIME_DELTA class LocalTimezone(tzinfo): def utcoffset(self, dt): return LOCAL_TIME_DELTA  def dst(self, dt): return ZERO_TIME_DELTA  def tzname(self, dt): return \’+08:00\’

一个tzinfo类需要实现utcoffset、dst和tzname这3个方法。其中utcoffset需要返回夏时令的时差调整;tzname需要返回时区名,如果你不需要用到的话,也可以不实现。

一旦生成了一个offset-aware类型的datetime对象,我们就能调用它的astimezone()方法,生成其他时区的时间(会根据时差来计算)。
而如果拿到的是offset-naive类型的datetime对象,也是可以调用它的replace()方法来替换tzinfo的,只不过这种替换不会根据时差来调整其他时间属性。
因此,如果拿到一个格林威治时间的offset-naive类型的datetime对象,直接调用replace(tzinfo=UTC())即可转换成offset-aware类型,然后再调用astimezone()生成其他时区的datetime对象。
而如果是+6:00时区的offset-naive类型的datetime对象,则可以创建一个+6:00时区的tzinfo类,然后用上述方式转换。
而反过来要将offset-aware类型转换成offset-naive类型时,为了不至于弄混,建议先用astimezone(UTC())生成格林威治时间,然后再replace(tzinfo=None)。

看上去一切都很简单,但不知道你还是否记得上文所述的夏时令。
提起夏时令这个玩意,真是让我头疼,因为它没有规则可循:有的国家实行夏时令,有的国家不实行,有的国家只在部分地区实行夏时令,有的地区只在某些年实行夏时令,每个地区实行夏时令的起止时间都不一定相同,而且有的地方TMD还不是用几月几日来指定夏时令的起止时间的,而是用某月的第几个星期几这种形式,。
所以说要写这样一个通用的dst方法估计能把人气死,于是我只好找来了pytz这个第三方库。(注意不要去sourceforge下载,那个版本4年没更新了,有严重bug。)

这个pytz的文档初看起来很简单,用pytz.country_timezones(‘国家代码’)可以拿到这个国家的时区名列表,而用pytz.timezone(‘时区名’)就能获取一个tzinfo对象。
例如取中国的第一个时区来生成datetime对象:

1234567 >>> from datetime import datetime>>> import pytz>>> tz = pytz.timezone(pytz.country_timezones(\’cn\’)[0])>>> tz >>> datetime.now(tz)datetime.datetime(2010, 12, 14, 19, 26, 12, 656000, tzinfo=)

可是它有个很奇怪的陷阱,注意看那个tz,它实际上是+8:06:00,比北京时间快了6分钟。更扯淡的是,中国的时区里找不到北京时间,只能拿上海时间来凑数…
平时使用时可能没什么问题,但是构造datetime对象,或调用replace方法时就会莫名其妙地差6分钟了:

12345678910111213 >>> dt = datetime.now(tz)>>> dtdatetime.datetime(2010, 12, 14, 19, 32, 23, 281000, tzinfo=)>>> dt2 = datetime(2010, 12, 14, 19, 32, 23, 281000, tzinfo=tz)>>> dt2datetime.datetime(2010, 12, 14, 19, 32, 23, 281000, tzinfo=)>>> dt == dt2False>>> dt dt2datetime.timedelta(0, 360)>>> dt.tzinfo >>> dt2.tzinfo

用同一个tz生成的datetime对象,居然会出现不相等的结果,而且连它们的tzinfo都不一样…

要解决这个问题,最简单的方法就是使用台北(Asia/Taipei)时间,它正好是+08:00。不过这样治标不治本,毕竟还有那么多不是整点的时区,不可能一一去找替代时区。
实际上limodou在《关于pytz的一些记录(续)》这篇文章中也提到了这个问题,他指出只要构造时生成offset-naive类型的datetime对象,再用tz.localize(dt)就能生成正确的时间了:

123456 >>> dt3 = datetime(2010, 12, 14, 19, 32, 23, 281000)>>> dt3 = tz.localize(dt3)>>> dt3datetime.datetime(2010, 12, 14, 19, 32, 23, 281000, tzinfo=)>>> dt == dt3True

最后就是关键的处理夏时令的代码,需要用到normalize方法:

1234567891011121314 >>> eastern = pytz.timezone(\’US/Eastern\’)>>> dt4 = datetime(2002, 10, 27, 1, 0)>>> dt4 = eastern.localize(dt4)>>> print dt420021027 01:00:0005:00>>> dt5 = dt4 timedelta(minutes=10)>>> print dt520021027 00:50:0005:00>>> dt6 = eastern.normalize(dt5)>>> print dt620021027 01:50:0004:00>>> dt5.tzinfo >>> dt6.tzinfo

可以看到,normalize以后,就能正确处理夏时令的变更了。


相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...