跳转到主要内容

世界时区定义,现代和历史

项目描述

作者:

Stuart Bishop <stuart@stuartbishop.net>

简介

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_timezonesall_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_timezonescommon_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

新版本公告将在Launchpad上发布,并托管在该处的Atom源

错误、功能请求和补丁

错误应报告在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>

项目详情


发布历史 发布通知 | RSS订阅

下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

pytz-2024.2.tar.gz (319.7 kB 查看哈希值)

上传时间

构建分布

pytz-2024.2-py2.py3-none-any.whl (508.0 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下组织支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面