PEP 431 – 时区支持改进
- 作者:
- Lennart Regebro <regebro at gmail.com>
- BDFL 委托:
- Barry Warsaw <barry at python.org>
- 状态:
- 已取代
- 类型:
- 标准跟踪
- 创建日期:
- 2012年12月11日
- 发布历史:
- 2012年12月11日,2012年12月28日,2013年1月28日
- 取代者:
- 615
摘要
本 PEP 提议在 Python 标准库中实现具体的时区支持,并改进时区 API 以处理夏令时 (DST) 变更期间的模糊时间规范。
撤回
经过长时间的讨论,我发现我原以为是 datetime 实现中的问题,实际上是有意为之。这些问题包括在进行日期时间算术时完全忽略 DST 转换。这使得本 PEP 中的 is_dst 标志变得毫无意义,因为它们没有有用的功能。datetime 在设计上不区分模糊日期时间,也永远不会。
因此,我撤回本 PEP。
更新:PEP 615“标准库中对 IANA 时区数据库的支持”已将 zoneinfo 模块添加到 Python 3.9 并取代了本 PEP。
提案
具体的时区支持
Python 中的时区支持在标准库中没有具体的实现,除了支持固定偏移的 tzinfo 基类。要正确支持时区,您需要包含一个包含所有时区(包括当前和历史时区)以及夏令时变更的数据库。但是此类信息会频繁更改,因此即使我们在 Python 版本中包含最新信息,该信息也将在几个月后过时。
因此,时区支持只能通过两个第三方模块获得:pytz 和 dateutil,两者都包含并封装了“zoneinfo”数据库。这个数据库,也称为“tz”或“The Olsen database”,是事实上的时区标准数据库,它包含在大多数 Unix 和类 Unix 操作系统中,包括 OS X。
这为我们提供了将支持 zoneinfo 数据的代码包含在标准库中的机会,但默认情况下使用操作系统的副本数据,该数据通常会通过操作系统或发行版的更新机制保持最新。
对于不包含 zoneinfo 数据库的操作系统(例如 Windows),Python 源代码分发将包含 zoneinfo 数据库的副本,并且包含最新 zoneinfo 数据库的分发也将在 Python Package Index 上提供,因此可以使用 Python 打包工具(例如 easy_install 或 pip)轻松安装。这也可以在不再接收更新的 Unix 系统上完成,因为这些系统具有过时数据库。
通过这种机制,Python 将在任何平台上的标准库中拥有全面的时区支持,并且一个简单的包安装将为那些未包含 zoneinfo 数据库的平台(例如 Windows)或不再提供操作系统更新的平台提供更新的时区数据库。
时区支持将通过将 datetime 模块转换为包,并在 datetime 中添加基于 Stuart Bishop 的 pytz 模块的时区支持来实现。
获取本地时区
在 Unix 系统上,没有标准方法可以找到正在使用的时区名称。所有可用的信息都是时区缩写,例如 EST 和 PDT,但这些缩写中的许多都是模糊的,因此您不能依赖它们来确定您所在的地区。
然而,有一个标准可以找到已编译的时区信息,因为它位于 /etc/localtime 中。因此,即使您不知道时区名称,也可以创建一个具有正确时区信息的本地时区对象。datetime 中应该提供一个函数来返回本地时区。
对此的支持将通过将 Lennart Regebro 的 tzlocal 模块集成到新的 datetime 模块中来实现。
对于 Windows,它将查找本地 Windows 时区名称,并使用 Unicode 联盟提供的 Windows 时区名称和 zoneinfo 时区名称之间的映射将其转换为 zoneinfo 时区。
映射应在每个主要版本或错误修复版本发布之前更新,用于此的脚本将位于 Tools/ 目录中。
模糊时间
当从夏令时 (DST) 转换时,时钟会拨回一小时。这意味着该小时内的时间会发生两次,一次是 DST,另一次是没有 DST。同样,当转换为夏令时时,会少一小时。
当前的 API 无法区分 DST 变更期间的两个模糊时间。例如,在斯德哥尔摩,2012-11-28 02:00:00 的时间会发生两次,一次在 UTC 2012-11-28 00:00:00,另一次在 2012-11-28 01:00:00。
当前时区 API 无法消除这种歧义,因此不清楚应该返回哪个时间
# This could be either 00:00 or 01:00 UTC:
>>> dt = datetime(2012, 10, 28, 2, 0, tzinfo=zoneinfo('Europe/Stockholm'))
# But we can not specify which:
>>> dt.astimezone(zoneinfo('UTC'))
datetime.datetime(2012, 10, 28, 1, 0, tzinfo=<UTC>)
pytz 通过向 tzinfo 对象的几个方法添加 is_dst 参数来解决这个问题,以便在需要时消除时间的歧义。
本 PEP 提议将这些 is_dst 参数添加到 datetime API 的相关方法中,从而将此功能直接添加到 datetime 中。这可能是本 PEP 最难的部分,因为它涉及使用此功能更新 datetime 库的 C 版本,因为这涉及编写新代码,而不仅仅是重组现有的外部库。
实现API
zoneinfo 数据库
最新版本的 zoneinfo 数据库应存在于 Python 源代码控制系统的 Lib/tzdata 目录中。此数据库副本应在每个 Python 功能和错误修复版本发布之前更新,但对于处于仅安全修复模式的 Python 版本发布则不更新。
更新数据库的脚本将位于 Tools/ 中,发布说明将更新以包含此更新。
将实现新的配置选项 --enable-internal-timezone-database 和 --disable-internal-timezone-database,以在从源代码安装时启用和禁用此数据库的安装。源代码安装将默认安装它们。
对于具有系统提供的 zoneinfo 数据库的系统的二进制安装程序,可以跳过安装包含的数据库,因为它永远不会用于这些平台。对于其他平台,例如 Windows,二进制安装程序必须安装包含的数据库。
datetime 模块的更改
新时区支持的公共 API 包含一个新类、一个新函数、一个新异常和四个新集合。除此之外,datetime 对象上的几个方法获得了新的 is_dst 参数。
新类 dsttimezone
此类提供了 tzinfo 基类的具体实现,该基类实现了 DST 支持。
新函数 zoneinfo(name=None, db_path=None)
此函数接受一个名称字符串,该字符串必须是指定有效 zoneinfo 时区的字符串,即“US/Eastern”、“Europe/Warsaw”或“Etc/GMT”。如果未给出,将查找本地时区。如果给出了无效的时区名称,或者无法检索本地时区,则该函数将引发 UnknownTimeZoneError。
该函数还接受 zoneinfo 数据库位置的可选路径,该路径应使用。如果未指定,该函数将按以下顺序查找数据库
- 检查
tzdata-update模块是否已安装,然后使用该数据库。 - 如果存在,使用
/usr/share/zoneinfo中的数据库。 - 使用
Lib/tzdata中 Python 提供的数据库。
如果未找到数据库,将引发 UnknownTimeZoneError 或其子类,并显示一条消息,解释找不到 zoneinfo 数据库,但可以使用 tzdata-update 包安装一个。
新参数 is_dst
在 DST 转换期间,为了处理时间模糊性,向几个方法添加了一个新的 is_dst 参数。
tzinfo.utcoffset(dt, is_dst=False)tzinfo.dst(dt, is_dst=False)tzinfo.tzname(dt, is_dst=False)datetime.astimezone(tz, is_dst=False)
is_dst 参数可以是 False (默认值)、True 或 None。
False 将指定给定的 datetime 应解释为未发生在夏令时期间,即指定的时间在 DST 转换之后。这是默认值,以保留现有行为。
True 将指定给定的 datetime 应解释为发生在夏令时期间,即指定的时间在 DST 转换之前。
如果指定的时间在 DST 转换期间,则 None 将引发 AmbiguousTimeError 异常。如果指定的时间在转换为 DST 期间的“缺失时间”期间,它还将引发 NonExistentTimeError。
新异常
UnknownTimeZoneError此异常是 KeyError 的子类,在给出无法找到的时区规范时引发
>>> datetime.zoneinfo('Europe/New_York') Traceback (most recent call last): ... UnknownTimeZoneError: There is no time zone called 'Europe/New_York'
InvalidTimeError此异常作为
AmbiguousTimeError和NonExistentTimeError的基类,使您能够分别捕获这两个异常。它将是 ValueError 的子类,因此您可以将其与 2011 年 2 月 29 日等输入一起捕获。AmbiguousTimeError在将
is_dst设置为 None 时,如果给定的日期时间规范不明确,则会引发此异常>>> datetime(2012, 11, 28, 2, 0, tzinfo=zoneinfo('Europe/Stockholm'), is_dst=None) >>> Traceback (most recent call last): ... AmbiguousTimeError: 2012-10-28 02:00:00 is ambiguous in time zone Europe/Stockholm
NonExistentTimeError在将
is_dst设置为 None 时,如果给定的日期时间规范由于夏令时而不存在,则会引发此异常>>> datetime(2012, 3, 25, 2, 0, tzinfo=zoneinfo('Europe/Stockholm'), is_dst=None) >>> Traceback (most recent call last): ... NonExistentTimeError: 2012-03-25 02:00:00 does not exist in time zone Europe/Stockholm
新集合
all_timezones是可用的时区名称的详尽列表,按字母顺序排列。common_timezones是有用、当前的时区列表,按字母顺序排列。
tzdata-update 包
zoneinfo 数据库将被打包,以便通过 easy_install/pip/buildout 轻松安装。此包不会安装任何 Python 代码,也不会包含任何 Python 代码,除非是安装所需的代码。
它将使用与内部数据库相同的工具保持更新,但在 zoneinfo 数据库更新时发布,并使用相同的版本架构。
与 pytz API 的区别
pytz具有localize()和normalize()函数,用于解决tzinfo没有 is_dst 的问题。当is_dst直接在datetime.tzinfo中实现时,它们就不再需要了。timezone()函数被称为zoneinfo(),以避免与 Python 3.2 中引入的timezone类冲突。- 如果没有参数调用
zoneinfo(),它将返回本地时区。 - 类
pytz.StaticTzInfo用于为静态时区提供is_dst支持。当is_dst支持包含在datetime.tzinfo中时,它就不再需要了。 InvalidTimeError是ValueError的子类。
资源
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0431.rst
最后修改时间:2025-02-01 08:59:27 GMT