世界时区定义,现代和历史
项目描述
简介
pytz 将Olson时区数据库引入Python。该库允许使用Python 2.4或更高版本进行准确和跨平台的时区计算。它还解决了夏令时结束时的模糊时间问题,您可以在Python库参考(datetime.tzinfo)中了解更多信息。
几乎所有的Olson时区都受到支持。
安装
此包可以使用 pip 安装,也可以使用标准 Python distutils 从 tarball 安装。
如果您使用 pip 安装,您不需要下载任何内容,因为最新的版本将为您从 PyPI 下载。
pip install pytz
如果您从 tarball 安装,请以管理员用户身份运行以下命令
python setup.py install
pytz 企业版
作为 Tidelift 订阅的一部分提供。
pytz 维护者以及成千上万的其他包维护者正在与 Tidelift 合作,为构建应用程序时使用的开源依赖项提供商业支持和维护。节省时间,降低风险,并提高代码质量,同时支付您使用的确切依赖项的维护者。了解更多信息。点击这里。
示例 & 使用
本地化时间和日期算术
>>> from datetime import datetime, timedelta >>> from pytz import timezone >>> import pytz >>> utc = pytz.utc >>> utc.zone 'UTC' >>> eastern = timezone('US/Eastern') >>> eastern.zone 'US/Eastern' >>> amsterdam = timezone('Europe/Amsterdam') >>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'
此库仅支持两种构建本地化时间的方法。第一种是使用 pytz 库提供的 localize() 方法。这用于本地化一个无知的 datetime(没有时区信息的 datetime)
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0)) >>> print(loc_dt.strftime(fmt)) 2002-10-27 06:00:00 EST-0500
构建本地化时间的第二种方法是使用标准的 astimezone() 方法转换现有的本地化时间
>>> ams_dt = loc_dt.astimezone(amsterdam) >>> ams_dt.strftime(fmt) '2002-10-27 12:00:00 CET+0100'
不幸的是,对于许多时区,使用标准 datetime 构造函数的 tzinfo 参数“不起作用”
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) # /!\ Does not work this way! '2002-10-27 12:00:00 LMT+0018'
对于没有夏令时转换的时区,例如 UTC,这是安全的
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) # /!\ Not recommended except for UTC '2002-10-27 12:00:00 UTC+0000'
处理时间的首选方式是始终在 UTC 下工作,仅在生成供人类阅读的输出时转换为本地时间。
>>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) >>> loc_dt = utc_dt.astimezone(eastern) >>> loc_dt.strftime(fmt) '2002-10-27 01:00:00 EST-0500'
此库还允许您使用本地时间执行日期算术,尽管这比在 UTC 下工作更复杂,因为您需要使用 normalize() 方法来处理夏令时和其他时区转换。在这个示例中,loc_dt 设置为美国东部时区夏令时结束的时刻。
>>> before = loc_dt - timedelta(minutes=10) >>> before.strftime(fmt) '2002-10-27 00:50:00 EST-0500' >>> eastern.normalize(before).strftime(fmt) '2002-10-27 01:50:00 EDT-0400' >>> after = eastern.normalize(before + timedelta(minutes=20)) >>> after.strftime(fmt) '2002-10-27 01:10:00 EST-0500'
创建本地时间也很棘手,这也是不建议使用本地时间的原因之一。不幸的是,您不能在构建 datetime 时简单地传递一个 tzinfo 参数(请参阅下一节以获取更多详细信息)
>>> dt = datetime(2002, 10, 27, 1, 30, 0) >>> dt1 = eastern.localize(dt, is_dst=True) >>> dt1.strftime(fmt) '2002-10-27 01:30:00 EDT-0400' >>> dt2 = eastern.localize(dt, is_dst=False) >>> dt2.strftime(fmt) '2002-10-27 01:30:00 EST-0500'
使用标准 astimezone 方法在时区之间转换要容易得多。
>>> utc_dt = datetime.fromtimestamp(1143408899, tz=utc) >>> utc_dt.strftime(fmt) '2006-03-26 21:34:59 UTC+0000' >>> au_tz = timezone('Australia/Sydney') >>> au_dt = utc_dt.astimezone(au_tz) >>> au_dt.strftime(fmt) '2006-03-27 08:34:59 AEDT+1100' >>> utc_dt2 = au_dt.astimezone(utc) >>> utc_dt2.strftime(fmt) '2006-03-26 21:34:59 UTC+0000' >>> utc_dt == utc_dt2 True
在处理时区转换的 UTC 方面,您可以采取捷径。在没有夏令时转换要处理的情况下,normalize() 和 localize() 真的并不是必需的。
>>> utc_dt = datetime.fromtimestamp(1143408899, tz=utc) >>> utc_dt.strftime(fmt) '2006-03-26 21:34:59 UTC+0000' >>> au_tz = timezone('Australia/Sydney') >>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz)) >>> au_dt.strftime(fmt) '2006-03-27 08:34:59 AEDT+1100' >>> utc_dt2 = au_dt.astimezone(utc) >>> utc_dt2.strftime(fmt) '2006-03-26 21:34:59 UTC+0000'
tzinfo API
由 timezone() 函数返回的 tzinfo 实例已扩展以处理模糊时间,通过向 utcoffset()、dst() && tzname() 方法添加 is_dst 参数来实现。
>>> tz = timezone('America/St_Johns')
>>> normal = datetime(2009, 9, 1) >>> ambiguous = datetime(2009, 10, 31, 23, 30)
对于大多数时间戳,is_dst 参数会被忽略。它仅在夏令时过渡的模糊期间使用,以解决这种模糊性。
>>> print(tz.utcoffset(normal, is_dst=True)) -1 day, 21:30:00 >>> print(tz.dst(normal, is_dst=True)) 1:00:00 >>> tz.tzname(normal, is_dst=True) 'NDT'
>>> print(tz.utcoffset(ambiguous, is_dst=True)) -1 day, 21:30:00 >>> print(tz.dst(ambiguous, is_dst=True)) 1:00:00 >>> tz.tzname(ambiguous, is_dst=True) 'NDT'
>>> print(tz.utcoffset(normal, is_dst=False)) -1 day, 21:30:00 >>> tz.dst(normal, is_dst=False).seconds 3600 >>> tz.tzname(normal, is_dst=False) 'NDT'
>>> print(tz.utcoffset(ambiguous, is_dst=False)) -1 day, 20:30:00 >>> tz.dst(ambiguous, is_dst=False) datetime.timedelta(0) >>> tz.tzname(ambiguous, is_dst=False) 'NST'
如果未指定 is_dst,模糊的时间戳将引发 pytz.exceptions.AmbiguousTimeError 异常。
>>> print(tz.utcoffset(normal)) -1 day, 21:30:00 >>> print(tz.dst(normal)) 1:00:00 >>> tz.tzname(normal) 'NDT'
>>> import pytz.exceptions >>> try: ... tz.utcoffset(ambiguous) ... except pytz.exceptions.AmbiguousTimeError: ... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00 >>> try: ... tz.dst(ambiguous) ... except pytz.exceptions.AmbiguousTimeError: ... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00 >>> try: ... tz.tzname(ambiguous) ... except pytz.exceptions.AmbiguousTimeError: ... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous) pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00
Localtime 的问题
我们必须解决的主要问题是某些日期时间在一年中可能发生两次。例如,在美国/东部时区中,十月的最后一个星期日上午发生以下情况
01:00 EDT 出现
一小时后,时钟回拨一小时,而不是 2:00am,时钟再次变为 01:00(这次是 01:00 EST)
实际上,01:00 和 02:00 之间的每一瞬间都发生了两次。这意味着如果您尝试使用标准 datetime 语法在 'US/Eastern' 时区创建时间,您无法指定是白天节约时间的结束之前还是之后。使用 pytz 自定义语法,您最多只能做出一个有根据的猜测
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 1, 30, 00)) >>> loc_dt.strftime(fmt) '2002-10-27 01:30:00 EST-0500'
正如您所看到的,系统已经为您选择了一个,有 50% 的可能性会错一小时。对于某些应用程序来说,这无关紧要。然而,如果您正在尝试与不同时区的人安排会议或分析日志文件,这是不可接受的。
最佳且最简单的方法是坚持使用 UTC。pytz 包通过包括基于 Python 文档中标准 Python 参考实现的特殊 UTC 实现来鼓励使用 UTC 进行内部时区表示。
UTC 时区反序列化时是相同的实例,并且序列化时的大小小于其他 pytz tzinfo 实例。UTC 实现可以通过 pytz.utc、pytz.UTC 或 pytz.timezone('UTC') 获取。
>>> import pickle, pytz >>> dt = datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc) >>> naive = dt.replace(tzinfo=None) >>> p = pickle.dumps(dt, 1) >>> naive_p = pickle.dumps(naive, 1) >>> len(p) - len(naive_p) 17 >>> new = pickle.loads(p) >>> new == dt True >>> new is dt False >>> new.tzinfo is dt.tzinfo True >>> pytz.utc is pytz.UTC is pytz.timezone('UTC') True
请注意,一些其他时区通常被认为是相同的(GMT、格林威治、通用等)。UTC 的定义与这些其他时区不同,它们并不等效。因此,在 Python 中它们不会比较相同。
>>> utc == pytz.timezone('GMT') False
请参阅下文中的 什么是 UTC 部分。
如果您坚持使用本地时间,此库提供了一种构造它们的明确方式
>>> loc_dt = datetime(2002, 10, 27, 1, 30, 00) >>> est_dt = eastern.localize(loc_dt, is_dst=True) >>> edt_dt = eastern.localize(loc_dt, is_dst=False) >>> print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt)) 2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500
如果您将 None 作为 is_dst 标志传递给 localize(),pytz 将拒绝猜测,并在您尝试构建模糊或不存在的时间时引发异常。
例如,2002 年 10 月 27 日凌晨 1:30 在美国/东部时区发生了两次,当时时钟在白天节约时间的结束时回拨
>>> dt = datetime(2002, 10, 27, 1, 30, 00) >>> try: ... eastern.localize(dt, is_dst=None) ... except pytz.exceptions.AmbiguousTimeError: ... print('pytz.exceptions.AmbiguousTimeError: %s' % dt) pytz.exceptions.AmbiguousTimeError: 2002-10-27 01:30:00
同样,2002 年 4 月 7 日凌晨 2:30 在美国/东部时区根本从未发生过,因为时钟在 2:00am 时前进了,跳过了整个小时
>>> dt = datetime(2002, 4, 7, 2, 30, 00) >>> try: ... eastern.localize(dt, is_dst=None) ... except pytz.exceptions.NonExistentTimeError: ... print('pytz.exceptions.NonExistentTimeError: %s' % dt) pytz.exceptions.NonExistentTimeError: 2002-04-07 02:30:00
这两个异常共享一个共同的基类,以便于错误处理
>>> isinstance(pytz.AmbiguousTimeError(), pytz.InvalidTimeError) True >>> isinstance(pytz.NonExistentTimeError(), pytz.InvalidTimeError) True
有一种特殊情况是,一些国家在没有夏令时切换的情况下更改他们的时区定义。例如,1915 年华沙从华沙时间切换到中欧时间,没有白天节约时间的转换。所以,1915 年 8 月 5 日午夜时分,时钟回拨了 24 分钟,创建了一个无法不提及时区缩写或实际 UTC 偏移量就无法指定的模糊时间段。在这种情况下,午夜发生了两次,这两个时间都不是在白天节约时间期间。pytz 通过将转换前的模糊时间段视为白天节约时间,将转换后的模糊时间段视为标准时间来处理这种转换。
>>> warsaw = pytz.timezone('Europe/Warsaw') >>> amb_dt1 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=True) >>> amb_dt1.strftime(fmt) '1915-08-04 23:59:59 WMT+0124' >>> amb_dt2 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=False) >>> amb_dt2.strftime(fmt) '1915-08-04 23:59:59 CET+0100' >>> switch_dt = warsaw.localize(datetime(1915, 8, 5, 00, 00, 00), is_dst=False) >>> switch_dt.strftime(fmt) '1915-08-05 00:00:00 CET+0100' >>> str(switch_dt - amb_dt1) '0:24:01' >>> str(switch_dt - amb_dt2) '0:00:01'
在模糊时间段创建时间最佳方式是从 UTC 等其他时区转换
>>> utc_dt = datetime(1915, 8, 4, 22, 36, tzinfo=pytz.utc) >>> utc_dt.astimezone(warsaw).strftime(fmt) '1915-08-04 23:36:00 CET+0100'
Python 处理所有这些模糊性的标准方式是不处理它们,如下例所示,使用 Python 文档中的美国/东部时区定义(注意,此实现仅适用于 1987 年至 2006 年之间的日期 - 仅用于测试!)
>>> from pytz.reference import Eastern # pytz.reference only for tests >>> dt = datetime(2002, 10, 27, 0, 30, tzinfo=Eastern) >>> str(dt) '2002-10-27 00:30:00-04:00' >>> str(dt + timedelta(hours=1)) '2002-10-27 01:30:00-05:00' >>> str(dt + timedelta(hours=2)) '2002-10-27 02:30:00-05:00' >>> str(dt + timedelta(hours=3)) '2002-10-27 03:30:00-05:00'
注意前两个结果吗?乍一看,你可能认为它们是正确的,但考虑到UTC偏移量,你会发现它们实际上相差两个小时,而不是我们要求的1小时。
>>> from pytz.reference import UTC # pytz.reference only for tests >>> str(dt.astimezone(UTC)) '2002-10-27 04:30:00+00:00' >>> str((dt + timedelta(hours=1)).astimezone(UTC)) '2002-10-27 06:30:00+00:00'
国家信息
提供了一种机制来访问特定国家的常用时区,使用ISO 3166国家代码进行查找。它返回一个字符串列表,可以使用pytz.timezone()来检索相关的tzinfo实例。
>>> print(' '.join(pytz.country_timezones['nz'])) Pacific/Auckland Pacific/Chatham
Olson数据库附带了一个ISO 3166国家代码到英文国家名称的映射,pytz将其作为字典公开。
>>> print(pytz.country_names['nz']) New Zealand
什么是UTC
‘UTC’是协调世界时(Coordinated Universal Time)。它是格林威治标准时间(GMT)和各种通用时间定义的后继者,但与它们不同。UTC现在是全球时钟和时间测量的标准。
所有其他时区都是相对于UTC定义的,包括像UTC+0800这样的偏移量——从UTC中添加或减去的小时数,以推导出当地时间。UTC中没有夏令时,这使得它是一个有用的时区,可以在不担心夏令时转换的混淆和歧义的情况下执行日期算术,或者当你的国家改变时区,或者移动计算机穿越多个时区时。
辅助工具
提供了两个时区列表。
all_timezones是可用的时区名称的详尽列表。
>>> from pytz import all_timezones >>> len(all_timezones) >= 500 True >>> 'Etc/Greenwich' in all_timezones True
common_timezones是有用、当前时区列表。它不包含已弃用的区域或历史区域,除了少数我认为常用的情况,例如US/Eastern(如果你认为其他时区应该包括在这里,请提交一个错误报告)。它也是一个字符串序列。
>>> from pytz import common_timezones >>> len(common_timezones) < len(all_timezones) True >>> 'Etc/Greenwich' in common_timezones False >>> 'Australia/Melbourne' in common_timezones True >>> 'US/Eastern' in common_timezones True >>> 'Canada/Eastern' in common_timezones True >>> 'Australia/Yancowinna' in all_timezones True >>> 'Australia/Yancowinna' in common_timezones False
common_timezones和all_timezones都按字母顺序排序
>>> common_timezones_dupe = common_timezones[:] >>> common_timezones_dupe.sort() >>> common_timezones == common_timezones_dupe True >>> all_timezones_dupe = all_timezones[:] >>> all_timezones_dupe.sort() >>> all_timezones == all_timezones_dupe True
all_timezones和common_timezones也作为集合提供。
>>> from pytz import all_timezones_set, common_timezones_set >>> 'US/Eastern' in all_timezones_set True >>> 'US/Eastern' in common_timezones_set True >>> 'Australia/Victoria' in common_timezones_set False
您还可以使用country_timezones()函数检索特定国家使用的时区列表。它需要一个ISO-3166两位字母国家代码。
>>> from pytz import country_timezones >>> print(' '.join(country_timezones('ch'))) Europe/Zurich >>> print(' '.join(country_timezones('CH'))) Europe/Zurich
国际化 - i18n/l10n
Pytz是IANA数据库的接口,它使用ASCII名称。Unicode联盟的Unicode区域(CLDR)项目提供翻译。可以使用Python包如Babel和Thomas Khyn的l18n包来从Python访问这些翻译。
许可证
MIT许可。
此代码也是Zope 3的一部分,根据Zope公共许可证版本2.1(ZPL)提供。
如果需要将其包含在其他开源项目中,我很乐意重新许可此代码。
最新版本
此包将在Olson时区数据库发布后更新。最新版本可以从Python包索引下载。用于生成此分发的代码托管在GitHub上,并可通过git访问。
git clone https://github.com/stub42/pytz.git
错误、功能请求和补丁
错误应报告在GitHub上。功能请求不太可能被考虑,而是将努力投入到现在已内置到Python中或与其一起工作的包中。
安全问题
有关安全问题的报告可通过Tidelift提交。
问题和限制
此项目处于维护模式。使用Python 3.9或更高版本的项目最好使用Python核心中现在包含的时区功能以及与其一起工作的包,如tzdata。
从UTC偏移量四舍五入到最近的整分钟,因此1937年之前的欧洲/阿姆斯特丹时区可能最多有30秒的误差。这是Python datetime库的一个限制。
如果您认为某个时区的定义不正确,我可能无法修复它。pytz是Olson时区数据库的直接翻译,时区定义的更改需要在这个源上进行。如果您发现错误,应向时区邮件列表报告,该列表可通过http://www.iana.org/time-zones链接访问。
进一步阅读
关于时区的更多信息,您可能想知道: https://data.iana.org/time-zones/tz-link.html
联系
Stuart Bishop <stuart@stuartbishop.net>
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。