cn-time-parser 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: "cn-time-parser"
3
+ description: "智能识别中文时间关键词并转换为具体日期和时间戳;使用lunisolar模块处理农历计算,chinese-workday模块处理节日和工作日判断;支持解析复杂农历日期和判断节假日;当用户需要解析自然语言中的时间信息、计算特定日期、处理农历节日转换时使用"
4
+ ---
5
+
6
+ # 时间解析器
7
+
8
+ ## 任务目标
9
+ - 本Skill用于:解析中文自然语言中的时间关键词,转换为具体的日期和时间戳
10
+ - 能力包含:识别常见中文时间关键词(今天、昨天、明天、前天、今年、去年等)、处理传统农历节日、解析具体农历日期(如农历二月二十五)、判断工作日和节假日、计算特定日期的时间戳
11
+ - 触发条件:用户提问中包含时间相关关键词,需要获取具体日期或时间戳
12
+
13
+ ## 前置准备
14
+ - 依赖说明:需要安装Node.js环境,以及dayjs、lunisolar和chinese-workday模块
15
+ - 准备命令:在使用前执行 `npm install`
16
+
17
+ ## 全局安装(可选)
18
+ ### 安装为全局命令
19
+ ```bash
20
+ npm install -g .
21
+ ```
22
+
23
+ ### 使用 npm link(开发时推荐)
24
+ ```bash
25
+ npm link
26
+ ```
27
+ 链接后本地修改即时生效,无需重复安装。卸载链接:`npm unlink -g cn-time-parser`
28
+
29
+ ### 全局使用
30
+ 安装/链接后可以直接在任何位置使用:
31
+ ```bash
32
+ cn-time-parser -q "今天"
33
+ cn-time-parser "农历二月二十五"
34
+ cn-time-parser --help
35
+ ```
36
+
37
+ ## 操作步骤
38
+ - 标准流程:
39
+ 1. 分析用户提问,提取时间关键词
40
+ 2. 调用脚本处理时间关键词:
41
+ - 脚本调用示例:`cn-time-parser --query "今天"`
42
+ 3. 根据返回的具体日期进行后续操作
43
+ - 可选分支:
44
+ - 当检测到农历节日关键词时:使用lunisolar模块进行农历转换
45
+ - 当需要判断节假日时:使用chinese-workday模块获取节日信息和工作日判断
46
+ - 当需要时间戳时:自动生成Unix时间戳
47
+
48
+ ## 使用示例
49
+ - 示例1:
50
+ - 场景/输入:用户问"明天是什么日子"
51
+ - 预期产出:解析出明天的具体日期(如2026-05-09)
52
+ - 关键要点:根据当前系统时间计算明天
53
+ - 示例2:
54
+ - 场景/输入:用户问"去年过年是哪一天"
55
+ - 预期产出:解析出去年春节的具体日期
56
+ - 关键要点:使用农历转换功能
57
+ - 示例3:
58
+ - 场景/输入:用户问"黄龙的生日是农历二月二十五,明年他的生日是公历的哪一天?"
59
+ - 预期产出:解析出明年农历二月二十五对应的公历日期
60
+ - 关键要点:支持解析具体农历日期并进行年份调整
61
+ - 示例4:
62
+ - 场景/输入:用户问"今年五一是不是工作日?"
63
+ - 预期产出:解析出五一的日期,并提供isWorkday和isHoliday字段
64
+ - 关键要点:使用chinese-workday模块判断节假日
65
+ - 示例5:
66
+ - 场景/输入:用户问"国庆节是哪一天"
67
+ - 预期产出:解析出今年国庆节的日期(默认当年)
68
+ - 关键要点:节日名称不带年份前缀时默认使用当前年份
69
+ - 示例6:
70
+ - 场景/输入:用户问"清明节"
71
+ - 预期产出:解析出今年清明节的日期(基于节气计算)
72
+ - 关键要点:清明节是节气节日,日期在4月4-5日间浮动
73
+ - 示例7:
74
+ - 场景/输入:用户问"元宵节"
75
+ - 预期产出:解析出今年元宵节的日期(农历正月十五)
76
+ - 关键要点:元宵节是农历节日,通过lunisolar转换
77
+
78
+ ## CLI命令行使用
79
+ ### 基础调用方式
80
+ - `cn-time-parser --query "今天"` - 使用--query参数
81
+ - `cn-time-parser -q "明天"` - 使用-q快捷参数
82
+ - `cn-time-parser "农历二月二十五"` - 直接传入位置参数
83
+
84
+ ### 输出格式选项
85
+ - `--format json` - JSON格式输出(默认)
86
+ - `--format text` - 文本格式输出
87
+ - `--format simple` - 仅输出日期
88
+
89
+ ### 其他选项
90
+ - `--examples` - 显示使用示例
91
+ - `--help` - 显示帮助信息
92
+ - `--year 2025` - 指定年份(用于农历日期转换)
93
+ - `--verbose, -v` - 显示详细信息
94
+ - `--version` - 显示版本号
95
+
96
+ ### npm脚本
97
+ - `npm test` - 运行测试
98
+ - `npm start` - 启动程序
99
+ - `npm run examples` - 显示示例
100
+
101
+ ## JSON 输出字段说明
102
+ 成功时返回以下 JSON 结构:
103
+ | 字段 | 说明 | 示例 |
104
+ |---|---|---|
105
+ | `success` | 是否解析成功 | `true` / `false` |
106
+ | `originalQuery` | 原始输入 | `"今天"` |
107
+ | `parsedDate` | 解析后的公历日期(YYYY-MM-DD) | `"2026-05-08"` |
108
+ | `timestamp` | Unix 毫秒时间戳 | `1778212479463` |
109
+ | `lunarInfo.year` | 农历年份 | `2026` |
110
+ | `lunarInfo.month` | 农历月份 | `1` |
111
+ | `lunarInfo.day` | 农历日 | `1` |
112
+ | `lunarInfo.isLeapMonth` | 是否为闰月 | `false` |
113
+ | `lunarInfo.zodiac` | 生肖 | `"马"` |
114
+ | `lunarInfo.term` | 节气 | `"立春"` |
115
+ | `festivalInfo` | 节日/假期名称 | `"春节"` / `"清明节"` / `"周末"` / `"工作日"` |
116
+ | `isWorkday` | 是否为工作日 | `true` / `false` |
117
+ | `isHoliday` | 是否为法定节假日 | `true` / `false` |
118
+ | `matchedKeyword` | 匹配到的关键词 | `"今年五一"` |
119
+
120
+ ## 资源索引
121
+ - 脚本:见 `scripts/parse-time.js`(用途与参数:解析时间关键词,支持--query参数传入用户提问;支持全局安装作为命令行工具使用)
122
+ - 参考:见 `references/time-keywords.md`(何时读取:需要了解所有支持的时间关键词时)
123
+ - 安装指南:见 `INSTALL.md`(何时读取:需要了解如何全局安装和使用命令行工具时)
124
+
125
+ ## 注意事项
126
+ - 脚本会根据当前系统时间进行计算,确保系统时间准确
127
+ - 农历转换基于lunisolar库,支持常见传统节日
128
+ - 节假日和工作日判断基于chinese-workday库
129
+ - 时间戳为毫秒级Unix时间戳
130
+ - `lunarInfo` 中的 `zodiac` 和 `term` 字段可能为 `null`,取决于 lunisolar 库对当前日期的数据覆盖
131
+ - `festivalInfo` 除具体节日名称外,也可能返回 `"周末"`、`"工作日"`、`"补劳动节"` 等描述
package/INSTALL.md CHANGED
@@ -1,54 +1,51 @@
1
1
  # 安装和使用指南
2
2
 
3
- ## 本地运行(无需安装)
4
-
5
- 在项目目录下直接使用 Node.js 运行:
3
+ ## 全局安装
6
4
 
7
5
  ```bash
8
- cd /path/to/cn-time-parser
9
- npm install # 首次使用前安装依赖
10
- node scripts/parse-time.js -q "今天"
11
- node scripts/parse-time.js "农历二月二十五"
6
+ npm install -g cn-time-parser
12
7
  ```
13
8
 
14
- 也可通过 npm scripts 快速启动:
9
+ ### 验证安装
15
10
 
16
11
  ```bash
17
- npm start -- --query "今天" # run start 脚本
18
- npm test # 运行测试(查询"今天")
19
- npm run examples # 显示使用示例
12
+ cn-time-parser --version
13
+ cn-time-parser --help
14
+ cn-time-parser --examples
20
15
  ```
21
16
 
22
- ## 全局安装
17
+ ## MCP 模式
23
18
 
24
- ### 方式一:npm link(开发时推荐)
19
+ 支持作为 MCP (Model Context Protocol) Server 使用,可集成到支持 MCP 的 AI 助手中。
25
20
 
26
- 将项目链接到全局,本地修改即时生效:
27
21
 
28
- ```bash
29
- cd /path/to/cn-time-parser
30
- npm link
31
- ```
22
+ ### AI 助手配置
32
23
 
33
- 之后可直接使用 `cn-time-parser` 命令。解除链接:
24
+ AI 助手的 MCP 配置文件中添加:
34
25
 
35
- ```bash
36
- npm unlink -g cn-time-parser
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "cn-time-parser": {
30
+ "command": "cn-time-parser-mcp"
31
+ }
32
+ }
33
+ }
37
34
  ```
38
35
 
39
- ### 方式二:npm install -g(正式安装)
36
+ ### MCP 工具使用
40
37
 
41
- ```bash
42
- cd /path/to/cn-time-parser
43
- npm install -g .
44
- ```
38
+ **工具名称:** `parse_chinese_time`
45
39
 
46
- ### 验证安装
40
+ **输入参数:**
41
+ - `query` (string, 必填) - 时间关键词查询
42
+ - `year` (number, 可选) - 指定年份
43
+
44
+ **示例:**
47
45
 
48
- ```bash
49
- cn-time-parser --version
50
- cn-time-parser --help
51
- cn-time-parser --examples
46
+ ```
47
+ 用户:明天是什么日子?
48
+ AI 调用:parse_chinese_time({ query: "明天" })
52
49
  ```
53
50
 
54
51
  ## 使用方法
@@ -126,4 +123,4 @@ npm uninstall -g cn-time-parser
126
123
  ### npm link 解除
127
124
  ```bash
128
125
  npm unlink -g cn-time-parser
129
- ```
126
+ ```
package/README.md CHANGED
@@ -12,17 +12,76 @@
12
12
  - **节日匹配** — 春节、五一、国庆、元旦、清明、元宵,支持 `今年/去年/明年` 前缀
13
13
  - **输出信息** — 公历日期、Unix 毫秒时间戳、农历信息、生肖、节气、工作日/节假日判断
14
14
 
15
+ ## 快速开始
15
16
 
16
- ### 全局安装
17
+ ```bash
18
+ # 安装依赖
19
+ npm install -g cn-time-parser
20
+
21
+ # CLI 模式
22
+ cn-time-parser "今天"
23
+ cn-time-parser "农历二月二十五"
24
+
25
+ # MCP 模式(AI 助手集成)
26
+ cn-time-parser-mcp
27
+ ```
28
+
29
+ ## MCP 模式
30
+
31
+ 支持作为 MCP (Model Context Protocol) Server 使用,可集成到支持 MCP 的 AI 助手中。
32
+
33
+ ### 安装 MCP Server
17
34
 
18
35
  ```bash
36
+ # 全局安装
19
37
  npm install -g cn-time-parser
20
38
  ```
21
39
 
40
+ ### AI 助手配置
22
41
 
23
- ## CLI 用法
42
+ AI 助手的 MCP 配置中添加:
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "cn-time-parser": {
48
+ "command": "cn-time-parser-mcp"
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### MCP 工具
55
+
56
+ **工具名称:** `parse_chinese_time`
57
+
58
+ **输入参数:**
24
59
 
60
+ | 参数 | 类型 | 必填 | 说明 |
61
+ |------|------|------|------|
62
+ | `query` | string | 是 | 时间关键词查询,如"今天"、"农历二月二十五" |
63
+ | `year` | number | 否 | 指定年份,默认为当前年份 |
64
+
65
+ **使用示例:**
66
+
67
+ ```
68
+ 用户:明天是什么日子?
69
+ AI 调用:parse_chinese_time({ query: "明天" })
70
+ AI 返回:{
71
+ "success": true,
72
+ "originalQuery": "明天",
73
+ "parsedDate": "2026-05-09",
74
+ "timestamp": 1778294400000,
75
+ "festivalInfo": "周末",
76
+ "isWorkday": false,
77
+ "isHoliday": false,
78
+ "matchedKeyword": "明天"
79
+ }
25
80
  ```
81
+
82
+ ## CLI 用法
83
+
84
+ ```bash
26
85
  cn-time-parser [选项] [查询...]
27
86
  ```
28
87
 
@@ -149,7 +208,8 @@ cn-time-parser "农历二月二十五" --year 2025
149
208
  - [lunisolar](https://github.com/magiclav/Lunisolar) — 农历/公历转换
150
209
  - [chinese-workday](https://github.com/nicedoc/chinese-workday) — 中国节假日判断
151
210
  - [argparse](https://github.com/nodeca/argparse) — CLI 参数解析
211
+ - [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/sdk) — MCP Server SDK
152
212
 
153
213
  ## 许可证
154
214
 
155
- MIT
215
+ MIT
package/package.json CHANGED
@@ -1,24 +1,31 @@
1
1
  {
2
2
  "name": "cn-time-parser",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "智能识别中文时间关键词并转换为具体日期和时间戳",
6
6
  "main": "scripts/parse-time.js",
7
7
  "bin": {
8
- "cn-time-parser": "./scripts/parse-time.js"
8
+ "cn-time-parser": "./scripts/parse-time.js",
9
+ "cn-time-parser-mcp": "./scripts/mcp-server.js"
9
10
  },
10
11
  "scripts": {
11
- "test": "node scripts/parse-time.js --query '今天'",
12
+ "test": "node scripts/parse-time.js --query 今天",
12
13
  "start": "node scripts/parse-time.js",
13
14
  "examples": "node scripts/parse-time.js --examples"
14
15
  },
15
16
  "dependencies": {
16
- "dayjs": "^1.11.10",
17
- "lunisolar": "^2.0.2",
17
+ "@modelcontextprotocol/sdk": "^1.29.0",
18
18
  "argparse": "^2.0.1",
19
- "chinese-workday": "^1.11.0"
19
+ "chinese-workday": "^1.11.0",
20
+ "dayjs": "^1.11.10",
21
+ "lunisolar": "^2.0.2"
20
22
  },
21
- "keywords": ["time", "parser", "chinese", "lunar"],
22
- "author": "",
23
+ "keywords": [
24
+ "time",
25
+ "parser",
26
+ "chinese",
27
+ "lunar"
28
+ ],
29
+ "author": "as3long",
23
30
  "license": "MIT"
24
31
  }
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { resolve } from 'path';
5
+ import { realpathSync } from 'fs';
6
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import {
9
+ CallToolRequestSchema,
10
+ ListToolsRequestSchema,
11
+ } from '@modelcontextprotocol/sdk/types.js';
12
+
13
+ import dayjs from 'dayjs';
14
+ import lunisolar from 'lunisolar';
15
+ import * as chineseWorkday from 'chinese-workday';
16
+
17
+ const { isWorkday, isHoliday, getFestival } = chineseWorkday;
18
+
19
+ const chineseNumbers = {
20
+ '零': 0, '〇': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
21
+ '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
22
+ '廿': 20, '卅': 30
23
+ };
24
+
25
+ function parseChineseNumber(text) {
26
+ if (!text) return null;
27
+ if (text === '正') return 1;
28
+ if (/^\d+$/.test(text)) return parseInt(text);
29
+
30
+ let result = 0;
31
+ let temp = 0;
32
+
33
+ for (let i = 0; i < text.length; i++) {
34
+ const char = text[i];
35
+ if (char === '十') {
36
+ if (temp === 0) temp = 1;
37
+ result += temp * 10;
38
+ temp = 0;
39
+ } else if (char === '廿') {
40
+ result += 20;
41
+ } else if (char === '卅') {
42
+ result += 30;
43
+ } else if (chineseNumbers[char] !== undefined) {
44
+ temp = chineseNumbers[char];
45
+ }
46
+ }
47
+ result += temp;
48
+ return result > 0 ? result : null;
49
+ }
50
+
51
+ function parseLunarDate(query, baseYear) {
52
+ let processedQuery = query.replace(/正月/g, '一月');
53
+
54
+ const lunarPatterns = [
55
+ {
56
+ regex: /农历(\d{4})年?(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
57
+ handler: (match) => {
58
+ const year = parseInt(match[1]);
59
+ const monthStr = match[2] === '正' ? '一' : match[2];
60
+ const month = parseChineseNumber(monthStr);
61
+ const day = parseChineseNumber(match[3]);
62
+ const isLeap = match[0].includes('闰');
63
+ return { year, month, day, isLeap, fullMatch: match[0] };
64
+ }
65
+ },
66
+ {
67
+ regex: /(今年|去年|明年|后年|前年).*?农历(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
68
+ handler: (match) => {
69
+ const yearModifier = match[1];
70
+ let year = baseYear;
71
+ if (yearModifier === '去年') year -= 1;
72
+ if (yearModifier === '明年') year += 1;
73
+ if (yearModifier === '后年') year += 2;
74
+ if (yearModifier === '前年') year -= 2;
75
+
76
+ const monthStr = match[2] === '正' ? '一' : match[2];
77
+ const month = parseChineseNumber(monthStr);
78
+ const day = parseChineseNumber(match[3]);
79
+ const isLeap = match[0].includes('闰');
80
+ return { year, month, day, isLeap, fullMatch: match[0] };
81
+ }
82
+ },
83
+ {
84
+ regex: /农历(今年|去年|明年|后年|前年)(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
85
+ handler: (match) => {
86
+ const yearModifier = match[1];
87
+ let year = baseYear;
88
+ if (yearModifier === '去年') year -= 1;
89
+ if (yearModifier === '明年') year += 1;
90
+ if (yearModifier === '后年') year += 2;
91
+ if (yearModifier === '前年') year -= 2;
92
+
93
+ const monthStr = match[2] === '正' ? '一' : match[2];
94
+ const month = parseChineseNumber(monthStr);
95
+ const day = parseChineseNumber(match[3]);
96
+ const isLeap = match[0].includes('闰');
97
+ return { year, month, day, isLeap, fullMatch: match[0] };
98
+ }
99
+ },
100
+ {
101
+ regex: /农历(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
102
+ handler: (match) => {
103
+ const monthStr = match[1] === '正' ? '一' : match[1];
104
+ const month = parseChineseNumber(monthStr);
105
+ const day = parseChineseNumber(match[2]);
106
+ const isLeap = match[0].includes('闰');
107
+ return { year: baseYear, month, day, isLeap, fullMatch: match[0] };
108
+ }
109
+ }
110
+ ];
111
+
112
+ for (const pattern of lunarPatterns) {
113
+ const match = processedQuery.match(pattern.regex);
114
+ if (match) {
115
+ try {
116
+ const lunarData = pattern.handler(match);
117
+ if (lunarData && lunarData.month && lunarData.day) {
118
+ const lunarDate = lunisolar.fromLunar({
119
+ year: lunarData.year,
120
+ month: lunarData.month,
121
+ day: lunarData.day,
122
+ isLeapMonth: lunarData.isLeap
123
+ });
124
+
125
+ let matchedKeyword = lunarData.fullMatch || match[0];
126
+ if (query.includes('正月') && matchedKeyword.includes('一月')) {
127
+ matchedKeyword = matchedKeyword.replace('一月', '正月');
128
+ }
129
+
130
+ return {
131
+ date: dayjs(lunarDate.toDate()),
132
+ matchedKeyword: matchedKeyword,
133
+ lunarData: lunarData
134
+ };
135
+ }
136
+ } catch (error) {
137
+ console.error('解析农历日期时出错:', error);
138
+ }
139
+ }
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ const festivalNameMap = {
146
+ '元旦': '元旦',
147
+ '春节': '春节',
148
+ '过年': '春节',
149
+ '五一': '劳动节',
150
+ '劳动节': '劳动节',
151
+ '国庆': '国庆节',
152
+ '国庆节': '国庆节',
153
+ '十一': '国庆节',
154
+ '清明': '清明节',
155
+ '清明节': '清明节',
156
+ '元宵': '元宵节',
157
+ '元宵节': '元宵节'
158
+ };
159
+
160
+ function getFestivalDate(year, festivalName) {
161
+ const normalizedName = festivalNameMap[festivalName] || festivalName;
162
+
163
+ if (normalizedName === '元旦') return dayjs(`${year}-01-01`);
164
+ if (normalizedName === '劳动节') return dayjs(`${year}-05-01`);
165
+ if (normalizedName === '国庆节') return dayjs(`${year}-10-01`);
166
+ if (normalizedName === '春节') return getSpringFestival(year);
167
+ if (normalizedName === '清明节') return getQingmingDate(year);
168
+ if (normalizedName === '元宵节') return getLanternFestival(year);
169
+
170
+ return getSpringFestival(year);
171
+ }
172
+
173
+ function getSpringFestival(year) {
174
+ try {
175
+ const lunarNewYear = lunisolar.fromLunar({
176
+ year: year,
177
+ month: 1,
178
+ day: 1,
179
+ isLeapMonth: false
180
+ });
181
+ return dayjs(lunarNewYear.toDate());
182
+ } catch (error) {
183
+ return dayjs(`${year}-02-01`);
184
+ }
185
+ }
186
+
187
+ function getQingmingDate(year) {
188
+ try {
189
+ for (let d = 1; d <= 10; d++) {
190
+ const date = new Date(year, 3, d);
191
+ const ls = lunisolar(date);
192
+ if (ls.solarTerm && ls.solarTerm.name === '清明') {
193
+ return dayjs(date);
194
+ }
195
+ }
196
+ return dayjs(`${year}-04-05`);
197
+ } catch (error) {
198
+ return dayjs(`${year}-04-05`);
199
+ }
200
+ }
201
+
202
+ function getLanternFestival(year) {
203
+ try {
204
+ const lunarDate = lunisolar.fromLunar({
205
+ year: year,
206
+ month: 1,
207
+ day: 15,
208
+ isLeapMonth: false
209
+ });
210
+ return dayjs(lunarDate.toDate());
211
+ } catch (error) {
212
+ return dayjs(`${year}-02-15`);
213
+ }
214
+ }
215
+
216
+ function parseTimeQuery(query, options = {}) {
217
+ const now = dayjs();
218
+ const baseYear = options.year || now.year();
219
+
220
+ let result = {
221
+ success: false,
222
+ originalQuery: query,
223
+ parsedDate: null,
224
+ timestamp: null,
225
+ lunarInfo: null,
226
+ festivalInfo: null,
227
+ isWorkday: null,
228
+ isHoliday: null,
229
+ matchedKeyword: null
230
+ };
231
+
232
+ const lunarResult = parseLunarDate(query, baseYear);
233
+ if (lunarResult) {
234
+ const date = lunarResult.date;
235
+ result.success = true;
236
+ result.parsedDate = date.format('YYYY-MM-DD');
237
+ result.timestamp = date.valueOf();
238
+ result.matchedKeyword = lunarResult.matchedKeyword;
239
+
240
+ try {
241
+ const lunar = lunisolar(date.toDate());
242
+ result.lunarInfo = {
243
+ year: lunar.lunarYear,
244
+ month: lunar.lunarMonth,
245
+ day: lunar.lunarDay,
246
+ isLeapMonth: lunar.isLeapMonth,
247
+ zodiac: lunar.yearZodiac ? lunar.yearZodiac.name : null,
248
+ term: lunar.solarTerm ? lunar.solarTerm.name : null
249
+ };
250
+ } catch (e) {
251
+ console.error('获取农历信息时出错:', e);
252
+ }
253
+
254
+ try {
255
+ result.festivalInfo = getFestival(date.toDate());
256
+ result.isWorkday = isWorkday(date.toDate());
257
+ result.isHoliday = isHoliday(date.toDate());
258
+ } catch (e) {
259
+ console.error('获取节日信息时出错:', e);
260
+ }
261
+
262
+ return result;
263
+ }
264
+
265
+ const keywords = [
266
+ { regex: /今天/, handler: () => now },
267
+ { regex: /昨天/, handler: () => now.subtract(1, 'day') },
268
+ { regex: /明天/, handler: () => now.add(1, 'day') },
269
+ { regex: /前天/, handler: () => now.subtract(2, 'day') },
270
+ { regex: /后天/, handler: () => now.add(2, 'day') },
271
+ { regex: /大前天/, handler: () => now.subtract(3, 'day') },
272
+ { regex: /大后天/, handler: () => now.add(3, 'day') },
273
+ { regex: /今年/, handler: () => now.startOf('year') },
274
+ { regex: /去年/, handler: () => now.subtract(1, 'year').startOf('year') },
275
+ { regex: /明年/, handler: () => now.add(1, 'year').startOf('year') }
276
+ ];
277
+
278
+ const festivalKeywords = [
279
+ { regex: /今年春节|今年过年/, handler: () => getFestivalDate(baseYear, '春节') },
280
+ { regex: /去年春节|去年过年/, handler: () => getFestivalDate(baseYear - 1, '春节') },
281
+ { regex: /明年春节|明年过年/, handler: () => getFestivalDate(baseYear + 1, '春节') },
282
+ { regex: /今年五一/, handler: () => getFestivalDate(baseYear, '五一') },
283
+ { regex: /去年五一/, handler: () => getFestivalDate(baseYear - 1, '五一') },
284
+ { regex: /明年五一/, handler: () => getFestivalDate(baseYear + 1, '五一') },
285
+ { regex: /今年十一|今年国庆/, handler: () => getFestivalDate(baseYear, '国庆') },
286
+ { regex: /去年十一|去年国庆/, handler: () => getFestivalDate(baseYear - 1, '国庆') },
287
+ { regex: /明年十一|明年国庆/, handler: () => getFestivalDate(baseYear + 1, '国庆') },
288
+ { regex: /今年元旦/, handler: () => getFestivalDate(baseYear, '元旦') },
289
+ { regex: /去年元旦/, handler: () => getFestivalDate(baseYear - 1, '元旦') },
290
+ { regex: /明年元旦/, handler: () => getFestivalDate(baseYear + 1, '元旦') },
291
+ { regex: /今年清明/, handler: () => getFestivalDate(baseYear, '清明节') },
292
+ { regex: /去年清明/, handler: () => getFestivalDate(baseYear - 1, '清明节') },
293
+ { regex: /明年清明/, handler: () => getFestivalDate(baseYear + 1, '清明节') },
294
+ { regex: /今年元宵/, handler: () => getFestivalDate(baseYear, '元宵节') },
295
+ { regex: /去年元宵/, handler: () => getFestivalDate(baseYear - 1, '元宵节') },
296
+ { regex: /明年元宵/, handler: () => getFestivalDate(baseYear + 1, '元宵节') },
297
+ { regex: /春节|过年/, handler: () => getFestivalDate(baseYear, '春节') },
298
+ { regex: /五一|劳动节/, handler: () => getFestivalDate(baseYear, '五一') },
299
+ { regex: /十一|国庆节|国庆/, handler: () => getFestivalDate(baseYear, '国庆') },
300
+ { regex: /元旦/, handler: () => getFestivalDate(baseYear, '元旦') },
301
+ { regex: /清明节|清明/, handler: () => getFestivalDate(baseYear, '清明节') },
302
+ { regex: /元宵节|元宵/, handler: () => getFestivalDate(baseYear, '元宵节') }
303
+ ];
304
+
305
+ const allKeywords = [...festivalKeywords, ...keywords];
306
+
307
+ for (const keyword of allKeywords) {
308
+ if (keyword.regex.test(query)) {
309
+ try {
310
+ const date = keyword.handler();
311
+ result.success = true;
312
+ result.parsedDate = date.format('YYYY-MM-DD');
313
+ result.timestamp = date.valueOf();
314
+ result.matchedKeyword = query.match(keyword.regex)[0];
315
+
316
+ try {
317
+ const lunar = lunisolar(date.toDate());
318
+ result.lunarInfo = {
319
+ year: lunar.lunarYear,
320
+ month: lunar.lunarMonth,
321
+ day: lunar.lunarDay,
322
+ isLeapMonth: lunar.isLeapMonth,
323
+ zodiac: lunar.yearZodiac ? lunar.yearZodiac.name : null,
324
+ term: lunar.solarTerm ? lunar.solarTerm.name : null
325
+ };
326
+ } catch (e) {
327
+ console.error('获取农历信息时出错:', e);
328
+ }
329
+
330
+ try {
331
+ result.festivalInfo = getFestival(date.toDate());
332
+ result.isWorkday = isWorkday(date.toDate());
333
+ result.isHoliday = isHoliday(date.toDate());
334
+ } catch (e) {
335
+ console.error('获取节日信息时出错:', e);
336
+ }
337
+
338
+ break;
339
+ } catch (error) {
340
+ console.error('处理时间关键词时出错:', error);
341
+ }
342
+ }
343
+ }
344
+
345
+ return result;
346
+ }
347
+
348
+ class CNTimeParserServer {
349
+ constructor() {
350
+ this.server = new Server(
351
+ {
352
+ name: 'cn-time-parser',
353
+ version: '1.0.1',
354
+ },
355
+ {
356
+ capabilities: {
357
+ tools: {},
358
+ },
359
+ }
360
+ );
361
+
362
+ this.setupToolHandlers();
363
+ }
364
+
365
+ setupToolHandlers() {
366
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
367
+ tools: [
368
+ {
369
+ name: 'parse_chinese_time',
370
+ description: '解析中文时间关键词并转换为具体日期和时间戳。支持:基础时间词(今天、明天、昨天等)、农历日期(农历二月二十五、闰二月等)、节日名称(春节、国庆、清明、元宵等)、年份偏移(今年、去年、明年)。当用户需要解析自然语言中的时间信息、计算特定日期、处理农历节日转换时使用。',
371
+ inputSchema: {
372
+ type: 'object',
373
+ properties: {
374
+ query: {
375
+ type: 'string',
376
+ description: '包含时间关键词的查询,例如:"今天"、"明天"、"农历二月二十五"、"今年五一"'
377
+ },
378
+ year: {
379
+ type: 'number',
380
+ description: '指定年份(用于农历转换),默认为当前年份',
381
+ default: new Date().getFullYear()
382
+ }
383
+ },
384
+ required: ['query']
385
+ }
386
+ }
387
+ ],
388
+ }));
389
+
390
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
391
+ try {
392
+ const { name, arguments: args } = request.params;
393
+
394
+ if (name === 'parse_chinese_time') {
395
+ const { query, year } = args;
396
+ const result = parseTimeQuery(query, { year });
397
+
398
+ return {
399
+ content: [
400
+ {
401
+ type: 'text',
402
+ text: JSON.stringify(result, null, 2)
403
+ }
404
+ ]
405
+ };
406
+ }
407
+
408
+ throw new Error(`Unknown tool: ${name}`);
409
+ } catch (error) {
410
+ return {
411
+ content: [
412
+ {
413
+ type: 'text',
414
+ text: `Error: ${error.message}`
415
+ }
416
+ ],
417
+ isError: true
418
+ };
419
+ }
420
+ });
421
+ }
422
+
423
+ async start() {
424
+ const transport = new StdioServerTransport();
425
+ await this.server.connect(transport);
426
+ console.error('cn-time-parser MCP Server started');
427
+ }
428
+ }
429
+
430
+ const server = new CNTimeParserServer();
431
+ server.start().catch(console.error);
@@ -67,7 +67,9 @@ const args = parser.parse_args();
67
67
 
68
68
  // 显示版本号
69
69
  if (args.version) {
70
- console.log('cn-time-parser v1.0.0');
70
+ const packageJsonPath = resolve(fileURLToPath(import.meta.url), '../../package.json');
71
+ const packageJson = JSON.parse(realpathSync(packageJsonPath));
72
+ console.log(`cn-time-parser v${packageJson.version}`);
71
73
  process.exit(0);
72
74
  }
73
75
 
@@ -77,27 +79,27 @@ if (args.examples) {
77
79
  === 使用示例 ===
78
80
 
79
81
  1. 基础用法:
80
- node scripts/parse-time.js --query "今天"
81
- node scripts/parse-time.js --q "明天"
82
- node scripts/parse-time.js "农历二月二十五"
82
+ cn-time-parser "今天"
83
+ cn-time-parser "明天"
84
+ cn-time-parser "农历二月二十五"
83
85
 
84
86
  2. 农历日期:
85
- node scripts/parse-time.js --query "农历正月十五"
86
- node scripts/parse-time.js --query "明年农历八月十五"
87
- node scripts/parse-time.js --query "农历2025年九月初九"
87
+ cn-time-parser "农历正月十五"
88
+ cn-time-parser "明年农历八月十五"
89
+ cn-time-parser "农历2025年九月初九"
88
90
 
89
91
  3. 节日查询:
90
- node scripts/parse-time.js --query "今年春节"
91
- node scripts/parse-time.js --query "明年五一"
92
- node scripts/parse-time.js --query "去年国庆"
92
+ cn-time-parser "今年春节"
93
+ cn-time-parser "明年五一"
94
+ cn-time-parser "去年国庆"
93
95
 
94
96
  4. 输出格式:
95
- node scripts/parse-time.js --query "今天" --format text
96
- node scripts/parse-time.js --query "今天" --format simple
97
+ cn-time-parser "今天" --format text
98
+ cn-time-parser "今天" --format simple
97
99
 
98
100
  5. 其他选项:
99
- node scripts/parse-time.js --query "农历二月" --year 2025
100
- node scripts/parse-time.js --query "今天" -v
101
+ cn-time-parser "农历二月" --year 2025
102
+ cn-time-parser "今天" -v
101
103
  `);
102
104
  process.exit(0);
103
105
  }