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,用于枚举两个模棱两可的时间。

术语
当时钟被拨回时,我们说时间中创建了一个折叠 [2]。当时钟被拨快时,就会创建一个间隙。落在折叠中的本地时间称为模棱两可的。落在间隙中的本地时间称为缺失的。
提案
“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
的新的仅限关键字参数,其默认值为 0。 fold
参数的值将用于初始化返回实例中 fold
属性的值。
方法
datetime.time
和 datetime.datetime
类的 replace()
方法将获得一个名为 fold
的新的仅限关键字参数。它的行为类似于其他 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)
返回一个具有指定年份、月份、日期、小时、分钟、秒、微秒和折叠的 datetime.datetime
对象。
PyObject* PyTime_FromTimeAndFold(
int hour, int minute, int second, int usecond, int 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 实例到感知时间的转换。
astimezone()
方法现在可以用于朴素的 self
。在这种情况下,将假定系统本地时区,并且将使用 fold
标志来确定模棱两可的情况下哪个本地时区有效。
例如,在设置为 US/Eastern 时区的系统上
>>> 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
属性。
例如,在设置为 US/Eastern 时区的系统上
>>> 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
中较小的值,否则返回较大的值。
例如,在设置为 US/Eastern 时区的系统上
>>> 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
的值,它对应于在时区中 dt
,其中 UTC 偏移量始终与时间间隔之前的偏移量相同,另一个是类似的值,但在 UTC 偏移量始终与时间间隔之后的偏移量相同的时区中。
对于给定的缺失的 dt
,dt.timestamp()
返回的值将是这两个“了解一下”值中较大的值,如果 dt.fold == 0
,否则为较小的值。(这不是打字错误 - 它有意与模棱两可时间规则相反。)
例如,在设置为 US/Eastern 时区的系统上
>>> datetime(2015, 3, 8, 2, 30, fold=0).timestamp()
1425799800.0
>>> datetime(2015, 3, 8, 2, 30, fold=1).timestamp()
1425796200.0
感知时间 datetime 实例
tzinfo
的 PEP 之前实现的用户将不会看到其已知日期时间实例的行为有任何变化。仅 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
折叠属性的值仅保存在使用协议版本 4(在 Python 3.4 中引入)或更高版本创建的 pickle 中。
datetime.datetime
和 datetime.time
对象的 pickle 大小不会改变。fold
值将编码在 datetime.datetime
pickle 有效负载的第 3 个字节的第一个比特中;以及 datetime.time
有效负载的第一个字节的第一个比特中。在当前实现中,这些字节用于存储月份 (1-12) 和小时 (0-23) 值,并且第一个比特始终为 0。我们选择这些字节是因为它们是当前 unpickle 代码检查的唯一字节。因此,在 PEP 之前的 Python 中加载 PEP 之后的 fold=1
pickle 将导致异常,而不是具有超出范围组件的实例。
标准库中 tzinfo 的实现
本 PEP 中没有提出 datetime.tzinfo
抽象类的任何新实现。现有的(固定偏移)时区不会引入模棱两可的本地时间,并且无论 fold
的值如何,它们的 utcoffset()
实现都将返回与现在相同的常数值。
抽象 datetime.tzinfo
类中 fromutc()
的基本实现不会改变。它目前在标准库中没有任何地方使用,因为唯一包含的 tzinfo
实现(实现固定偏移时区的 datetime.timezone
类)覆盖了 fromutc()
。保持默认实现不变的好处是,继承默认 fromutc()
的 PEP 之前的第三方实现不会意外受到影响。
新的 tzinfo 实现指南
想要支持可变 UTC 偏移量(由于夏令时和其他原因)的具体 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()
方法应该使用折叠属性的值来确定一个原本模棱两可的时间 t
是否对应于转换前或转换后的时间。根据定义,在任何导致折叠的转换之前,utcoffset()
较大,之后较小。tzname()
和 dst()
返回的值可能取决于或可能不取决于 fold
属性的值,具体取决于转换的类型。
上图说明了在回拨转换前后 UTC 时间和本地时间之间的关系。锯齿线是 fromutc()
实现的函数的图形。UTC 轴上与转换点相邻且大小为转换时的时间偏移量的两个区间映射到本地轴上的同一区间。 fromutc()
方法的新实现应该在 self
位于 UTC 轴上黄色标记的区域时将折叠属性设置为 1。(所有区间都应被视为左侧闭合,右侧打开。)
注意间隙
fromutc()
方法绝不应产生间隙中的时间。
如果在落在间隙中的本地时间上调用 utcoffset()
、tzname()
或 dst()
方法,则如果 fold=0
,则应使用转换前的规则。否则,应使用转换后的规则。
上图说明了在顺拨转换前后 UTC 时间和本地时间之间的关系。在转换时,本地时钟将向前跳动,跳过间隙中的时间。为了确定 utcoffset()
、tzname()
和 dst()
的值,转换前的线将向前延伸以找到对应于 fold=0
的间隙中的时间的 UTC 时间,而对于 fold=1
的实例,转换后的线将向后延伸。
转换时的规则总结
在模棱两可/缺失的时间上,utcoffset()
应根据下表返回相应的值
fold=0 | fold=1 | |
---|---|---|
折叠 | oldoff | newoff = oldoff - delta |
间隙 | oldoff | newoff = oldoff + delta |
其中 oldoff
(newoff
)是转换前(后)的 UTC 偏移量,delta
是折叠或间隙的绝对大小。
请注意,折叠属性的解释在折叠和间隙情况下是一致的。在这两种情况下,fold=0
(fold=1
)表示使用转换前(后)的 fromutc()
线来查找 UTC 时间。仅在“折叠”情况下,UTC 时间 u0
和 u1
是方程 fromutc(u) == t
的“真实”解,而在“间隙”情况下,它们是“虚构”解。
夏令时转换
在 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
是标准(非夏令时)偏移量,dstoff
是夏令时校正值(通常为dstoff = timedelta(hours=1)
),zero = timedelta(0)
。
时间算术和比较运算符
在数学方面,他胜过第谷·布拉赫或厄拉·帕特因为他,通过几何比例,可以计算啤酒罐的大小;用正弦和正切直接解决,面包或黄油是否缺斤少两,并巧妙地告诉一天中的几点时钟用代数来报时。——塞缪尔·巴特勒的《胡迪布拉斯》
对于所有使用朴素日期时间实例的操作,fold
属性的值都将被忽略。因此,仅fold
值不同的朴素datetime.datetime
或datetime.time
实例将被视为相等。需要区分此类实例的应用程序应显式检查fold
的值,或将这些实例转换为没有模糊时间(例如UTC)的时区。
每当将timedelta加到或减去可能为已知或朴素的datetime实例时,fold
的值也将被忽略。即使原始datetime实例具有fold=1
,将timedelta加到(从)datetime的结果始终将fold
设置为0。
对于日期时间实例t
和s
,计算差值t - s
的方式没有提出任何更改。如果两个实例都是朴素的,或者t.tzinfo
与s.tzinfo
是同一个实例(t.tzinfo is s.tzinfo
计算结果为True
),则t - s
是一个timedelta d
,使得s + d == t
。如上一段所述,timedelta加法会忽略fold
和tzinfo
属性,区域内或朴素日期时间减法也是如此。
朴素和区域内比较将忽略fold
的值并返回与现在相同的结果。(这是保持向后兼容性的唯一方法。如果您需要使用fold的已知区域内比较,请先将双方都转换为UTC。)
区域间减法将按现在的定义进行:t - s
计算为(t - t.utcoffset()) - (s - s.utcoffset()).replace(tzinfo=t.tzinfo)
,但结果将取决于t.fold
和s.fold
的值,前提是t.tzinfo
或s.tzinfo
是PEP后的。[5]
s == t
但s - u != t - u
。这种悖论并不是真正的新事物,而是对减号运算符在区域内和区域间操作中进行不同重载的固有特性。例如,可以轻松构建具有某些可变偏移量tzinfo
的日期时间实例t
和s
,以及具有tzinfo=timezone.utc
的日期时间u
,使得(t - u) - (s - u) != t - s
。对这种悖论的解释是,括号内的减号和其他两个减号实际上是三种不同的运算:区域间日期时间减法、timedelta减法和区域内日期时间减法,每种运算都分别具有减法的数学性质,但在单个表达式中组合时则不然。感知时间 datetime 的相等比较
已知日期时间比较运算符的工作方式与现在相同,其结果会间接受到fold
值的影响,只要其中一个操作数的utcoffset()
值依赖于它,就有一个例外。每当区域间比较中的一个或两个操作数使得其utcoffset()
依赖于其fold
属性的值时,结果为False
。[6]
形式上,当t.tzinfo is s.tzinfo
计算结果为False
时,t == s
可以定义如下。令toutc(t, fold)
为一个函数,该函数接受一个已知日期时间实例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时间戳的转换和从POSIX时间戳的转换现在将正确往返(最多浮点数舍入)。可能需要修改实现了旧错误行为解决方法的程序。
旧程序生成的pickle将保持完全向前兼容。只有在新的版本中以pickle形式保存的fold=1
的datetime/time实例将无法被旧版本的Python读取。具有fold=0
(默认值)的实例的pickle将保持不变。
问答
为什么不将新标志称为“isdst”?
非技术性答案
- 爱丽丝:鲍勃——我们明天凌晨1:30举行一次观星派对吧!
- 鲍勃:我是否应该最初假设指定时间是否处于夏令时?
- 爱丽丝:啊?
- 鲍勃:爱丽丝——我们明天凌晨1:30举行一次观星派对吧!
- 爱丽丝:你知道,鲍勃,明天凌晨1:30会发生两次。你指的是哪一次?
- 鲍勃:我没有想过,但让我们选择第一次。
(相同的人物,一小时后)
- 鲍勃:爱丽丝——我的这个Py-O-Clock小玩意儿要求我在设置明天凌晨1:30时在fold=0和fold=1之间进行选择。我该怎么办?
- 爱丽丝:我从未听说过Py-O-Clock,但我想fold=0是第一次凌晨1:30,fold=1是第二次。
技术原因
虽然time.struct_time
对象的tm_isdst
字段可用于消除fold中的本地时间歧义,但这种消除歧义的语义与本PEP中的提案完全不同。
tm_isdst
字段的主要问题在于,如果不了解仅对tzinfo
实现可用的有关时区的信息,就无法知道tm_isdst
的哪个值是合适的。因此,虽然tm_isdst
在诸如time.localtime
之类的函数的输出中很有用,但作为诸如time.mktime
之类的函数的输入,它却很麻烦。
如果程序员错误地将tm_isdst
的非负值指定给time.mktime
,则结果时间将相差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 成为默认值而被拒绝。
以下替代名称也已被考虑
- later
- 与“fold”非常接近的一个竞争者。一位作者不喜欢它,因为它容易与同样合适的“latter”混淆,但在到处都是自动完成的时代,这是一个小的考虑因素。一个更强烈的反对意见可能是,在缺少时间的情况下,我们将拥有
later=True
实例被.astimezone(timezone.utc)
转换为更早的时间,而later=False
则不会。但是,这也可以被解释为一个理想的指示,表明原始时间无效。 - which
- 用于
localtime
函数分支索引的 原始 占位符名称 独立地被提出 用于歧义属性的名称,并获得 一些支持。 - repeated
- 在邮件列表中没有收到任何支持。
- ltdf
- (本地时间歧义标志) - 简短且没有人会尝试猜测其含义,除非阅读文档。(此缩写词在 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) 仅适用于恰好位于系统时区中的折叠或间隙中的朴素日期时间。在所有其他情况下,fold
的值在转换中将被忽略,除非实例使用 fold
感知的 tzinfo
,而这在 PEP 之前的程序中是不可用的。类似地,对朴素实例调用的 astimezone()
在此类程序中也将不可用,因为 astimezone()
目前不适用于朴素日期时间。
这使得我们只有一种情况,即现有程序在实施本 PEP 后可能会开始产生不同的结果:当对恰好位于折叠或间隙中的朴素日期时间实例调用 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
根据其余字段从给定时间确定 DST 是否有效)是实际上唯一有用的选择。
但是,使用 fold
标志,datetime.timestamp()
将在 99.98% 的时间内返回与 mktime
具有 tm_isdst=-1
的相同值,这适用于大多数具有 DST 转换的时区。此外,无论 fold
的值如何,都指定了类似于 tm_isdst=-1
的行为。
只有在 0.02% 的情况下(每年 2 小时),datetime.timestamp()
和 mktime
具有 tm_isdst=-1
可能会不一致。但是,即使在这种情况下,大多数 mktime
实现也将返回 fold=0
或 fold=1
值,即使相关标准允许 mktime
返回 -1 并在这些情况下设置错误代码。
换句话说,本 PEP 中并没有缺少 tm_isdst=-1
行为。相反,它是以两种不同的明确定义的方式提供的唯一行为。缺少的行为是当给定的本地小时由于 tm_isdst
指定错误而被解释为不同的本地小时时。
例如,在北半球的遵守 DST 的时区(DST 在 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
表示模棱两可或缺少的本地时间,dt.utcoffset()
就会引发错误。
此提议的主要问题是,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
上次修改时间:2024-01-11 16:25:09 GMT