符合ISO标准的Prolog系统在日期时间转换边界场景下应抛出何种错误?
datime/2的错误处理规范 咱们先把核心问题摆出来:datime/2是SICStus和SWI-Prolog都实现的谓词,用来在UNIX时间戳和datime(Year,Month,Day,Hour,Min,Sec)格式的日期记录之间做双向转换。但在一些边界场景下,这两个实现都出现了不符合预期的输出,那符合ISO标准的Prolog系统在这些场景下应该抛出什么错误呢?
先明确datime/2的官方定义(来自SICStus Prolog手册):
now(-When):把当前日期时间以UNIX时间戳的形式和When合一。datime(-Datime):将Datime与当前日期时间绑定为datime/6结构,所有字段都是整数。datime(+When,-Datime)/datime(-When,+Datime):在now/1获取的时间戳和datime/6记录之间双向转换。
先看两个实现的异常表现
SICStus Prolog 4.6.0的异常输出
| ?- use_module(library(system)). yes | ?- datime(X,Y). no % 预期:合法结果 或 明确的错误提示 | ?- datime(-67768200000000000,X). no % 预期:合法结果 或 错误提示 | ?- datime(X,datime(19700000000000,1,1,1,0,0)). X = -32029950380687608 ? ; % 预期:X应为合法非负时间戳(UNIX时间通常从1970年开始) no
SWI-Prolog 8.2.0的异常"结果"
?- use_module(library(dialect/sicstus/system)). true. ?- datime(X,datime(1970,1,1,1,0,0)). ERROR: Arguments are not sufficiently instantiated % 预期:返回对应的时间戳X=0(或UTC偏移后的对应值) ?- datime(10000000000000000000000000,X). X = datime(1901, 1461302465, 32753, 9, 35, 13). ?- datime(1000000000000000000000000000,X). X = datime(1901, 1461302465, 32753, 9, 35, 13). ?- datime(100000000000000000000000000000,X). X = datime(1901, 1461302465, 32753, 9, 35, 13). % 预期:不同的超大时间戳应返回不同的日期记录
符合ISO标准的错误处理逻辑
ISO Prolog定义了明确的错误分类,针对上面的场景,正确的错误处理应该是这样的:
1. 两个参数都未实例化:datime(X,Y)
当datime/2的两个参数都是变量时,系统无法确定要执行哪个方向的转换(转时间戳还是转日期记录),这种情况下应该抛出**instantiation_error**,而不是像SICStus那样返回no——因为no会让用户误以为没有匹配的结果,但实际是参数不足导致无法执行。
2. 时间戳超出合法范围
不管是像SICStus里的极端负时间戳,还是SWI里的超大正时间戳,只要输入的时间戳超出了实现支持的合法范围(UNIX时间戳有明确的上下限,哪怕是64位系统也不是无限的),符合标准的系统应该抛出**domain_error**,明确说明输入值不属于unix_time_stamp这个合法域。
另外,SWI对不同超大时间戳返回相同结果的情况属于实现bug——要么支持该范围并返回正确的日期,要么直接报错,不能随便返回一个固定的无效日期。
3. 无效的datime/6记录
比如SICStus例子里的datime(19700000000000,1,1,1,0,0),年份明显超出合理范围,而且datime/6的每个字段都有自己的合法域:
- Year:合理的整数年份(比如正负年份但不能极端到无意义)
- Month:1-12
- Day:1到当月的最大天数
- Hour:0-23
- Min:0-59
- Sec:0-59(部分实现支持闰秒到60)
当任何字段的值不在对应域内时,应该抛出**domain_error**,还要明确指出是哪个字段的域错误(比如year或month)。像SICStus那样返回一个无意义的负时间戳是不符合标准的,因为这个无效日期根本无法对应合法的UNIX时间戳,应该直接报错。
4. 参数实例化足够但被错误报错
比如SWI里的datime(X,datime(1970,1,1,1,0,0)),第二个参数是完全实例化的合法日期记录,第一个参数是变量,系统完全可以完成从日期到时间戳的转换,这里抛出instantiation_error是错误的。只有当日期记录里有未绑定的变量(比如datime(X,datime(Y,1,1,1,0,0)),Y是变量),才应该抛出instantiation_error。
总结
符合ISO标准的Prolog系统处理datime/2的异常场景时,应根据情况抛出以下错误:
instantiation_error:参数实例化程度不足,无法确定转换方向或完成转换时。domain_error:时间戳超出合法范围,或日期记录的字段值不在对应域的合法范围内时。type_error:参数类型错误(比如时间戳不是整数,日期记录不是datime/6结构)。
内容的提问来源于stack exchange,提问作者repeat

