我想在维基数据API给出的日期之间进行日期比较。
起初我虽然使用 Python 的日期时间模块,但我遇到了两个问题:
- 维基数据处理儒略历和公历中过去或未来跨越数十亿年的日期,日期时间仅适用于 1 年到 9999 年之间的公历日期。
- 当精度为 (9) 年或更低时,月份和日期将呈现为"00-00",datetime.strptime 无法管理。
例如,在有关巴黎的示例查询中,可以将此日期转换为日期时间:
datetime.strptime("+1968-01-01T00:00:00Z","+%Y-%m-%dT%H:%M:%SZ")
datetime.datetime(1968, 1, 1, 0, 0)
这个不能:
datetime.strptime("+2012-00-00T00:00:00Z","+%Y-%m-%dT%H:%M:%SZ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/_strptime.py", line 510, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.5/_strptime.py", line 343, in _strptime
(data_string, format))
ValueError: time data '+2012-00-00T00:00:00Z' does not match format '+%Y-%m-%dT%H:%M:%SZ'
更不用说"-0300-00-00T00:00:00Z"(公元前 300 年)
我不能简单地使用年份比较,因为对于有关共同时代之前发生的事情的项目,在同一负年份中可以有多个日期。
我不太确定处理这个问题的最佳方法。我可以使用另一个库吗?
tl;DR:日期时间不能处理这种事情,所以甚至不要尝试。你有字符串,保留它们并这样对待它们。
您可以简单地将它们排序为字符串,前提是它们的长度一致(否则根据需要填充)和格式。这将允许对">扩展"的 ISO8601:2004 时间戳进行排序(按照标准00
不允许对月份和天数进行排序)。
假设 Python3,这段代码:
import urllib.request,json
url = urllib.request.urlopen("https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&ids=Q90&props=info%7Caliases%7Clabels%7Cdescriptions%7Cclaims%7Cdatatype%7Csitelinks%2Furls&languages=fr&languagefallback=1&formatversion=2")
data = json.loads(url.read().decode())
P6 = sorted(data['entities']['Q90']['claims']['P6'], key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'])
for x in P6:
print(x['mainsnak']['datavalue']['value']['numeric-id'])
产生以下结果集:
1685301
947901
656015
2596877
3131449
1986521
1685102
1684642
601266
677730
289303
959708
2105
1685859
256294
2851133
此外,您需要将列表分为两个:
- 以
-
符号开头的项目 - 以
+
符号开头的项目
然后按月-日期-时间升序对第一个列表进行排序,然后按字符串表示的年份的无符号整数值排序(因为sort()
和sorted()
保证"稳定">),简单地对第二个列表进行排序,然后再次将它们连接起来。这将允许对签名ISO8601时间戳进行正确排序。
neg = [x for x in P6 if x['qualifiers']['P580'][0]['datavalue']['value']['time'].startswith('-') ]
pos = [x for x in P6 if x['qualifiers']['P580'][0]['datavalue']['value']['time'].startswith('+') ]
neg.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'][5:])
neg.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'][1:5])
pos.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'])
P6sorted = neg+pos
至于填充,如果需要,使用string.rjust()
就足够微不足道了(尽管您必须稍微更改排序以反映"新"时间戳的长度;string.zfill()
不是该工作的正确工具,因为您要更改的字符串不是数字,具有"T"、"Z"、"-"和":"):
maxlength = max( map( lambda claim: len( claim['qualifiers']['P580'][0]['datavalue']['value']['time'] ), P6 ) )
for claim in P6:
claim['qualifiers']['P580'][0]['datavalue']['value']['time'] = claim['qualifiers']['P580'][0]['datavalue']['value']['time'][0] + claim['qualifiers']['P580'][0]['datavalue']['value']['time'][1:].rjust(maxlength-1, "0");
neg = [x for x in P6 if x['qualifiers']['P580'][0]['datavalue']['value']['time'].startswith('-') ]
pos = [x for x in P6 if x['qualifiers']['P580'][0]['datavalue']['value']['time'].startswith('+') ]
neg.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'][maxlength-16:])
neg.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'][maxlength-22:maxlength-16], reverse=True)
pos.sort(key=lambda claim: claim['qualifiers']['P580'][0]['datavalue']['value']['time'])
P6sorted = neg+pos
for claim in P6sorted:
print([claim['mainsnak']['datavalue']['value']['id'],claim['qualifiers']['P580'][0]['datavalue']['value']['time']])
顺便说一句,为了可读性,您可能需要">装饰-排序-未装饰"(执行施瓦茨变换)。
最后,如果您担心儒略历与公历,则必须通过添加相应的天数,根据国家和年份将儒略日期转换为公历日期,并应用上述方法。但请记住,儒略日期 (YYYY)-(MM)-(DD) 早于"似乎提前一天"的公历日期,因此真的不应该太担心。