news 2026/9/23 13:59:21

compromise-dates 插件全解析:用自然语言解析日期、时间与时长

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
compromise-dates 插件全解析:用自然语言解析日期、时间与时长

compromise-dates 插件全解析:用自然语言解析日期、时间与时长

【免费下载链接】compromisemodest natural-language processing项目地址: https://gitcode.com/gh_mirrors/co/compromise

compromise-dates 是 compromise 生态中最具实用价值的插件之一,它把"把一段人话变成结构化时间数据"这件事做到了开箱即用:无论是the second monday of february2 years, 4 months, and 5 days ago还是GMT+9,都能解析出明确的start/endISO 时间戳。本指南以 plugins/dates/README.md 为核心,结合插件源码与测试,完整讲解其能力边界、API、配置项与底层解析管线,读完即可在自己的前端或后端项目里接入自然语言日期解析。

快速开始:安装与加载

compromise-dates 是独立发布的 npm 包,与 compromise 主库分开安装:

npm install compromise compromise-dates

在代码中先加载 compromise,再通过nlp.plugin()注册日期插件:

import nlp from 'compromise' import datePlugin from 'compromise-dates' nlp.plugin(datePlugin) let doc = nlp('the second monday of february') doc.dates().get()[0] /* { start: '2021-02-08T00:00:00.000Z', end: '2021-02-08T23:59:59.999Z'} */

从 package.json 可以看到,插件声明了compromise >=14.2.0作为 peerDependency,运行时依赖spacetime(时区与夏令时计算)与spacetime-holiday(节假日推算),并提供 CJS/ESM/UMD 三种构建产物(builds 目录),浏览器可直接引入compromise-dates.min.js

它能解析什么:能力全景

插件覆盖了从最直白的书面日期到口语化时间表达的大多数场景。下表完整列出了 README 中收录的解析能力,Start/End两列表示解析结果的时间范围:

明确日期(explicit dates)

输入说明StartEnd
march 2nd月+日March 2, 12:00amMarch 2, 11:59pm
2 march日+月
tues march 2星期+月+日
march the second自然语言数字
on the 2nd隐含月份
tuesday the 2nd日期推算

数字日期(numeric dates)

输入说明StartEnd
2020/03/02ISO 格式
2020-03-02连字符 ISO
03-02-2020英式格式
03/02月/日
2020.08.13替代 ISO

命名日期(named dates)

输入说明StartEnd
today
tomorrow
christmas eve日历节假日Dec 24, 12:00amDec 24, 11:59pm
easter天文节假日依年份而定
q1财务季度Jan 1, 12:00amMar 31, 11:59pm

时间(times)

输入说明StartEnd
2pm
2:12pm
2:12
02:12:00奇怪 ISO 时间
two oclock文字形式
before 1时间前
noon
at night非正式时段
in the morning
tomorrow evening

时区(timezones)

输入说明StartEnd
eastern time非正式时区
est时区缩写
peru time地区时区
GMT+9UTC/GMT 偏移
-4h小时偏移
Canada/EasternIANA 时区码

相对时长(relative durations)

输入说明StartEnd
this march
this week
this sunday
next april
this past year
second week of march第 N 周
last weekend of march
last spring季节
the saturday after next后推

推后日期(punted dates)

输入说明StartEnd
in seven weeks现在+时长
two days after june 6th日期+时长
2 weeks from now
2 weeks after june
2 years, 4 months, and 5 days ago复杂时长
a week and a half before文字数字
a week friday习语格式

开始/结束(start/end)

输入说明StartEnd
end of the week倾向结尾
start of next year倾向开头
middle of q2 last year粗略居中

日期区间(date-ranges)

输入说明StartEnd
between june and july显式区间
from today to next halloween
aug 1 - aug 31破折号区间
22-23 February
today to next friday
during june
aug to june 1999共享区间信息
before [2019]截至某日期
by march
after february日期到无限

重复区间(repeating-intervals)

输入说明StartEnd
any wednesdayN 次重复日期
any day in June区间内重复日期June 1 ..... June 30
any wednesday this week
weekends in July更复杂区间
every weekday until February截至某日期的区间

API 详解

.dates():主入口

doc.dates()用于查找所有日期短语,返回一个Dates视图(继承自 compromise 的View),核心实现在 src/api/dates.js。其查找逻辑位于 src/api/find/index.js:先用doc.match('#Date+')匹配,再做一系列反例过滤——排除纯时长(如20 minutes)、金额百分比($5 an hour)、per #Duration等,最后按规则切分日期块,避免30 minutes on tuesday这类短语被误判为日期。

支持以下子方法:

  • .dates().get():返回精简的 start/end JSON。get(n)可传入索引取第 n 个结果;源码中会过滤掉既无start也无repeat的结果,因此every tuesday这类重复日期也会被保留(见 dates.js)。
  • .dates().json():在 compromise 原生 json 基础上叠加日期元数据,每个结果附带dates字段(可用{ dates: false }关闭,见 dates.js)。
  • .dates().format(fmt):把原文中的日期短语原地替换为格式化日期。格式串是 spacetime 的格式语法,测试 format.test.js 给出了可运行示例:
let doc = nlp(`i'm going skiing two days after November 1st 2019 at 7pm`) doc.dates().format('{day} {month} {date-ordinal}, {time}') // i'm going skiing Sunday November 3rd, 7:00pm doc = nlp(`halloween`) doc.dates().format('{month} {date-ordinal}') // October 31st
  • .dates().isBefore(iso)/.dates().isAfter(iso):仅保留早于/晚于给定 ISO 日期的结果。
  • .dates().isSame(unit, iso):仅保留与给定日期同年、同月或同日的(如isSame('month', '2021-02-01'))。

get()返回的DateJSON结构(与 index.d.ts 一致)为:

interface DateJSON { start: string | null // ISO 时间戳 end: string | null timezone: string | null duration: { years?, months?, days?, hours?, minutes? } // 区间跨度 unit?: string // 跨度单位:'day' / 'time' / 'year' 等 repeat?: { // 重复日期,如 'every tuesday' interval: Record<string, number> filter?: { weekDays?: Record<string, boolean> } choose?: 'AND' | 'OR' | null time?: string | null } }

其中unit的推断逻辑很巧妙(见 toJSON.js):如果start恰好落在某单位(year/quarter/month/week/day)的起点,且end落在下一单位起点,就推断该区间为一个完整单位——例如jan 1 to dec 31会被识别为unit: 'year'

.durations():时长

doc.durations()匹配2 months2mins20min这类长度表达(见 durations/index.js),匹配模式为#Value+ #Duration (and? #Value+ #Duration)?,并排除in 20 minutes这类日期偏移表达。get()返回形如{ minute: 30, hour: 2 }的对象。

单位归一化逻辑在 durations/parse.js:m→minutehr→hourwk→weekqtr→quarteryr→year等缩写映射,并支持20mins这种数字字母粘连的词内拆分。测试 durations.test.js 验证了三类行为的边界:

nlp('in 20 mins').dates().found // true —— 'in 20 mins' 是日期偏移 nlp('in 20 mins').durations().found // false nlp('for 20 mins').dates().found // false —— 'for 20 mins' 是纯时长 nlp('for 20 mins').durations().found // true

.times():一天中的时刻

doc.times()匹配4:30pmhalf past fiveten past three等时间表达(见 times.js),查找模式为#Time+ (am|pm)?get()返回:

interface TimeJSON { time: string | null // 规范时间,如 '3:10pm' '24h': string | null // 24 小时制,如 '15:10' hour?: number minute?: number }

format('24h')可把文本时间统一为 24 小时制。时间区间(如tuesday from 4 to 5pm9-5 on tuesday)也能被 range 解析 拆成{ start, end }两个时间点,测试见 times.test.js。

配置选项:context 对象

.dates()接受一个可选 context 对象来设定日期解析的基准环境,这是该插件最重要的定制入口(TypeScript 类型见 index.d.ts 的DateOptions):

const context = { timezone: 'Canada/Eastern', // 默认是你的本地时区 today: '2020-02-20', // 隐含的基准日/基准年 punt: { weeks: 2 }, // 'after june 2nd' 这类表达的隐含时长 dayStart: '8:00am', // 一天的默认开始时间 dayEnd: '5:30pm', // 一天的默认结束时间 dmy: false // 设为 true 时,歧义日期按英式(日月年)解析 } nlp('in two days').dates(context).get() /* [{ start: '2020-02-22T08:00:00.000+5:00', end: '2020-02-22T17:30:00.000+5:00' }] */

各选项的底层行为可以从 parse/index.js 的 context 归一化看到:

  • timezone:设为false时强制使用'UTC',避免本地时区干扰;解析到的时区信息(如eastern time)会覆盖该默认值。
  • today:解析"今天/今年"的相对基准,接受 ISO 字符串、epoch 数字或 Date;默认为spacetime.now(timezone)
  • punt'after june 2nd'这类表达没有明确终点时的隐含时长,默认{ weeks: 2 }
  • dayStart/dayEnd:无显式时刻的日期默认从dayStart开始、dayEnd结束,因此上面的示例中两天后返回8:0017:30而非 0 点到 23:59。
  • dmy:控制歧义数字日期的解释,见下文"英美日期歧义"。

时区还会参与日期基准的换算:当文本自带时区(如in PST)时,parse/one/index.js 会把today的墙上时钟时间搬到目标时区,保证"同一天、同一时刻"语义正确。

解析管线的源码剖析

理解插件为何能处理这么多表达,关键在于其分层的解析管线。整条链路(parse/index.js → range/index.js)可以概括为"单日期解析 + 区间组合"两层:

1. 标签与查找(compute 阶段):插件注册了 tags、words、regex 与一个计算钩子(见 plugin.js)。compute/index.js 中会执行两遍正则网络匹配(doMatches调用两次以链式处理2 years, 4 months and 5 days ago这类复合表达),随后依次运行00-year01-time-range02-timezone03-fixup四步处理,最后对#DateShift表达再做两轮强化打标。

2. 单日期解析(parse/one):任何一段日期文本都要经过"分词 → 解析 → 变换"三个阶段(parse/one/index.js):

  • tokenize(01-tokenize):按 7 个维度切分文本,包括 shift(偏移量)、counter(计数)、time、relative(相对词)、section、timezone、weekday;
  • parse(02-parse):分别处理today(基准日)、holidays(节假日)、next/last(相对星期/月份)、yearly(年度表达)、explicit(显式日期);
  • transform(03-transform):把解析出的各部件叠加到基准日期对象上,例如addCounter实现second week of march这类第 N 周计算。

3. 区间解析(parse/range):先检测重复日期(every tuesday),再按模板依次尝试twoTimescombosdateRangeoneDate四类区间(range/index.js),每个模板内部先doc.match(fmt.match)命中再执行自定义parse逻辑。quarter to five这类"钟表时间"会被特殊拦截,避免误当成日期区间。最后若发现start晚于end,还会自动交换两者以保证区间方向正确。

4. JSON 输出(toJSON):toJSON.js 通过end - start计算duration字段(删除毫秒与秒),并推导unitrepeat,最终产出{ start, end, timezone, duration, unit?, repeat? }的扁平结构。

设计决策与"观点"(Opinions)

README 用专门的章节记录了插件在歧义场景下的取舍,这些决策对集成方至关重要:

一周从周一开始

默认情况下,一周从周一开始,'next week'表示周一早上到周日晚上。README 说明该配置目前没有透传给 spacetime,属于插件内固定行为。

隐含时长(Implied durations)

'after October'默认返回从Nov 1st开始、持续2 周的区间。可通过punt覆盖:

doc.dates({ punt: { month: 1 } })

未来倾向(Future bias)

'May 7th'倾向返回未来最近的 5 月 7 日;但在当前月份内会回退到过去日期:

// 假设今天是 3 月 2 日 nlp('feb 30th').dates({ today: '2021-02-01' }).get()

This / Next / Last 的语义

  • this monday:裸的monday总是指它自己或即将到来的周一——周一当天说this monday是当天,周二说则指下周一。this june同理,6 月说指当月,其他月份指最近的未来 6 月。README 也坦言,未来版本可以考虑借助句子时态(i paid on mondayvsi will pay on monday)进一步消歧。
  • last monday:周二说last monday不是昨天,而是-1 周a week ago monday同样有效;this past monday才指昨天。'last X'若跨过周起始点可能少于 7 天,例如周一说的last friday只有几天前。README 对比了同类库:Wit.ai 与 chronic 返回昨天,Natty 与 SugarJS 与本插件一致返回 -1 周。
  • next wednesday:周二说next wednesday不是明天,而是+1 周a week wednesday同样 +1 周;this coming wednesday才是明天。此处 Wit.ai、chronic、Natty 均返回明天,SugarJS 与本插件一致返回 +1 周。

第 N 周(Nth Week)

一个月的"第一周"或一年的"第一周"定义为包含周四的那一周——这是广泛采用但略显奇怪的惯例(README 猜测源自军事格式),且不易配置。因此first week of January的起始日可能是 12 月的某个周一;而first monday of January则必然落在 1 月内。

英美日期歧义

默认与 JavaScript 保持一致:01/02/2020按美式解析为1 月 2 日,但13/01/2020会按英式解析为1 月 13 日(因为 13 不可能是月份)。若想强制02/03/1999的解释,用dmy: true

nlp('02/03/1999').dates().get() // February 3(美式) nlp('02/03/1999').dates({dmy:true}).get() // March 2(英式)

ISO 日期(如1999-03-02)不受该选项影响。对应测试见 dmy.test.js。

季节与昼夜

默认'this summer'返回6 月 1 日 – 9 月 1 日(北半球 ISO 定义),半球配置未来可能支持。lunch time等词有硬编码时刻;一般情况下一天从12:00am开始、到11:59pm(当天最后一毫秒)结束。

无效日期(Invalid dates)

compromise 会先把"看起来像日期"的东西打上标签,但直到解析时才校验有效性

  • 'january 34th 2020'→ 返回Jan 31 2020(钳制到当月最后一天);
  • 'tomorrow at 2:62pm'→ 直接返回'tomorrow'(丢弃无效时间);
  • '6th week of february'→ 返回 3 月的第 2 周(越界顺延);
  • 遇到 DST 跳变中被跳过或重复的小时,返回离 DST 变更最近的合法时间。

包含/排他区间

'between january and march'排他的——结束于 3 月开始之前;'january to march'包含的——结束于 3 月的最后一天。README 承认这在日常语义中通常模棱两可。

日期贪婪度(Date greediness)

插件默认对输入文本不做假设,尽力避免误报。若你能确认文本里必然包含日期,可以用 compromise 的nlp.extend提高打标强度(这是 README 提供的官方示例):

nlp.extend(function (Doc, world) { // 歧义缩写词 world.addWords({ weds: 'WeekDay', wed: 'WeekDay', sat: 'WeekDay', sun: 'WeekDay', }) world.postProcess(doc => { // 把 '2nd quarter' 标记为日期 doc.match('#Ordinal quarter').tag('#Date') // 把 '2/2' 标记为日期(而非分数) doc.match('/[0-9]{1,2}/[0-9]{1,2}/').tag('#Date') }) })

杂项行为

  • 'thursday the 16th'会强制落到 16 日,即便 16 日并非周四;
  • 'in a few hours/years'按 3 小时/年计,'a couple of'按 2 计;
  • 'jan 5th 2008 to Jan 6th the following year'支持跨年显式引用;
  • 'half past 5'默认按下午 5 点(5pm)处理。

边界与限制:诚实的清单

README 同样坦白记录了插件"做得勉强"和"做不到"的场景,集成前务必知晓:

处理得勉强的(awkward)

输入说明
middle of 2019/June尝试寻找"大致中心",返回 June 15
good friday 2025尝试推算天文设定的节假日
Oct 22 1975 2am in PST历史 DST 变更(默认按当前 DST 规则计算)

不支持的(doesn't do)

输入说明
not this Saturday, but the Saturday after自引用逻辑
3 years ago tomorrow口语化省略表达
2100军事时间格式

测试与可靠性

插件在 tests 目录 下配备了 30+ 个测试文件,覆盖am big的典型场景:ambig-monthambig-weekbefore-afterdmydurationfull-isotimezonetodaytokenizerweek等,另有 false-positive.test.js 专门防守误报。测试通过 tests/_lib.js 同时验证源码版(../src/plugin.js)与构建产物版(builds/compromise-dates.mjs),确保发布包与源码行为一致。运行方式:

cd plugins/dates npm test # 源码测试 npm run testb # 生产构建产物测试

可交互演示位于 plugins/dates/demo/index.html,便于在不写代码的情况下快速验证各种输入。

依赖与生态位置

从 package.json 可以看到插件的技术底座:

  • spacetime:承担时区、夏令时(DST)与所有日历算术——README 明确指出 Tokenization 与消歧由 compromise 负责,时区与 DST 推算交给 spacetime,数字解析复用 compromise-numbers,非正式时区名(eastern time)则由 spacetime-informal 调和;
  • spacetime-holiday:提供easterchristmas eve等节假日推算;
  • peerDependencies:要求compromise >=14.2.0

README 的"About"一节给出了作者的核心立场:正则表达式太脆弱、神经网络太飘忽、商业公司不该垄断通用日期解析——他们认为基于规则与简单 NLP 的开源社区库才是构建自然语言日期解析器的最优解,这也是整个 compromise 项目"modest natural-language processing"定位在日期领域的落地体现。

总结

compromise-dates 用一套清晰的"标签查找 → 单日期解析 → 区间组合 → JSON 输出"管线,把自然语言日期解析做成了三个简单方法(dates()/durations()/times())。掌握 context 配置(timezonetodaypuntdayStart/dayEnddmy)与 README 中记录的设计决策,就能预判它在歧义输入下的行为,从而在自己的应用中可靠地使用它。配合format()isBefore/isAfter/isSame,它足以支撑日程提醒、内容提取、报表归档等常见的时间信息处理需求。

【免费下载链接】compromisemodest natural-language processing项目地址: https://gitcode.com/gh_mirrors/co/compromise

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 13:58:56

DPP是什么,哪些企业需要注册DPP,中国企业怎么注册DPP?

数字产品护照DPP&#xff1a;中国企业如何完成欧盟注册合规 2026年7月20日&#xff0c;欧盟数字产品护照&#xff08;Digital Product Passport&#xff0c;简称DPP&#xff09;中央注册系统正式上线运行。这意味着&#xff0c;DPP不再停留在政策讨论层面&#xff0c;而是进入了…

作者头像 李华
网站建设 2026/9/23 13:58:47

短剧APP开发核心技术解析与架构设计

1. 短剧行业现状与市场机会最近两年&#xff0c;短剧市场呈现爆发式增长。根据行业数据显示&#xff0c;2023年短剧市场规模已突破百亿&#xff0c;用户日均观看时长达到45分钟以上。这种介于短视频和长视频之间的内容形式&#xff0c;凭借其紧凑的剧情节奏和沉浸式观看体验&am…

作者头像 李华
网站建设 2026/9/23 13:57:54

SMIC40LL流片签核实操指南:功耗、时序与电迁移协同验证

简介&#xff1a;本资源是中芯国际&#xff08;SMIC&#xff09;官方发布的40LL工艺节点设计签核&#xff08;Sign-off&#xff09;技术指南&#xff0c;面向集成电路设计工程师、物理验证工程师及高校微电子相关专业高年级学生与研究人员&#xff0c;用于指导基于SMIC 40LL工艺…

作者头像 李华
网站建设 2026/9/23 13:57:18

Python调OKX V5 API实战:签名、限频与模拟盘全流程指南

简介&#xff1a;OKEx交易所Web API的Python调用示例&#xff0c;覆盖杠杆交易、现货交易、历史记录与历史数据获取等核心场景&#xff0c;适合具备Python基础、想对接加密货币交易所API的开发者与量化交易初学者。资源包共12个文件&#xff0c;全部为.py脚本&#xff0c;按现货…

作者头像 李华
网站建设 2026/9/23 13:56:50

硕士论文AI生成实测:深度和结构能达标吗

硕士论文写作中&#xff0c;AI工具最受质疑的并非效率&#xff0c;而是生成内容的学术深度与结构严谨性。本次实测不聊泛泛的“AI写论文”&#xff0c;而是聚焦“深度”与“结构”两个硬指标&#xff0c;对市面主流工具做一次压力测试。 测试样本选取某文科类硕士学位论文的“…

作者头像 李华