PEP 495 – 本地时间消歧
- 作者:
- Alexander Belopolsky <alexander.belopolsky at gmail.com>, Tim Peters <tim.peters at gmail.com>
- 讨论至:
- Datetime-SIG 列表
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2015年8月2日
- Python 版本:
- 3.6
- 决议:
- Datetime-SIG 消息
摘要
本 PEP 为 datetime.time 和 datetime.datetime 类的实例添加了一个新属性 fold,该属性可用于区分本地时间相同的两个时刻。 fold 属性的允许值为 0 和 1,其中 0 对应于模糊本地时间中较早的读取,1 对应于较晚的读取。
基本原理
在世界大部分地区,曾有并将有本地时钟拨回的时间。[1] 在这些时候,会引入本地时钟在同一天内两次显示相同时间的时间间隔。在这种情况下,本地时钟显示的信息(或存储在 Python datetime 实例中)不足以识别特定时刻。拟议的解决方案是为 datetime 实例添加一个取值 0 和 1 的属性,用于枚举两个模糊时间。
术语
当钟表拨回时,我们说时间中产生了一个**折叠 (fold)** [2]。当钟表拨快时,产生了一个**间隙 (gap)**。落在折叠中的本地时间称为**模糊 (ambiguous)**。落在间隙中的本地时间称为**缺失 (missing)**。
提案
“fold” 属性
我们建议为 datetime.time 和 datetime.datetime 类的实例添加一个名为 fold 的属性。除了表示模糊情况下时间上(按时间顺序)第二个时刻的实例外,所有实例的此属性值应为 0。对于这些实例,该值将为 1。[3]
fold=1 的实例被称为表示无效时间(或简称无效),但用户不会被阻止通过将 fold=1 传递给构造函数或 replace() 方法来创建无效实例。这类似于目前春季拨快间隙中实例的情况。此类实例不表示任何有效时间,但构造函数和 replace() 方法都不会检查它们产生的实例是否有效。此外,本 PEP 规定了在给定无效实例时各种函数的行为方式。受影响的 API
属性
datetime.time 和 datetime.datetime 类的实例将获得一个新属性 fold,其可能值为 0 和 1。
构造函数
datetime.time 和 datetime.datetime 类的 __new__ 方法将获得一个名为 fold 的新关键字-only参数,其默认值为 0。 fold 参数的值将用于初始化返回实例中 fold 属性的值。
方法
datetime.time 和 datetime.datetime 类的 replace() 方法将获得一个名为 fold 的新关键字-only参数。它的行为将与其他 replace() 参数类似:如果指定了 fold 参数并给定值 0 或 1,则 replace() 返回的新实例的 fold 属性将设置为该值。在 CPython 中,任何非整数的 fold 值都将引发 TypeError,但其他实现可能允许值 None 的行为与未给定 fold 时相同。[4] (这是为了适应此方法在 Python 实现中对其他位置的 None 参数的不同处理方式;它并非旨在为未来对 fold=None 的替代解释留下空间。)如果未指定 fold 参数,则 fold 属性的原始值将复制到结果中。
None 表示 replace() 中所有其他属性的“不更改现有属性”。C-API
将定义访问宏以从 PyDateTime_DateTime 和 PyDateTime_Time 对象中提取 fold 的值。
int PyDateTime_DATE_GET_FOLD(PyDateTime_DateTime *o)
将 fold 的值作为 C int 返回。
int PyDateTime_TIME_GET_FOLD(PyDateTime_Time *o)
将 fold 的值作为 C int 返回。
将定义新的构造函数,它们将接受一个附加参数以指定所创建实例中 fold 的值。
PyObject* PyDateTime_FromDateAndTimeAndFold(
int year, int month, int day, int hour, int minute,
int second, int usecond, int fold)
返回一个具有指定年、月、日、时、分、秒、微秒和 fold 的 datetime.datetime 对象。
PyObject* PyTime_FromTimeAndFold(
int hour, int minute, int second, int usecond, int fold)
返回一个具有指定小时、分钟、秒、微秒和 fold 的 datetime.time 对象。
受影响的行为
现在几点了?
在系统本地时间折叠中返回两个模糊时间中的第二个时,不带参数调用的 datetime.now() 方法将设置 fold=1。当使用 tzinfo 参数调用时,fold 的值将由 tzinfo.fromutc() 实现确定。当 datetime.timezone 类的实例(stdlib 的固定偏移量 tzinfo 子类,例如 datetime.timezone.utc)作为 tzinfo 传递时,返回的 datetime 实例将始终具有 fold=0。 datetime.utcnow() 方法不受影响。
从“天真”时间转换为“感知”时间
提出了一项新功能,以促进从“天真”datetime 实例到“感知”datetime 实例的转换。
astimezone() 方法现在将对“天真”的 self 起作用。在这种情况下,将假定系统本地时区,并使用 fold 标志来确定在模糊情况下哪个本地时区生效。
例如,在一个设置为美国/东部时区的系统上
>>> dt = datetime(2014, 11, 2, 1, 30)
>>> dt.astimezone().strftime('%D %T %Z%z')
'11/02/14 01:30:00 EDT-0400'
>>> dt.replace(fold=1).astimezone().strftime('%D %T %Z%z')
'11/02/14 01:30:00 EST-0500'
这意味着 datetime.now(tz) 完全等同于 datetime.now().astimezone(tz) (假设 tz 是一个后-PEP tzinfo 实现的实例,即一个正确处理和设置 fold 的实例)。
从 POSIX 纪元秒转换
datetime.datetime 的 fromtimestamp() 静态方法将在返回的对象中适当设置 fold 属性。
例如,在一个设置为美国/东部时区的系统上
>>> datetime.fromtimestamp(1414906200)
datetime.datetime(2014, 11, 2, 1, 30)
>>> datetime.fromtimestamp(1414906200 + 3600)
datetime.datetime(2014, 11, 2, 1, 30, fold=1)
转换为 POSIX 纪元秒
datetime.datetime 的 timestamp() 方法将对仅其 fold 属性值不同的 datetime.datetime 实例返回不同的值,当且仅当这些实例表示模糊或缺失时间时。
当 datetime.datetime 实例 dt 表示模糊时间时,存在两个值 s0 和 s1,使得
datetime.fromtimestamp(s0) == datetime.fromtimestamp(s1) == dt
(这是因为 == 会忽略 fold 的值——参见下文。)
在这种情况下,如果 dt.fold == 0,则 dt.timestamp() 将返回 s0 和 s1 中较小的值,否则返回较大的值。
例如,在一个设置为美国/东部时区的系统上
>>> datetime(2014, 11, 2, 1, 30, fold=0).timestamp()
1414906200.0
>>> datetime(2014, 11, 2, 1, 30, fold=1).timestamp()
1414909800.0
当 datetime.datetime 实例 dt 表示缺失时间时,没有值 s 可以满足
datetime.fromtimestamp(s) == dt
但我们可以形成两个“最好知道”的 s 值,它们之间的差值是间隙的秒数大小。其中一个是 s 的值,它对应于在 UTC 偏移量始终与间隙之前偏移量相同的时区中的 dt,另一个是类似的值,但在 UTC 偏移量始终与间隙之后偏移量相同的时区中。
给定缺失的 dt 时,dt.timestamp() 返回的值,如果 dt.fold == 0,则为两个“最好知道”值中较大的一个,否则为较小的一个。(这不是笔误——它有意与模糊时间的规则相反。)
例如,在一个设置为美国/东部时区的系统上
>>> datetime(2015, 3, 8, 2, 30, fold=0).timestamp()
1425799800.0
>>> datetime(2015, 3, 8, 2, 30, fold=1).timestamp()
1425796200.0
感知 datetime 实例
预-PEP tzinfo 实现的用户将不会看到其感知 datetime 实例的行为有任何变化。两个这样的实例,如果仅在 fold 属性值上有所不同,除了明确访问 fold 值外,无法通过任何其他方式区分。(这是因为这些预-PEP 实现没有使用 fold 属性。)
另一方面,如果一个对象的 tzinfo 被设置为一个折叠感知实现,那么在折叠或间隙中,fold 的值将影响几个方法的结果:utcoffset()、dst()、tzname()、astimezone()、strftime()(如果格式规范中使用了“%Z”或“%z”指令)、isoformat() 和 timetuple()。
组合和拆分日期和时间
datetime.datetime.combine() 方法会将 fold 属性的值复制到生成的 datetime.datetime 实例中。
datetime.datetime.time() 方法会将 fold 属性的值复制到生成的 datetime.time 实例中。
Pickle
fold 属性的值将仅保存在使用协议版本 4(在 Python 3.4 中引入)或更高版本创建的 pickle 中。
datetime.datetime 和 datetime.time 对象的 pickle 大小不会改变。 fold 值将编码在 datetime.datetime pickle 有效载荷的第三个字节的第一个位;并编码在 datetime.time 有效载荷的第一个字节的第一个位。在 当前实现中,这些字节用于存储月份(1-12)和小时(0-23)值,第一个位始终为 0。我们选择这些字节是因为它们是当前 unpickle 代码检查的唯一字节。因此,在预-PEP Python 中加载后-PEP fold=1 pickle 将导致异常,而不是具有超出范围组件的实例。
标准库中的 tzinfo 实现
本 PEP 未提出 datetime.tzinfo 抽象类的新实现。现有的(固定偏移量)时区不引入模糊的本地时间,其 utcoffset() 实现将返回与现在相同的常量值,无论 fold 的值如何。
抽象 datetime.tzinfo 类中 fromutc() 的基本实现不会改变。它目前在标准库中没有使用,因为唯一包含的 tzinfo 实现(实现固定偏移时区的 datetime.timezone 类)覆盖了 fromutc()。保持默认实现不变的好处是,继承默认 fromutc() 的预-PEP 第三方实现不会意外受到影响。
新 tzinfo 实现指南
希望支持可变 UTC 偏移量(由于 DST 和其他原因)的具体 datetime.tzinfo 子类的实现者应遵循以下准则。
无知是福
utcoffset()、tzname() 和 dst() 方法的新实现应忽略 fold 的值,除非它们在模糊或缺失时间上被调用。
在折叠中
新的子类应该覆盖基类的 fromutc() 方法并实现它,使得在两个不同的 UTC 时间 u0 和 u1 (u0 <u1) 对应于相同本地时间 t 的所有情况下,fromutc(u0) 将返回一个 fold=0 的实例,而 fromutc(u1) 将返回一个 fold=1 的实例。在所有其他情况下,返回的实例应该具有 fold=0。
utcoffset()、tzname() 和 dst() 方法应使用 fold 属性的值来确定模糊时间 t 对应于过渡之前还是之后的时间。根据定义,在任何产生折叠的过渡之前,utcoffset() 会更大,之后会更小。 tzname() 和 dst() 返回的值可能取决于或不取决于 fold 属性的值,这取决于过渡的类型。
上图描绘了回拨转换前后 UTC 时间和本地时间之间的关系。之字形线是 fromutc() 实现的函数图。UTC 轴上与转换点相邻且大小与转换时移量相同的两个时间间隔被映射到本地轴上的相同时间间隔。 fromutc() 方法的新实现应在 self 位于 UTC 轴上黄色标记区域时将 fold 属性设置为 1。(所有时间间隔都应视为左闭右开。)
注意差距
fromutc() 方法不应产生处于间隙中的时间。
如果对落在间隙中的本地时间调用 utcoffset()、tzname() 或 dst() 方法,则如果 fold=0,应使用转换前的规则。否则,应使用转换后的规则。
上图描绘了春季拨快转换前后 UTC 时间和本地时间之间的关系。在转换时,本地时钟会向前拨快,跳过间隙中的时间。为了确定 utcoffset()、tzname() 和 dst() 的值,转换之前的线会向前延伸,以找到对应于 fold=0 的间隙中的 UTC 时间,对于 fold=1 的实例,转换之后的线会向后延伸。
过渡期的规则总结
在模糊/缺失时间上,utcoffset() 应根据下表返回值
| fold=0 | fold=1 | |
|---|---|---|
| 折叠 | 旧偏移 | 新偏移 = 旧偏移 - 差值 |
| 间隙 | 旧偏移 | 新偏移 = 旧偏移 + 差值 |
其中 oldoff(newoff)是转换之前(之后)的 UTC 偏移量,delta 是折叠或间隙的绝对大小。
请注意,fold 属性的解释在折叠和间隙情况下是一致的。在这两种情况下,fold=0 (fold=1) 都意味着使用转换前 (后) 的 fromutc() 行来查找 UTC 时间。只有在“折叠”情况下,UTC 时间 u0 和 u1 才是方程 fromutc(u) == t 的“真实”解,而在“间隙”情况下它们是“虚构”解。
DST 过渡
在 DST 开始时引入的缺失时间上,utcoffset() 和 dst() 方法返回的值应如下所示
| fold=0 | fold=1 | |
|---|---|---|
| utcoffset() | stdoff | stdoff + dstoff |
| dst() | zero | dstoff |
在 DST 结束时引入的模糊时间上,utcoffset() 和 dst() 方法返回的值应如下所示
| fold=0 | fold=1 | |
|---|---|---|
| utcoffset() | stdoff + dstoff | stdoff |
| dst() | dstoff | zero |
其中 stdoff 是标准(非 DST)偏移量,dstoff 是 DST 校正(通常 dstoff = timedelta(hours=1)),zero = timedelta(0)。
时间算术和比较运算符
在*数学*方面,他更伟大比第谷·布拉赫,或埃拉·帕特因为他,通过几何尺度,能测量出啤酒杯的大小;通过正弦和切线,直接解决,面包或黄油是否缺斤少两,并明智地告知一天中的几点时钟通过代数敲响。——塞缪尔·巴特勒的《胡迪布拉斯》
在所有与“天真”datetime 实例的操作中,fold 属性的值都将被忽略。因此,仅在 fold 值上有所不同的“天真”datetime.datetime 或 datetime.time 实例将比较为相等。需要区分此类实例的应用程序应显式检查 fold 的值,或将这些实例转换为没有模糊时间的时区(例如 UTC)。
无论 datetime 实例是感知还是天真,当 timedelta 添加到或从 datetime 实例中减去时,fold 的值都将被忽略。timedelta 添加(减去)到(从)datetime 的结果将始终将 fold 设置为 0,即使原始 datetime 实例的 fold=1。
对于 datetime 实例 t 和 s,计算差值 t - s 的方式没有变化。如果两个实例都是“天真”的,或者 t.tzinfo 与 s.tzinfo 是相同的实例(t.tzinfo is s.tzinfo 评估为 True),则 t - s 是一个 timedelta d,使得 s + d == t。如上一段所述,timedelta 加法会忽略 fold 和 tzinfo 属性,区域内或“天真”datetime 减法也是如此。
“天真”和区域内比较将忽略 fold 的值,并返回与现在相同的结果。(这是保持向后兼容性的唯一方法。如果您需要使用 fold 的“感知”区域内比较,请先将两边都转换为 UTC。)
区域间减法仍将按现有方式定义:t - s 计算为 (t - t.utcoffset()) - (s - s.utcoffset()).replace(tzinfo=t.tzinfo),但当 t.tzinfo 或 s.tzinfo 为后-PEP 时,结果将取决于 t.fold 和 s.fold 的值。[5]
s == t 但 s - u != t - u 的悖论情况。这种悖论并非真正的新问题,它们是减号运算符在区域内和区域间操作中不同重载的固有结果。例如,我们可以轻松构造具有某个可变偏移 tzinfo 的 datetime 实例 t 和 s,以及一个具有 tzinfo=timezone.utc 的 datetime u,使得 (t - u) - (s - u) != t - s。对此悖论的解释是,括号内的减号和另外两个减号实际上是三种不同的操作:区域间 datetime 减法、timedelta 减法和区域内 datetime 减法,它们各自单独具有减法的数学性质,但当它们组合在单个表达式中时则不具备。感知 datetime 等值比较
感知 datetime 比较运算符将与现在相同,结果间接受到 fold 值的影响,只要其中一个操作数的 utcoffset() 值依赖于它,但有一个例外。当区域间比较中的一个或两个操作数是其 utcoffset() 依赖于其 fold 属性值的情况时,结果为 False。[6]
形式上,当 t.tzinfo is s.tzinfo 评估为 False 时,t == s 可以定义如下。设 toutc(t, fold) 是一个函数,它接受一个感知 datetime 实例 t 并返回一个表示相同 UTC 时间的“天真”实例,假设给定 fold 的值
def toutc(t, fold):
u = t - t.replace(fold=fold).utcoffset()
return u.replace(tzinfo=None)
那么 t == s 等价于
toutc(t, fold=0) == toutc(t, fold=1) == toutc(s, fold=0) == toutc(s, fold=1)
向后和向前兼容性
此提案对不明确读取 fold 标志或使用不涉及它的 tzinfo 实现的程序影响甚微。此类程序的唯一可见变化是,与 POSIX 时间戳之间的转换现在可以正确地往返(最多到浮点舍入)。那些为了旧的不正确行为而实现了变通方法的程序可能需要修改。
旧程序产生的 Pickle 将保持完全向前兼容。只有在新版本中 pickled 的 fold=1 datetime/time 实例将无法被旧 Python 版本读取。 fold=0 (这是默认值) 实例的 Pickle 将保持不变。
问答
为什么不将新标志命名为 “isdst”?
一个非技术性的回答
- 爱丽丝:鲍勃 - 明天凌晨 01:30 举行观星派对吧!
- 鲍勃:我最初应该假定指定时间是否处于夏令时?
- 爱丽丝:啊?
- 鲍勃:爱丽丝 - 明天凌晨 01:30 举行观星派对吧!
- 爱丽丝:你知道,鲍勃,明天凌晨 01:30 会出现两次。你指的是哪个时间?
- 鲍勃:我没想到这个,但我们选第一个吧。
(同人物,一小时后)
- 鲍勃:爱丽丝 - 我的 Py-O-Clock 小工具让我明天凌晨 01:30 设置时选择 fold=0 还是 fold=1。我该怎么办?
- 爱丽丝:我从没听说过 Py-O-Clock,但我猜 fold=0 是第一个凌晨 01:30,fold=1 是第二个。
一个技术原因
尽管 time.struct_time 对象的 tm_isdst 字段可用于消除折叠中本地时间的歧义,但这种消歧的语义与本 PEP 中的提议完全不同。
tm_isdst 字段的主要问题是,如果不了解时区细节,就无法知道 tm_isdst 的适当值,而这些细节只有 tzinfo 实现才能获得。因此,尽管 tm_isdst 在 time.localtime 等方法的*输出*中有用,但作为 time.mktime 等方法的*输入*却很麻烦。
如果程序员对 time.mktime 错误地指定了非负值的 tm_isdst,结果将是偏差 1 小时的时间,而且由于在调用 time.mktime 之前很少有办法了解夏令时(DST),因此唯一明智的选择通常是 tm_isdst=-1。
与 tm_isdst 不同,提议的 fold 属性对 datetime 实例的解释没有影响,除非没有该属性时可能存在两种(或没有)解释。
由于有一个名为 isdst 但语义与 tm_isdst 不同的东西会非常令人困惑,所以我们需要一个不同的名称。此外,datetime.datetime 类已经有一个名为 dst() 的方法,如果我们把 fold 称为“isdst”,就必然会出现“isdst”为零但 dst() 不为零或反之的情况。
为什么是 “fold”?
由 Guido van Rossum 建议,并受到一位作者的青睐(但最初被另一位作者反对)。在属性的允许值从 False/True 更改为 0/1 后达成了共识。名词“fold”具有正确的含义和易于记忆的规则,但同时又不会引发无根据的假设。
什么是 “first”?
这是最初选择的属性的工作名称,因为显而易见的替代方案(“second”)与现有属性冲突。它被拒绝的主要原因是它会使 True 成为默认值。
还考虑了以下替代名称
- 稍后
- 与“fold”的有力竞争者。一位作者不喜欢它,因为它容易与同样合适的“latter”混淆,但在自动补全无处不在的时代,这只是一个小小的考虑。一个更强烈的反对意见可能是,在缺失时间的情况下,我们将有一个
later=True实例被.astimezone(timezone.utc)转换为较早的时间,而later=False的实例则不然。然而,这又可以解释为原始时间无效的一个可取的指示。 - 哪个
localtime函数分支索引的原始占位符名称独立提议作为消歧属性的名称,并获得了一些支持。- 重复的
- 未在邮件列表中获得任何支持。
- ltdf
- (Local Time Disambiguation Flag) - 简短,没有人会不阅读文档就试图猜测它的含义。(这个缩写在 PEP 讨论中被那些不想认可任何替代方案的人使用,其含义是
ltdf=False是较早的。)
两个值足够吗?
已提出允许 fold 属性为 None 或 -1 值的几个原因:向后兼容性、与 tm_isdst 的类比以及对无效时间的严格检查。
向后兼容性
有人建议,如果 fold 标志的默认值为 None,这将表示请求预-PEP 行为,从而可以改善向后兼容性。根据下面的分析,我们认为所提议的更改(默认 fold=0)具有足够的向后兼容性。
本 PEP 仅提供三种方式让程序发现两个原本相同的 datetime 实例具有不同的 fold 值:(1) 显式检查 fold 属性;(2) 如果实例是“天真”的——使用 astimezone() 方法转换为另一个时区;以及 (3) 使用 timestamp() 方法转换为 float。
由于 fold 是一个新属性,因此现有程序无法使用第一个选项。请注意,选项 (2) 仅适用于在系统时区中恰好处于折叠或间隙中的“天真”datetime。在所有其他情况下,转换中将忽略 fold 的值,除非实例使用“fold”感知 tzinfo,而这在预-PEP 程序中是不可用的。同样,对此类程序而言,对“天真”实例调用 astimezone() 将不可用,因为 astimezone() 目前不适用于“天真”datetime。
这使我们只剩下一种情况,现有程序在实现此 PEP 后可能开始产生不同的结果:当对恰好处于折叠或间隙中的“天真”datetime 实例调用 datetime.timestamp() 方法时。在当前实现中,结果是未定义的。根据系统 mktime 实现,程序在这些情况下可能会看到不同的结果或错误。有了这个 PEP,时间戳的值在这些情况下将是明确定义的,但将取决于 fold 标志的值。我们认为 datetime.timestamp() 方法行为的变化是此 PEP 实现的错误修复。如果用户依赖旧行为,仍然可以通过编写 time.mktime(dt.timetuple()) + 1e-6*dt.microsecond 来模拟旧行为,而不是 dt.timestamp()。
与 tm_isdst 的类比
time.mktime 接口允许 tm_isdst 标志的三个值:-1、0 和 1。正如我们上面解释的,-1(要求 mktime 根据其余字段确定给定时间是否处于夏令时)是实践中唯一有用的选择。
然而,有了 fold 标志,对于大多数具有夏令时转换的时区,datetime.timestamp() 将在 99.98% 的时间内返回与 mktime 使用 tm_isdst=-1 相同的值。此外,无论 fold 的值如何,都指定了类似 tm_isdst=-1 的行为。
只有在 0.02% 的情况下(每年 2 小时),datetime.timestamp() 和使用 tm_isdst=-1 的 mktime 可能会不一致。然而,即使在这种情况下,大多数 mktime 实现仍将返回 fold=0 或 fold=1 的值,尽管相关标准允许 mktime 在这些情况下返回 -1 并设置错误代码。
换句话说,本 PEP 不缺少 tm_isdst=-1 行为。相反,它是以两种不同且明确定义的风味提供的唯一行为。缺失的行为是当给定的本地小时因 tm_isdst 错误指定而被解释为不同的本地小时时。
例如,在北半球实行夏令时(6月实行夏令时)的时区中,可以得到
>>> from time import mktime, localtime
>>> t = mktime((2015, 6, 1, 12, 0, 0, -1, -1, 0))
>>> localtime(t)[:]
(2015, 6, 1, 13, 0, 0, 0, 152, 1)
请注意,12:00 被 mktime 解释为 13:00。使用 datetime.timestamp 和 datetime.fromtimestamp,目前保证
>>> t = datetime.datetime(2015, 6, 1, 12).timestamp()
>>> datetime.datetime.fromtimestamp(t)
datetime.datetime(2015, 6, 1, 12, 0)
本 PEP 将同样的保证扩展到 fold 的两个值
>>> t = datetime.datetime(2015, 6, 1, 12, fold=0).timestamp()
>>> datetime.datetime.fromtimestamp(t)
datetime.datetime(2015, 6, 1, 12, 0)
>>> t = datetime.datetime(2015, 6, 1, 12, fold=1).timestamp()
>>> datetime.datetime.fromtimestamp(t)
datetime.datetime(2015, 6, 1, 12, 0)
因此,建议将 fold=-1 的用途之一——匹配传统行为——是不需要的。任何选择的 fold 都将与旧行为匹配,除了旧行为未定义的少数情况。
严格的无效时间检查
另一个建议是使用 fold=-1 或 fold=None 来表示程序确实没有办法处理折叠和间隙,并且 dt.utcoffset() 在 dt 表示模糊或缺失本地时间时应引发错误。
此提议的主要问题在于,dt.utcoffset() 在内部用于无法引发错误的情况:例如,在字典查找或列表/集合成员检查中。因此,严格的间隙/折叠检查行为需要由单独的标志控制,例如 dt.utcoffset(raise_on_gap=True, raise_on_fold=False)。然而,此功能可以轻松地在用户代码中实现
def utcoffset(dt, raise_on_gap=True, raise_on_fold=False):
u = dt.utcoffset()
v = dt.replace(fold=not dt.fold).utcoffset()
if u == v:
return u
if (u < v) == dt.fold:
if raise_on_fold:
raise AmbiguousTimeError
else:
if raise_on_gap:
raise MissingTimeError
return u
此外,在问题情况下引发错误只是众多可能的解决方案之一。交互式程序可以要求用户提供额外输入,而服务器进程可以记录警告并采取适当的默认操作。我们无法为所有可能的用户需求提供功能,但本 PEP 提供了以几行代码实现任何所需行为的方法。
实施
版权
本文档已置于公共领域。
图片来源
此图片是一名美国军方或国防部雇员的作品,是在其公务中拍摄或制作的。作为美国联邦政府的作品,该图片属于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0495.rst
最后修改时间:2025-02-01 08:59:27 GMT