2

我想在 Wikidata API 给出的日期之间进行日期比较。

起初我想使用 Python 的 datetime 模块,但我遇到了两个问题:

  • Wikidata 处理过去数十亿年的日期或儒略历和公历中的未来,日期时间仅适用于公历 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 年)

我不能简单地使用年份比较,因为对于有关共同时代之前发生的事情的项目,同一负年份中可能有多个日期。

我不太确定处理这个问题的最佳方法。我可以使用另一个库吗?

4

1 回答 1

2

tl;dr : datetime 不能处理那种事情,所以不要尝试。你有字符串,保留它们并照此对待它们。

您可以简单地将它们排序为字符串,只要它们具有一致的长度(否则根据需要填充)和格式。这将允许对“扩展的” 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() ) ,对第二个列表进行简单排序,然后再次将它们连接起来。这将允许对签名的ISO8601 时间戳进行正确排序。sorted()

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']])

顺便说一句,为了可读性,您可能想要“装饰-排序-不装饰”(执行 Schwartzian 变换)。

最后,如果您担心儒略历和公历,则必须通过添加相应的天数将儒略日期转换为基于国家和年份的公历日期,并应用上述方法。但请记住,儒略历 (YYYY)-(MM)-(DD) 早于“似乎提前一天”的公历日期,所以真的不应该太担心。

于 2017-06-22T16:24:02.380 回答