1. 设计目标
为古籍、历史文献、方志、档案等非结构化文本中的时间表达提供一套:
- 学术严谨:尊重历史时间的模糊性、多历法、不确定性
- 工程可用:支持高效查询、可视化、统计分析
- 语义清晰:区分时间在文本中的不同话语功能
- 可扩展:兼容未来新增时间类型与历法系统
2. 核心理念:三层抽象
表格
| 层级 | 名称 | 目的 | 特点 |
|---|---|---|---|
| L1 | 一次时间(原始层) | 保留原始证据 | 不可变,人类可读 |
| L2 | 二次时间(语义层) | 结构化建模 | 显式表达粒度、不确定性、相对性 |
| L3 | 三次时间(计算层) | 标准化数值接口 | 统一单位(秒),支持高效计算 |
✅ 原则:L1 → L2 → L3 是单向增强,不可逆。L1 永远是权威来源。
3. 时间角色分类(temporal_role)
不同时间在文本中承担不同功能,需差异化处理:
表格
| 角色 | 枚举值 | 典型形式 | 处理策略 |
|---|---|---|---|
| 创作时间 | composition | “贞观三年”“撰于洪武初” | 年号转公历,考虑作者生卒 |
| 记录时间 | recording | “嘉靖乙酉刻本”“康熙重修” | 标注为时间段,confidence 降低 |
| 发生时间 | occurrence | “赤壁之战”“春大旱” | 优先用年,季节补为月 |
| 指向时间 | reference | “今”“昔者”“后五年” | 必须绑定锚点,可延迟解析 |
| 传播时间 | dissemination | “初刊于1905年” | 近代精确,直接 absolute |
🔒 约束:
reference类型必须提供relative_anchor_id或time_offset。
4. 字段定义
4.1 一次时间(L1)
表格
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
original_text | TEXT | ✅ | 原始时间短语,如“明中叶以降” |
4.2 二次时间(L2)
表格
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
temporal_role | VARCHAR(20) | ✅ | 五类角色之一 |
time_center | JSONB | ✅ | {"value": "1369", "unit": "year"} |
time_radius | JSONB | ✅ | {"amount": 30, "unit": "year"} |
time_offset | TEXT | ⚠️ | ISO 8601 duration,如 "P1Y" |
time_confidence | NUMERIC(3,2) | ✅ | [0.00, 1.00] |
relative_anchor_id | UUID | ⚠️ | 仅 reference 类型必填 |
✅ 单位枚举(
unit):century,decade,year,month,day,hour,minute,second
4.3 三次时间(L3)
表格
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
computed_time_center | BIGINT | ⚠️ | Unix 秒(可负,表公元前) |
computed_time_offset | BIGINT | ⚠️ | 偏移(秒),通常 NULL |
computed_time_radius | BIGINT | ⚠️ | 不确定性半径(秒) |
computed_time_confidence | NUMERIC(3,2) | ⚠️ | 通常 = time_confidence |
⚠️ 三次时间由 ETL pipeline 按需填充,可为空
5. 典型示例
示例 1:创作时间(模糊)
原文:“《齐民要术》成书于北魏”
json
编辑
1{
2 "original_text": "北魏",
3 "temporal_role": "composition",
4 "time_center": {"value": "500", "unit": "year"},
5 "time_radius": {"amount": 70, "unit": "year"},
6 "time_confidence": 0.8,
7 "computed_time_center": -47336256000,
8 "computed_time_radius": 2209032000
9}
区间:430–570 CE,中心=500年中点
示例 2:指向时间(相对)
原文:“其后五年”
json
编辑
1{
2 "original_text": "其后五年",
3 "temporal_role": "reference",
4 "time_center": {"value": "1374", "unit": "year"},
5 "time_offset": "P5Y",
6 "relative_anchor_id": "a1b2c3...",
7 "time_confidence": 0.9,
8 "computed_time_center": -19317225600
9}
锚点为“洪武二年”(1369),+5年=1374
示例 3:记录时间(低可信)
原文:“宋刻本”
json
编辑
1{
2 "original_text": "宋刻本",
3 "temporal_role": "recording",
4 "time_center": {"value": "1100", "unit": "year"},
5 "time_radius": {"amount": 150, "unit": "year"},
6 "time_confidence": 0.6,
7 "computed_time_center": -28401408000,
8 "computed_time_radius": 4733640000
9}
区间:950–1250 CE(覆盖北宋+南宋)
6. 数据库实现(PostgreSQL)
sql
编辑
1CREATE TABLE temporals (
2 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3 original_text TEXT NOT NULL,
4 temporal_role VARCHAR(20) NOT NULL
5 CHECK (temporal_role IN ('composition', 'recording', 'occurrence', 'reference', 'dissemination')),
6
7 -- L2: 语义层
8 time_center JSONB NOT NULL,
9 time_radius JSONB NOT NULL DEFAULT '{"amount": 0, "unit": "year"}',
10 time_offset TEXT,
11 time_confidence NUMERIC(3,2) NOT NULL DEFAULT 1.00,
12 relative_anchor_id UUID REFERENCES temporals(id),
13
14 -- L3: 计算层
15 computed_time_center BIGINT,
16 computed_time_offset BIGINT,
17 computed_time_radius BIGINT,
18 computed_time_confidence NUMERIC(3,2),
19
20 -- 元数据
21 text_node_id TEXT NOT NULL,
22 text_node_type VARCHAR(30) NOT NULL,
23 extractor VARCHAR(50),
24 workflow_type VARCHAR(20),
25 version INTEGER NOT NULL DEFAULT 1,
26 batch_id UUID,
27 created_at TIMESTAMPTZ DEFAULT NOW(),
28 updated_at TIMESTAMPTZ DEFAULT NOW(),
29
30 -- 约束
31 CONSTRAINT valid_time_center CHECK (time_center ?& ARRAY['value', 'unit']),
32 CONSTRAINT valid_time_radius CHECK (
33 time_radius ?& ARRAY['amount', 'unit'] AND
34 (time_radius->>'amount')::INTEGER >= 0
35 ),
36 CONSTRAINT reference_requires_context
37 CHECK (temporal_role != 'reference' OR relative_anchor_id IS NOT NULL OR time_offset IS NOT NULL)
38);
39
40-- 索引
41CREATE INDEX idx_temporals_role ON temporals (temporal_role);
42CREATE INDEX idx_temporals_computed_center ON temporals (computed_time_center)
43 WHERE computed_time_center IS NOT NULL;
7. 工作流建议
- 抽取:NLP 识别
original_text+ 初步temporal_role - 标准化:
composition:查年号表 → 公历年reference:运行指代消解 → 绑定anchor_idoccurrence:查事件库 → 补全年份
- 置信度校准:根据来源(正史/笔记/传说)调整
confidence - 计算层生成:将 L2 转为 Unix 秒(含公元前处理)
- 人工校验:对
confidence < 0.8的条目进行专家审核
8. 附录:单位换算常数(用于 L2→L3)
表格
| 单位 | 秒数(近似) |
|---|---|
| 1 year | 31,557,600 (= 365.25 × 86400) |
| 1 month | 2,629,746 (= 年 / 12) |
| 1 day | 86,400 |
| 1 hour | 3,600 |
📌 注意:公元前年份转换需使用天文年编号(1 BCE = year 0, 2 BCE = year -1)
📌 版本:v1.0
适用场景:古籍时间标注、历史事件数据库、数字方志、CBDB 扩展
维护建议:每新增一类时间表达,先评估是否属于现有五类,否则扩展temporal_role