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-01-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”或“Olsen 数据库”,是时区的实际标准时区数据库,它包含在大多数 Unix 和类 Unix 操作系统中,包括 OS X。
这使我们有机会将支持 zoneinfo 数据的代码包含在标准库中,但默认情况下使用操作系统的数据库副本,这些副本通常会由操作系统的更新机制或发行版保持更新。
对于操作系统(例如 Windows)未包含 zoneinfo 数据库的用户,Python 源代码分发版将包含 zoneinfo 数据库的副本,并且包含最新 zoneinfo 数据库的分发版也将可在 Python 包索引中找到,因此可以使用 Python 打包工具(如 easy_install
或 pip
)轻松安装。这也可以在不再接收更新且因此数据库过时的 Unix 系统上完成。
通过这种机制,Python 在任何平台上都将在标准库中拥有完整的时区支持,并且简单的软件包安装将在那些未包含 zoneinfo 数据库的平台(如 Windows)或不再提供操作系统更新的平台上提供更新的时区数据库。
时区支持将通过将 datetime
模块转换为包并在基于 Stuart Bishop 的 pytz
模块的 datetime
中添加时区支持来实现。
获取本地时区
在 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
在几个方法中添加了一个新的 `is_dst` 参数来处理 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` 将指定给定日期时间应解释为未在夏令时期间发生,即指定的时间在从 DST 更改之后。这是默认值以保留现有行为。
`True` 将指定给定日期时间应解释为在夏令时期间发生,即指定的时间在从 DST 更改之前。
`None` 如果指定的时间在 DST 更改期间,则会引发 `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