cn-time-parser 1.0.0
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.
- package/AGENTS.md +23 -0
- package/INSTALL.md +129 -0
- package/README.md +178 -0
- package/SKILL.md +140 -0
- package/package.json +24 -0
- package/references/time-keywords.md +133 -0
- package/scripts/parse-time.js +562 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Project Instructions
|
|
2
|
+
|
|
3
|
+
This file provides context for AI assistants working on this project.
|
|
4
|
+
|
|
5
|
+
## Project Type: Node.js
|
|
6
|
+
|
|
7
|
+
### Commands
|
|
8
|
+
- Install: `npm install`
|
|
9
|
+
- Test: `npm test`
|
|
10
|
+
- Build: `npm run build`
|
|
11
|
+
- Start: `npm start`
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Guidelines
|
|
15
|
+
|
|
16
|
+
- Follow existing code style and patterns
|
|
17
|
+
- Write tests for new functionality
|
|
18
|
+
- Keep changes focused and atomic
|
|
19
|
+
- Document public APIs
|
|
20
|
+
|
|
21
|
+
## Important Notes
|
|
22
|
+
|
|
23
|
+
<!-- Add project-specific notes here -->
|
package/INSTALL.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# 安装和使用指南
|
|
2
|
+
|
|
3
|
+
## 本地运行(无需安装)
|
|
4
|
+
|
|
5
|
+
在项目目录下直接使用 Node.js 运行:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd /path/to/cn-time-parser
|
|
9
|
+
npm install # 首次使用前安装依赖
|
|
10
|
+
node scripts/parse-time.js -q "今天"
|
|
11
|
+
node scripts/parse-time.js "农历二月二十五"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
也可通过 npm scripts 快速启动:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm start -- --query "今天" # run start 脚本
|
|
18
|
+
npm test # 运行测试(查询"今天")
|
|
19
|
+
npm run examples # 显示使用示例
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 全局安装
|
|
23
|
+
|
|
24
|
+
### 方式一:npm link(开发时推荐)
|
|
25
|
+
|
|
26
|
+
将项目链接到全局,本地修改即时生效:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd /path/to/cn-time-parser
|
|
30
|
+
npm link
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
之后可直接使用 `cn-time-parser` 命令。解除链接:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm unlink -g cn-time-parser
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 方式二:npm install -g(正式安装)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cd /path/to/cn-time-parser
|
|
43
|
+
npm install -g .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 验证安装
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cn-time-parser --version
|
|
50
|
+
cn-time-parser --help
|
|
51
|
+
cn-time-parser --examples
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 使用方法
|
|
55
|
+
|
|
56
|
+
### 基础用法
|
|
57
|
+
```bash
|
|
58
|
+
# 使用 --query 参数
|
|
59
|
+
cn-time-parser --query "今天"
|
|
60
|
+
|
|
61
|
+
# 使用 -q 快捷参数
|
|
62
|
+
cn-time-parser -q "明天"
|
|
63
|
+
|
|
64
|
+
# 直接传入位置参数
|
|
65
|
+
cn-time-parser "农历二月二十五"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 输出格式
|
|
69
|
+
```bash
|
|
70
|
+
# JSON格式(默认)
|
|
71
|
+
cn-time-parser --query "今天"
|
|
72
|
+
|
|
73
|
+
# 文本格式
|
|
74
|
+
cn-time-parser --query "今天" --format text
|
|
75
|
+
|
|
76
|
+
# 简单格式(仅日期)
|
|
77
|
+
cn-time-parser --query "今天" --format simple
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 其他选项
|
|
81
|
+
```bash
|
|
82
|
+
# 显示示例
|
|
83
|
+
cn-time-parser --examples
|
|
84
|
+
|
|
85
|
+
# 显示帮助
|
|
86
|
+
cn-time-parser --help
|
|
87
|
+
|
|
88
|
+
# 详细模式
|
|
89
|
+
cn-time-parser --query "今天" -v
|
|
90
|
+
|
|
91
|
+
# 指定年份(用于农历转换)
|
|
92
|
+
cn-time-parser --query "农历二月" --year 2025
|
|
93
|
+
|
|
94
|
+
# 显示版本号
|
|
95
|
+
cn-time-parser --version
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 常用示例
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 查询今天
|
|
102
|
+
cn-time-parser "今天"
|
|
103
|
+
|
|
104
|
+
# 查询农历日期
|
|
105
|
+
cn-time-parser "农历正月十五"
|
|
106
|
+
|
|
107
|
+
# 查询节日(支持今年/去年/明年)
|
|
108
|
+
cn-time-parser "今年春节"
|
|
109
|
+
cn-time-parser "去年五一"
|
|
110
|
+
cn-time-parser "明年国庆"
|
|
111
|
+
|
|
112
|
+
# 文本格式输出
|
|
113
|
+
cn-time-parser "明年农历八月十五" --format text
|
|
114
|
+
|
|
115
|
+
# 简单格式(仅日期)
|
|
116
|
+
cn-time-parser "明天" --format simple
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 卸载
|
|
120
|
+
|
|
121
|
+
### 全局安装
|
|
122
|
+
```bash
|
|
123
|
+
npm uninstall -g cn-time-parser
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### npm link 解除
|
|
127
|
+
```bash
|
|
128
|
+
npm unlink -g cn-time-parser
|
|
129
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# cn-time-parser
|
|
2
|
+
|
|
3
|
+
智能识别中文自然语言中的时间关键词,转换为具体公历日期和时间戳。
|
|
4
|
+
|
|
5
|
+
支持基础时间词(今天、明天、昨天等)、传统农历日期(二月二十五、闰二月等)、节日名称(春节、国庆、清明、元宵等),以及工作日/节假日判断。
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
- **基础日期** — 今天、明天、昨天、前天、后天、大前天、大后天
|
|
10
|
+
- **年份偏移** — 今年、去年、明年
|
|
11
|
+
- **农历日期解析** — `农历二月二十五`、`明年农历八月十五`、`农历2024年正月十五`、`闰二月` 等
|
|
12
|
+
- **节日匹配** — 春节、五一、国庆、元旦、清明、元宵,支持 `今年/去年/明年` 前缀
|
|
13
|
+
- **输出信息** — 公历日期、Unix 毫秒时间戳、农历信息、生肖、节气、工作日/节假日判断
|
|
14
|
+
|
|
15
|
+
## 快速开始
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 克隆项目后安装依赖
|
|
19
|
+
npm install
|
|
20
|
+
|
|
21
|
+
# 直接运行
|
|
22
|
+
node scripts/parse-time.js -q "今天"
|
|
23
|
+
node scripts/parse-time.js "农历二月二十五"
|
|
24
|
+
|
|
25
|
+
# 或通过 npm scripts
|
|
26
|
+
npm start -- --query "今年五一"
|
|
27
|
+
npm test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 全局安装
|
|
31
|
+
|
|
32
|
+
**方式一:npm link(开发推荐)**
|
|
33
|
+
```bash
|
|
34
|
+
cd /path/to/cn-time-parser
|
|
35
|
+
npm link
|
|
36
|
+
cn-time-parser -q "今天"
|
|
37
|
+
# 解除:npm unlink -g cn-time-parser
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**方式二:npm install -g**
|
|
41
|
+
```bash
|
|
42
|
+
cd /path/to/cn-time-parser
|
|
43
|
+
npm install -g .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## CLI 用法
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
node scripts/parse-time.js [选项] [查询...]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 传入查询
|
|
53
|
+
|
|
54
|
+
方式 | 示例
|
|
55
|
+
---|---
|
|
56
|
+
`--query` / `-q` | `--query "今天"` / `-q "明天"`
|
|
57
|
+
位置参数 | `"农历二月二十五"` / `"今年五一"`
|
|
58
|
+
|
|
59
|
+
### 选项
|
|
60
|
+
|
|
61
|
+
选项 | 说明
|
|
62
|
+
---|---
|
|
63
|
+
`--format <type>` | 输出格式:`json`(默认)、`text`、`simple`
|
|
64
|
+
`--year <int>` | 指定年份(用于农历转换)
|
|
65
|
+
`--verbose` / `-v` | 显示详细信息
|
|
66
|
+
`--examples` | 显示使用示例
|
|
67
|
+
`--version` | 显示版本号
|
|
68
|
+
`--help` | 显示帮助
|
|
69
|
+
|
|
70
|
+
### 输出格式
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# JSON(默认)
|
|
74
|
+
node scripts/parse-time.js -q "今年五一"
|
|
75
|
+
|
|
76
|
+
# 文本
|
|
77
|
+
node scripts/parse-time.js -q "明天" --format text
|
|
78
|
+
|
|
79
|
+
# 仅日期
|
|
80
|
+
node scripts/parse-time.js -q "后天" --format simple
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 支持的时间表达式
|
|
84
|
+
|
|
85
|
+
### 基础时间词
|
|
86
|
+
|
|
87
|
+
`今天` `昨天` `明天` `前天` `后天` `大前天` `大后天`
|
|
88
|
+
|
|
89
|
+
### 年份偏移
|
|
90
|
+
|
|
91
|
+
`今年` `去年` `明年`
|
|
92
|
+
|
|
93
|
+
### 农历日期
|
|
94
|
+
|
|
95
|
+
- `农历二月二十五` — 默认为当前年份
|
|
96
|
+
- `明年农历八月十五` — 支持年份偏移
|
|
97
|
+
- `农历2025年正月十五` — 指定公历年份
|
|
98
|
+
- `农历闰二月初五` — 支持闰月
|
|
99
|
+
|
|
100
|
+
### 节日名称
|
|
101
|
+
|
|
102
|
+
以下节日可直接查询,不带年份前缀时默认今年:
|
|
103
|
+
|
|
104
|
+
节日 | 可用的输入关键词
|
|
105
|
+
---|---
|
|
106
|
+
春节 | `春节` `过年` `今年春节` `去年春节` `明年春节`
|
|
107
|
+
元宵节 | `元宵` `元宵节` `今年元宵` `去年元宵` `明年元宵`
|
|
108
|
+
清明节 | `清明` `清明节` `今年清明` `去年清明` `明年清明`
|
|
109
|
+
劳动节 | `五一` `劳动节` `今年五一` `去年五一` `明年五一`
|
|
110
|
+
国庆节 | `十一` `国庆` `国庆节` `今年国庆` `去年国庆` `明年国庆`
|
|
111
|
+
元旦 | `元旦` `今年元旦` `去年元旦` `明年元旦`
|
|
112
|
+
|
|
113
|
+
## 输出字段
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"success": true,
|
|
118
|
+
"originalQuery": "今年五一",
|
|
119
|
+
"parsedDate": "2026-05-01",
|
|
120
|
+
"timestamp": 1777564800000,
|
|
121
|
+
"lunarInfo": {
|
|
122
|
+
"year": 2026,
|
|
123
|
+
"month": 3,
|
|
124
|
+
"day": 14,
|
|
125
|
+
"isLeapMonth": false,
|
|
126
|
+
"zodiac": "马",
|
|
127
|
+
"term": null
|
|
128
|
+
},
|
|
129
|
+
"festivalInfo": "劳动节",
|
|
130
|
+
"isWorkday": false,
|
|
131
|
+
"isHoliday": true,
|
|
132
|
+
"matchedKeyword": "今年五一"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
字段 | 说明
|
|
137
|
+
---|---
|
|
138
|
+
`success` | 是否解析成功
|
|
139
|
+
`originalQuery` | 原始输入
|
|
140
|
+
`parsedDate` | 公历日期 `YYYY-MM-DD`
|
|
141
|
+
`timestamp` | Unix 毫秒时间戳
|
|
142
|
+
`lunarInfo.*` | 农历年/月/日/是否闰月/生肖/节气
|
|
143
|
+
`festivalInfo` | 节日名称(可能为 `"工作日"` `"周末"` 等)
|
|
144
|
+
`isWorkday` | 是否工作日
|
|
145
|
+
`isHoliday` | 是否法定节假日
|
|
146
|
+
`matchedKeyword` | 匹配到的关键词
|
|
147
|
+
|
|
148
|
+
## 示例
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 基础日期
|
|
152
|
+
cn-time-parser "今天"
|
|
153
|
+
cn-time-parser "明天" --format simple
|
|
154
|
+
|
|
155
|
+
# 农历
|
|
156
|
+
cn-time-parser "农历正月十五"
|
|
157
|
+
cn-time-parser "明年农历八月十五" --format text
|
|
158
|
+
|
|
159
|
+
# 节日
|
|
160
|
+
cn-time-parser "春节"
|
|
161
|
+
cn-time-parser "去年五一"
|
|
162
|
+
cn-time-parser "清明节"
|
|
163
|
+
cn-time-parser "元宵节"
|
|
164
|
+
|
|
165
|
+
# 指定年份
|
|
166
|
+
cn-time-parser "农历二月" --year 2025
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 依赖
|
|
170
|
+
|
|
171
|
+
- [dayjs](https://day.js.org/) — 日期处理
|
|
172
|
+
- [lunisolar](https://github.com/magiclav/Lunisolar) — 农历/公历转换
|
|
173
|
+
- [chinese-workday](https://github.com/nicedoc/chinese-workday) — 中国节假日判断
|
|
174
|
+
- [argparse](https://github.com/nodeca/argparse) — CLI 参数解析
|
|
175
|
+
|
|
176
|
+
## 许可证
|
|
177
|
+
|
|
178
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
- 准备命令:在使用前执行 `cd /path/to/cn-time-parser && npm install`
|
|
16
|
+
|
|
17
|
+
## 全局安装(可选)
|
|
18
|
+
### 安装为全局命令
|
|
19
|
+
```bash
|
|
20
|
+
cd /path/to/cn-time-parser
|
|
21
|
+
npm install -g .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 使用 npm link(开发时推荐)
|
|
25
|
+
```bash
|
|
26
|
+
cd /path/to/cn-time-parser
|
|
27
|
+
npm link
|
|
28
|
+
```
|
|
29
|
+
链接后本地修改即时生效,无需重复安装。卸载链接:`npm unlink -g cn-time-parser`
|
|
30
|
+
|
|
31
|
+
### 全局使用
|
|
32
|
+
安装/链接后可以直接在任何位置使用:
|
|
33
|
+
```bash
|
|
34
|
+
cn-time-parser -q "今天"
|
|
35
|
+
cn-time-parser "农历二月二十五"
|
|
36
|
+
cn-time-parser --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 卸载
|
|
40
|
+
```bash
|
|
41
|
+
npm uninstall -g cn-time-parser
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
详细的安装和使用说明请参考 [INSTALL.md](INSTALL.md)
|
|
45
|
+
|
|
46
|
+
## 操作步骤
|
|
47
|
+
- 标准流程:
|
|
48
|
+
1. 分析用户提问,提取时间关键词
|
|
49
|
+
2. 调用脚本处理时间关键词:
|
|
50
|
+
- 脚本调用示例:`node scripts/parse-time.js --query "今天的天气怎么样"`
|
|
51
|
+
3. 根据返回的具体日期进行后续操作
|
|
52
|
+
- 可选分支:
|
|
53
|
+
- 当检测到农历节日关键词时:使用lunisolar模块进行农历转换
|
|
54
|
+
- 当需要判断节假日时:使用chinese-workday模块获取节日信息和工作日判断
|
|
55
|
+
- 当需要时间戳时:自动生成Unix时间戳
|
|
56
|
+
|
|
57
|
+
## 使用示例
|
|
58
|
+
- 示例1:
|
|
59
|
+
- 场景/输入:用户问"明天是什么日子"
|
|
60
|
+
- 预期产出:解析出明天的具体日期(如2023-10-05)
|
|
61
|
+
- 关键要点:根据当前系统时间计算明天
|
|
62
|
+
- 示例2:
|
|
63
|
+
- 场景/输入:用户问"去年过年是哪一天"
|
|
64
|
+
- 预期产出:解析出去年春节的具体日期
|
|
65
|
+
- 关键要点:使用农历转换功能
|
|
66
|
+
- 示例3:
|
|
67
|
+
- 场景/输入:用户问"黄龙的生日是农历二月二十五,明年他的生日是公历的哪一天?"
|
|
68
|
+
- 预期产出:解析出明年农历二月二十五对应的公历日期
|
|
69
|
+
- 关键要点:支持解析具体农历日期并进行年份调整
|
|
70
|
+
- 示例4:
|
|
71
|
+
- 场景/输入:用户问"今年五一是不是工作日?"
|
|
72
|
+
- 预期产出:解析出五一的日期,并提供isWorkday和isHoliday字段
|
|
73
|
+
- 关键要点:使用chinese-workday模块判断节假日
|
|
74
|
+
- 示例5:
|
|
75
|
+
- 场景/输入:用户问"国庆节是哪一天"
|
|
76
|
+
- 预期产出:解析出今年国庆节的日期(默认当年)
|
|
77
|
+
- 关键要点:节日名称不带年份前缀时默认使用当前年份
|
|
78
|
+
- 示例6:
|
|
79
|
+
- 场景/输入:用户问"清明节"
|
|
80
|
+
- 预期产出:解析出今年清明节的日期(基于节气计算)
|
|
81
|
+
- 关键要点:清明节是节气节日,日期在4月4-5日间浮动
|
|
82
|
+
- 示例7:
|
|
83
|
+
- 场景/输入:用户问"元宵节"
|
|
84
|
+
- 预期产出:解析出今年元宵节的日期(农历正月十五)
|
|
85
|
+
- 关键要点:元宵节是农历节日,通过lunisolar转换
|
|
86
|
+
|
|
87
|
+
## CLI命令行使用
|
|
88
|
+
### 基础调用方式
|
|
89
|
+
- `node scripts/parse-time.js --query "今天"` - 使用--query参数
|
|
90
|
+
- `node scripts/parse-time.js -q "明天"` - 使用-q快捷参数
|
|
91
|
+
- `node scripts/parse-time.js "农历二月二十五"` - 直接传入位置参数
|
|
92
|
+
|
|
93
|
+
### 输出格式选项
|
|
94
|
+
- `--format json` - JSON格式输出(默认)
|
|
95
|
+
- `--format text` - 文本格式输出
|
|
96
|
+
- `--format simple` - 仅输出日期
|
|
97
|
+
|
|
98
|
+
### 其他选项
|
|
99
|
+
- `--examples` - 显示使用示例
|
|
100
|
+
- `--help` - 显示帮助信息
|
|
101
|
+
- `--year 2025` - 指定年份(用于农历日期转换)
|
|
102
|
+
- `--verbose, -v` - 显示详细信息
|
|
103
|
+
- `--version` - 显示版本号
|
|
104
|
+
|
|
105
|
+
### npm脚本
|
|
106
|
+
- `npm test` - 运行测试
|
|
107
|
+
- `npm start` - 启动程序
|
|
108
|
+
- `npm run examples` - 显示示例
|
|
109
|
+
|
|
110
|
+
## JSON 输出字段说明
|
|
111
|
+
成功时返回以下 JSON 结构:
|
|
112
|
+
| 字段 | 说明 | 示例 |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `success` | 是否解析成功 | `true` / `false` |
|
|
115
|
+
| `originalQuery` | 原始输入 | `"今天"` |
|
|
116
|
+
| `parsedDate` | 解析后的公历日期(YYYY-MM-DD) | `"2026-05-08"` |
|
|
117
|
+
| `timestamp` | Unix 毫秒时间戳 | `1778212479463` |
|
|
118
|
+
| `lunarInfo.year` | 农历年份 | `2026` |
|
|
119
|
+
| `lunarInfo.month` | 农历月份 | `1` |
|
|
120
|
+
| `lunarInfo.day` | 农历日 | `1` |
|
|
121
|
+
| `lunarInfo.isLeapMonth` | 是否为闰月 | `false` |
|
|
122
|
+
| `lunarInfo.zodiac` | 生肖 | `"马"` |
|
|
123
|
+
| `lunarInfo.term` | 节气 | `"立春"` |
|
|
124
|
+
| `festivalInfo` | 节日/假期名称 | `"春节"` / `"清明节"` / `"周末"` / `"工作日"` |
|
|
125
|
+
| `isWorkday` | 是否为工作日 | `true` / `false` |
|
|
126
|
+
| `isHoliday` | 是否为法定节假日 | `true` / `false` |
|
|
127
|
+
| `matchedKeyword` | 匹配到的关键词 | `"今年五一"` |
|
|
128
|
+
|
|
129
|
+
## 资源索引
|
|
130
|
+
- 脚本:见 [scripts/parse-time.js](scripts/parse-time.js)(用途与参数:解析时间关键词,支持--query参数传入用户提问;支持全局安装作为命令行工具使用)
|
|
131
|
+
- 参考:见 [references/time-keywords.md](references/time-keywords.md)(何时读取:需要了解所有支持的时间关键词时)
|
|
132
|
+
- 安装指南:见 [INSTALL.md](INSTALL.md)(何时读取:需要了解如何全局安装和使用命令行工具时)
|
|
133
|
+
|
|
134
|
+
## 注意事项
|
|
135
|
+
- 脚本会根据当前系统时间进行计算,确保系统时间准确
|
|
136
|
+
- 农历转换基于lunisolar库,支持常见传统节日
|
|
137
|
+
- 节假日和工作日判断基于chinese-workday库
|
|
138
|
+
- 时间戳为毫秒级Unix时间戳
|
|
139
|
+
- `lunarInfo` 中的 `zodiac` 和 `term` 字段可能为 `null`,取决于 lunisolar 库对当前日期的数据覆盖
|
|
140
|
+
- `festivalInfo` 除具体节日名称外,也可能返回 `"周末"`、`"工作日"`、`"补劳动节"` 等描述
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cn-time-parser",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "智能识别中文时间关键词并转换为具体日期和时间戳",
|
|
6
|
+
"main": "scripts/parse-time.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cn-time-parser": "./scripts/parse-time.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node scripts/parse-time.js --query '今天'",
|
|
12
|
+
"start": "node scripts/parse-time.js",
|
|
13
|
+
"examples": "node scripts/parse-time.js --examples"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"dayjs": "^1.11.10",
|
|
17
|
+
"lunisolar": "^2.0.2",
|
|
18
|
+
"argparse": "^2.0.1",
|
|
19
|
+
"chinese-workday": "^1.11.0"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["time", "parser", "chinese", "lunar"],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# 支持的时间关键词列表
|
|
2
|
+
|
|
3
|
+
## 概览
|
|
4
|
+
本文档列出了时间解析器支持的所有时间关键词,包括基础时间关键词、节日关键词和农历日期解析。
|
|
5
|
+
|
|
6
|
+
## 农历日期解析
|
|
7
|
+
|
|
8
|
+
### 支持的农历日期格式
|
|
9
|
+
- 农历[月份][日期] - 例如:农历二月十五、农历三月初八
|
|
10
|
+
- [年份修饰]农历[月份][日期] - 例如:明年农历二月二十五、去年农历三月初五
|
|
11
|
+
- 农历[年份]年[月份][日期] - 例如:农历2025年二月十五
|
|
12
|
+
- 农历闰[月份][日期] - 例如:农历闰二月初五(仅当年有该闰月时有效)
|
|
13
|
+
|
|
14
|
+
### 中文数字支持
|
|
15
|
+
- 月份和日期支持中文数字:一、二、三...十、二十、三十
|
|
16
|
+
- 也支持阿拉伯数字:1、2、3...10、20、30
|
|
17
|
+
|
|
18
|
+
## 基础时间关键词
|
|
19
|
+
|
|
20
|
+
### 相对日期
|
|
21
|
+
- 今天 - 当前日期
|
|
22
|
+
- 昨天 - 昨天的日期
|
|
23
|
+
- 明天 - 明天的日期
|
|
24
|
+
- 前天 - 前天的日期
|
|
25
|
+
- 后天 - 后天的日期
|
|
26
|
+
- 大前天 - 大前天的日期
|
|
27
|
+
- 大后天 - 大后天的日期
|
|
28
|
+
|
|
29
|
+
### 相对年份
|
|
30
|
+
- 今年 - 今年的开始日期(1月1日)
|
|
31
|
+
- 去年 - 去年的开始日期(1月1日)
|
|
32
|
+
- 明年 - 明年的开始日期(1月1日)
|
|
33
|
+
|
|
34
|
+
## 节日关键词
|
|
35
|
+
|
|
36
|
+
### 农历节日
|
|
37
|
+
- 今年春节/今年过年 - 今年春节的日期
|
|
38
|
+
- 去年春节/去年过年 - 去年春节的日期
|
|
39
|
+
- 明年春节/明年过年 - 明年春节的日期
|
|
40
|
+
|
|
41
|
+
### 公历节日
|
|
42
|
+
- 今年五一 - 今年五一国际劳动节(5月1日)
|
|
43
|
+
- 去年五一 - 去年五一国际劳动节
|
|
44
|
+
- 明年五一 - 明年五一国际劳动节
|
|
45
|
+
- 今年十一/今年国庆 - 今年国庆节(10月1日)
|
|
46
|
+
- 去年十一/去年国庆 - 去年国庆节
|
|
47
|
+
- 明年十一/明年国庆 - 明年国庆节
|
|
48
|
+
- 今年元旦 - 今年元旦(1月1日)
|
|
49
|
+
- 去年元旦 - 去年元旦
|
|
50
|
+
- 明年元旦 - 明年元旦
|
|
51
|
+
|
|
52
|
+
## 使用示例
|
|
53
|
+
|
|
54
|
+
### 基础时间关键词示例
|
|
55
|
+
```
|
|
56
|
+
输入:"今天天气怎么样?"
|
|
57
|
+
匹配关键词:"今天"
|
|
58
|
+
输出:当前日期
|
|
59
|
+
|
|
60
|
+
输入:"明天有什么安排?"
|
|
61
|
+
匹配关键词:"明天"
|
|
62
|
+
输出:明天的日期
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 节日关键词示例
|
|
66
|
+
```
|
|
67
|
+
输入:"今年春节是哪一天?"
|
|
68
|
+
匹配关键词:"今年春节"
|
|
69
|
+
输出:今年春节的具体日期
|
|
70
|
+
|
|
71
|
+
输入:"去年十一放假几天?"
|
|
72
|
+
匹配关键词:"去年十一"
|
|
73
|
+
输出:去年国庆节的日期(10月1日)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 农历日期示例
|
|
77
|
+
```
|
|
78
|
+
输入:"黄龙的生日是农历二月二十五,明年他的生日是公历的哪一天?"
|
|
79
|
+
匹配关键词:"明年农历二月二十五"
|
|
80
|
+
输出:明年农历二月二十五对应的公历日期
|
|
81
|
+
|
|
82
|
+
输入:"农历三月十五是什么日子?"
|
|
83
|
+
匹配关键词:"农历三月十五"
|
|
84
|
+
输出:今年农历三月十五对应的公历日期
|
|
85
|
+
|
|
86
|
+
输入:"农历2024年八月十五"
|
|
87
|
+
匹配关键词:"农历2024年八月十五"
|
|
88
|
+
输出:2024年农历八月十五对应的公历日期(中秋节)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 输出格式说明
|
|
92
|
+
|
|
93
|
+
### 成功时的输出格式
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"success": true,
|
|
97
|
+
"originalQuery": "用户的原始提问",
|
|
98
|
+
"parsedDate": "YYYY-MM-DD格式的日期",
|
|
99
|
+
"timestamp": 1234567890123,
|
|
100
|
+
"lunarInfo": {
|
|
101
|
+
"year": 农历年,
|
|
102
|
+
"month": 农历月,
|
|
103
|
+
"day": 农历日,
|
|
104
|
+
"isLeapMonth": 是否闰月,
|
|
105
|
+
"zodiac": "生肖",
|
|
106
|
+
"term": "节气"
|
|
107
|
+
},
|
|
108
|
+
"festivalInfo": "节日名称(如春节、劳动节、国庆节等)",
|
|
109
|
+
"isWorkday": true/false,
|
|
110
|
+
"isHoliday": true/false,
|
|
111
|
+
"matchedKeyword": "匹配到的关键词"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 新增字段说明
|
|
116
|
+
- `festivalInfo`: 使用 chinese-workday 模块获取的节日信息
|
|
117
|
+
- `isWorkday`: 是否为工作日(使用 chinese-workday 判断)
|
|
118
|
+
- `isHoliday`: 是否为节假日(使用 chinese-workday 判断)
|
|
119
|
+
|
|
120
|
+
### 失败时的输出格式
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"success": false,
|
|
124
|
+
"error": "错误信息",
|
|
125
|
+
"originalQuery": "用户的原始提问"
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 注意事项
|
|
130
|
+
1. 时间解析基于当前系统时间,请确保系统时间准确
|
|
131
|
+
2. 农历转换使用lunisolar库,支持大部分常见农历日期
|
|
132
|
+
3. 如果未匹配到任何关键词,将返回success: false
|
|
133
|
+
4. 时间戳为毫秒级Unix时间戳
|
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
import { realpathSync } from 'fs';
|
|
6
|
+
import dayjs from 'dayjs';
|
|
7
|
+
import lunisolar from 'lunisolar';
|
|
8
|
+
import { ArgumentParser } from 'argparse';
|
|
9
|
+
import * as chineseWorkday from 'chinese-workday';
|
|
10
|
+
|
|
11
|
+
const { isWorkday, isHoliday, getFestival } = chineseWorkday;
|
|
12
|
+
|
|
13
|
+
const parser = new ArgumentParser({
|
|
14
|
+
description: '中文时间解析器 - 智能识别中文时间关键词并转换为具体日期',
|
|
15
|
+
add_help: true
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// 主参数
|
|
19
|
+
parser.add_argument('--query', {
|
|
20
|
+
help: '包含时间关键词的用户提问',
|
|
21
|
+
required: false
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 快捷参数
|
|
25
|
+
parser.add_argument('-q', '--q', {
|
|
26
|
+
help: '快捷方式:包含时间关键词的用户提问',
|
|
27
|
+
required: false,
|
|
28
|
+
dest: 'query_short'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// 位置参数(直接传入查询)
|
|
32
|
+
parser.add_argument('positional_query', {
|
|
33
|
+
help: '直接传入的时间查询(可选)',
|
|
34
|
+
nargs: '*',
|
|
35
|
+
default: []
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 输出格式选项
|
|
39
|
+
parser.add_argument('--format', {
|
|
40
|
+
help: '输出格式:json(默认)、text、simple',
|
|
41
|
+
choices: ['json', 'text', 'simple'],
|
|
42
|
+
default: 'json'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 其他选项
|
|
46
|
+
parser.add_argument('--year', {
|
|
47
|
+
help: '指定年份(用于农历日期转换)',
|
|
48
|
+
type: 'int'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
parser.add_argument('--verbose', '-v', {
|
|
52
|
+
help: '显示详细信息',
|
|
53
|
+
action: 'store_true'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
parser.add_argument('--examples', {
|
|
57
|
+
help: '显示使用示例',
|
|
58
|
+
action: 'store_true'
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
parser.add_argument('--version', {
|
|
62
|
+
help: '显示版本号',
|
|
63
|
+
action: 'store_true'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const args = parser.parse_args();
|
|
67
|
+
|
|
68
|
+
// 显示版本号
|
|
69
|
+
if (args.version) {
|
|
70
|
+
console.log('cn-time-parser v1.0.0');
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 显示示例
|
|
75
|
+
if (args.examples) {
|
|
76
|
+
console.log(`
|
|
77
|
+
=== 使用示例 ===
|
|
78
|
+
|
|
79
|
+
1. 基础用法:
|
|
80
|
+
node scripts/parse-time.js --query "今天"
|
|
81
|
+
node scripts/parse-time.js --q "明天"
|
|
82
|
+
node scripts/parse-time.js "农历二月二十五"
|
|
83
|
+
|
|
84
|
+
2. 农历日期:
|
|
85
|
+
node scripts/parse-time.js --query "农历正月十五"
|
|
86
|
+
node scripts/parse-time.js --query "明年农历八月十五"
|
|
87
|
+
node scripts/parse-time.js --query "农历2025年九月初九"
|
|
88
|
+
|
|
89
|
+
3. 节日查询:
|
|
90
|
+
node scripts/parse-time.js --query "今年春节"
|
|
91
|
+
node scripts/parse-time.js --query "明年五一"
|
|
92
|
+
node scripts/parse-time.js --query "去年国庆"
|
|
93
|
+
|
|
94
|
+
4. 输出格式:
|
|
95
|
+
node scripts/parse-time.js --query "今天" --format text
|
|
96
|
+
node scripts/parse-time.js --query "今天" --format simple
|
|
97
|
+
|
|
98
|
+
5. 其他选项:
|
|
99
|
+
node scripts/parse-time.js --query "农历二月" --year 2025
|
|
100
|
+
node scripts/parse-time.js --query "今天" -v
|
|
101
|
+
`);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 确定最终的查询字符串
|
|
106
|
+
let query = args.query || args.query_short;
|
|
107
|
+
if (!query && args.positional_query.length > 0) {
|
|
108
|
+
query = args.positional_query.join(' ');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!query) {
|
|
112
|
+
console.error('错误:请提供查询内容!使用 --examples 查看示例。');
|
|
113
|
+
parser.print_help();
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 中文数字映射
|
|
118
|
+
const chineseNumbers = {
|
|
119
|
+
'零': 0, '〇': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
|
|
120
|
+
'五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
|
|
121
|
+
'廿': 20, '卅': 30
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// 解析中文数字
|
|
125
|
+
function parseChineseNumber(text) {
|
|
126
|
+
if (!text) return null;
|
|
127
|
+
|
|
128
|
+
// 特殊处理"正月"
|
|
129
|
+
if (text === '正') return 1;
|
|
130
|
+
|
|
131
|
+
// 直接是阿拉伯数字
|
|
132
|
+
if (/^\d+$/.test(text)) {
|
|
133
|
+
return parseInt(text);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 中文数字解析
|
|
137
|
+
let result = 0;
|
|
138
|
+
let temp = 0;
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < text.length; i++) {
|
|
141
|
+
const char = text[i];
|
|
142
|
+
if (char === '十') {
|
|
143
|
+
if (temp === 0) temp = 1;
|
|
144
|
+
result += temp * 10;
|
|
145
|
+
temp = 0;
|
|
146
|
+
} else if (char === '廿') {
|
|
147
|
+
result += 20;
|
|
148
|
+
} else if (char === '卅') {
|
|
149
|
+
result += 30;
|
|
150
|
+
} else if (chineseNumbers[char] !== undefined) {
|
|
151
|
+
temp = chineseNumbers[char];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
result += temp;
|
|
155
|
+
|
|
156
|
+
return result > 0 ? result : null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 解析农历日期
|
|
160
|
+
function parseLunarDate(query, baseYear) {
|
|
161
|
+
// 预处理:将"正月"替换为"一月"便于匹配
|
|
162
|
+
let processedQuery = query.replace(/正月/g, '一月');
|
|
163
|
+
|
|
164
|
+
// 匹配模式:农历[年份]?[闰]?[月份][初/一/二/...][日]
|
|
165
|
+
const lunarPatterns = [
|
|
166
|
+
// 带年份的模式:农历2024年二月二十五(优先级最高)
|
|
167
|
+
{
|
|
168
|
+
regex: /农历(\d{4})年?(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
|
|
169
|
+
handler: (match) => {
|
|
170
|
+
const year = parseInt(match[1]);
|
|
171
|
+
// 处理"正月"
|
|
172
|
+
const monthStr = match[2] === '正' ? '一' : match[2];
|
|
173
|
+
const month = parseChineseNumber(monthStr);
|
|
174
|
+
const day = parseChineseNumber(match[3]);
|
|
175
|
+
const isLeap = match[0].includes('闰');
|
|
176
|
+
return { year, month, day, isLeap, fullMatch: match[0] };
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
// 完整模式:明年农历二月二十五(两种变体)
|
|
180
|
+
{
|
|
181
|
+
regex: /(今年|去年|明年|后年|前年).*?农历(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
|
|
182
|
+
handler: (match) => {
|
|
183
|
+
const yearModifier = match[1];
|
|
184
|
+
let year = baseYear;
|
|
185
|
+
if (yearModifier === '去年') year -= 1;
|
|
186
|
+
if (yearModifier === '明年') year += 1;
|
|
187
|
+
if (yearModifier === '后年') year += 2;
|
|
188
|
+
if (yearModifier === '前年') year -= 2;
|
|
189
|
+
|
|
190
|
+
// 处理"正月"
|
|
191
|
+
const monthStr = match[2] === '正' ? '一' : match[2];
|
|
192
|
+
const month = parseChineseNumber(monthStr);
|
|
193
|
+
const day = parseChineseNumber(match[3]);
|
|
194
|
+
const isLeap = match[0].includes('闰');
|
|
195
|
+
return { year, month, day, isLeap, fullMatch: match[0] };
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
// 变体:农历明年二月二十五
|
|
199
|
+
{
|
|
200
|
+
regex: /农历(今年|去年|明年|后年|前年)(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
|
|
201
|
+
handler: (match) => {
|
|
202
|
+
const yearModifier = match[1];
|
|
203
|
+
let year = baseYear;
|
|
204
|
+
if (yearModifier === '去年') year -= 1;
|
|
205
|
+
if (yearModifier === '明年') year += 1;
|
|
206
|
+
if (yearModifier === '后年') year += 2;
|
|
207
|
+
if (yearModifier === '前年') year -= 2;
|
|
208
|
+
|
|
209
|
+
// 处理"正月"
|
|
210
|
+
const monthStr = match[2] === '正' ? '一' : match[2];
|
|
211
|
+
const month = parseChineseNumber(monthStr);
|
|
212
|
+
const day = parseChineseNumber(match[3]);
|
|
213
|
+
const isLeap = match[0].includes('闰');
|
|
214
|
+
return { year, month, day, isLeap, fullMatch: match[0] };
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
// 简单模式:农历二月二十五(优先级最低)
|
|
218
|
+
{
|
|
219
|
+
regex: /农历(?:闰)?(\S+?)月(\S+?)(?=$|[,。!?、;:""''()【】\s]|日|号)/,
|
|
220
|
+
handler: (match) => {
|
|
221
|
+
// 处理"正月"
|
|
222
|
+
const monthStr = match[1] === '正' ? '一' : match[1];
|
|
223
|
+
const month = parseChineseNumber(monthStr);
|
|
224
|
+
const day = parseChineseNumber(match[2]);
|
|
225
|
+
const isLeap = match[0].includes('闰');
|
|
226
|
+
return { year: baseYear, month, day, isLeap, fullMatch: match[0] };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
for (const pattern of lunarPatterns) {
|
|
232
|
+
const match = processedQuery.match(pattern.regex);
|
|
233
|
+
if (match) {
|
|
234
|
+
try {
|
|
235
|
+
const lunarData = pattern.handler(match);
|
|
236
|
+
if (lunarData && lunarData.month && lunarData.day) {
|
|
237
|
+
const lunarDate = lunisolar.fromLunar({
|
|
238
|
+
year: lunarData.year,
|
|
239
|
+
month: lunarData.month,
|
|
240
|
+
day: lunarData.day,
|
|
241
|
+
isLeapMonth: lunarData.isLeap
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// 从原始query中提取匹配的关键词,保持"正月"的原貌
|
|
245
|
+
let matchedKeyword = lunarData.fullMatch || match[0];
|
|
246
|
+
if (query.includes('正月') && matchedKeyword.includes('一月')) {
|
|
247
|
+
matchedKeyword = matchedKeyword.replace('一月', '正月');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
date: dayjs(lunarDate.toDate()),
|
|
252
|
+
matchedKeyword: matchedKeyword,
|
|
253
|
+
lunarData: lunarData
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error('解析农历日期时出错:', error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 节日名称映射
|
|
266
|
+
const festivalNameMap = {
|
|
267
|
+
'元旦': '元旦',
|
|
268
|
+
'春节': '春节',
|
|
269
|
+
'过年': '春节',
|
|
270
|
+
'五一': '劳动节',
|
|
271
|
+
'劳动节': '劳动节',
|
|
272
|
+
'国庆': '国庆节',
|
|
273
|
+
'国庆节': '国庆节',
|
|
274
|
+
'十一': '国庆节',
|
|
275
|
+
'清明': '清明节',
|
|
276
|
+
'清明节': '清明节',
|
|
277
|
+
'元宵': '元宵节',
|
|
278
|
+
'元宵节': '元宵节'
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// 已知节日的固定日期映射(作为备用)
|
|
282
|
+
const fixedFestivalDates = {
|
|
283
|
+
'元旦': (year) => dayjs(`${year}-01-01`),
|
|
284
|
+
'劳动节': (year) => dayjs(`${year}-05-01`),
|
|
285
|
+
'国庆节': (year) => dayjs(`${year}-10-01`),
|
|
286
|
+
'春节': (year) => getSpringFestival(year),
|
|
287
|
+
'清明节': (year) => getQingmingDate(year),
|
|
288
|
+
'元宵节': (year) => getLanternFestival(year)
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// 获取节日日期
|
|
292
|
+
function getFestivalDate(year, festivalName) {
|
|
293
|
+
// 标准化节日名称
|
|
294
|
+
const normalizedName = festivalNameMap[festivalName] || festivalName;
|
|
295
|
+
|
|
296
|
+
// 对于有固定日期的节日,直接返回
|
|
297
|
+
if (fixedFestivalDates[normalizedName]) {
|
|
298
|
+
return fixedFestivalDates[normalizedName](year);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 备用方法
|
|
302
|
+
return getSpringFestival(year);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function parseTimeQuery(query, options = {}) {
|
|
306
|
+
const now = dayjs();
|
|
307
|
+
const baseYear = options.year || now.year();
|
|
308
|
+
|
|
309
|
+
let result = {
|
|
310
|
+
success: false,
|
|
311
|
+
originalQuery: query,
|
|
312
|
+
parsedDate: null,
|
|
313
|
+
timestamp: null,
|
|
314
|
+
lunarInfo: null,
|
|
315
|
+
festivalInfo: null,
|
|
316
|
+
isWorkday: null,
|
|
317
|
+
isHoliday: null,
|
|
318
|
+
matchedKeyword: null
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// 首先尝试解析农历日期(优先级最高)
|
|
322
|
+
const lunarResult = parseLunarDate(query, baseYear);
|
|
323
|
+
if (lunarResult) {
|
|
324
|
+
const date = lunarResult.date;
|
|
325
|
+
result.success = true;
|
|
326
|
+
result.parsedDate = date.format('YYYY-MM-DD');
|
|
327
|
+
result.timestamp = date.valueOf();
|
|
328
|
+
result.matchedKeyword = lunarResult.matchedKeyword;
|
|
329
|
+
|
|
330
|
+
// 获取农历信息
|
|
331
|
+
try {
|
|
332
|
+
const lunar = lunisolar(date.toDate());
|
|
333
|
+
result.lunarInfo = {
|
|
334
|
+
year: lunar.lunarYear,
|
|
335
|
+
month: lunar.lunarMonth,
|
|
336
|
+
day: lunar.lunarDay,
|
|
337
|
+
isLeapMonth: lunar.isLeapMonth,
|
|
338
|
+
zodiac: lunar.yearZodiac ? lunar.yearZodiac.name : null,
|
|
339
|
+
term: lunar.solarTerm ? lunar.solarTerm.name : null
|
|
340
|
+
};
|
|
341
|
+
} catch (e) {
|
|
342
|
+
if (args.verbose) console.error('获取农历信息时出错:', e);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 获取节日和工作日信息
|
|
346
|
+
try {
|
|
347
|
+
result.festivalInfo = getFestival(date.toDate());
|
|
348
|
+
result.isWorkday = isWorkday(date.toDate());
|
|
349
|
+
result.isHoliday = isHoliday(date.toDate());
|
|
350
|
+
} catch (e) {
|
|
351
|
+
if (args.verbose) console.error('获取节日信息时出错:', e);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 基础时间关键词
|
|
358
|
+
const keywords = [
|
|
359
|
+
{ regex: /今天/, handler: () => now },
|
|
360
|
+
{ regex: /昨天/, handler: () => now.subtract(1, 'day') },
|
|
361
|
+
{ regex: /明天/, handler: () => now.add(1, 'day') },
|
|
362
|
+
{ regex: /前天/, handler: () => now.subtract(2, 'day') },
|
|
363
|
+
{ regex: /后天/, handler: () => now.add(2, 'day') },
|
|
364
|
+
{ regex: /大前天/, handler: () => now.subtract(3, 'day') },
|
|
365
|
+
{ regex: /大后天/, handler: () => now.add(3, 'day') },
|
|
366
|
+
{ regex: /今年/, handler: () => now.startOf('year') },
|
|
367
|
+
{ regex: /去年/, handler: () => now.subtract(1, 'year').startOf('year') },
|
|
368
|
+
{ regex: /明年/, handler: () => now.add(1, 'year').startOf('year') }
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
// 节日关键词
|
|
372
|
+
const festivalKeywords = [
|
|
373
|
+
// 带年份前缀 → 精确匹配
|
|
374
|
+
{ regex: /今年春节|今年过年/, handler: () => getFestivalDate(baseYear, '春节') },
|
|
375
|
+
{ regex: /去年春节|去年过年/, handler: () => getFestivalDate(baseYear - 1, '春节') },
|
|
376
|
+
{ regex: /明年春节|明年过年/, handler: () => getFestivalDate(baseYear + 1, '春节') },
|
|
377
|
+
{ regex: /今年五一/, handler: () => getFestivalDate(baseYear, '五一') },
|
|
378
|
+
{ regex: /去年五一/, handler: () => getFestivalDate(baseYear - 1, '五一') },
|
|
379
|
+
{ regex: /明年五一/, handler: () => getFestivalDate(baseYear + 1, '五一') },
|
|
380
|
+
{ regex: /今年十一|今年国庆/, handler: () => getFestivalDate(baseYear, '国庆') },
|
|
381
|
+
{ regex: /去年十一|去年国庆/, handler: () => getFestivalDate(baseYear - 1, '国庆') },
|
|
382
|
+
{ regex: /明年十一|明年国庆/, handler: () => getFestivalDate(baseYear + 1, '国庆') },
|
|
383
|
+
{ regex: /今年元旦/, handler: () => getFestivalDate(baseYear, '元旦') },
|
|
384
|
+
{ regex: /去年元旦/, handler: () => getFestivalDate(baseYear - 1, '元旦') },
|
|
385
|
+
{ regex: /明年元旦/, handler: () => getFestivalDate(baseYear + 1, '元旦') },
|
|
386
|
+
{ regex: /今年清明/, handler: () => getFestivalDate(baseYear, '清明节') },
|
|
387
|
+
{ regex: /去年清明/, handler: () => getFestivalDate(baseYear - 1, '清明节') },
|
|
388
|
+
{ regex: /明年清明/, handler: () => getFestivalDate(baseYear + 1, '清明节') },
|
|
389
|
+
{ regex: /今年元宵/, handler: () => getFestivalDate(baseYear, '元宵节') },
|
|
390
|
+
{ regex: /去年元宵/, handler: () => getFestivalDate(baseYear - 1, '元宵节') },
|
|
391
|
+
{ regex: /明年元宵/, handler: () => getFestivalDate(baseYear + 1, '元宵节') },
|
|
392
|
+
// 无年份前缀 → 默认今年(兜底,优先级最低)
|
|
393
|
+
{ regex: /春节|过年/, handler: () => getFestivalDate(baseYear, '春节') },
|
|
394
|
+
{ regex: /五一|劳动节/, handler: () => getFestivalDate(baseYear, '五一') },
|
|
395
|
+
{ regex: /十一|国庆节|国庆/, handler: () => getFestivalDate(baseYear, '国庆') },
|
|
396
|
+
{ regex: /元旦/, handler: () => getFestivalDate(baseYear, '元旦') },
|
|
397
|
+
{ regex: /清明节|清明/, handler: () => getFestivalDate(baseYear, '清明节') },
|
|
398
|
+
{ regex: /元宵节|元宵/, handler: () => getFestivalDate(baseYear, '元宵节') }
|
|
399
|
+
];
|
|
400
|
+
|
|
401
|
+
// 合并所有关键词,节日关键词在前(更长的关键词优先匹配)
|
|
402
|
+
const allKeywords = [...festivalKeywords, ...keywords];
|
|
403
|
+
|
|
404
|
+
// 匹配关键词
|
|
405
|
+
for (const keyword of allKeywords) {
|
|
406
|
+
if (keyword.regex.test(query)) {
|
|
407
|
+
try {
|
|
408
|
+
const date = keyword.handler();
|
|
409
|
+
result.success = true;
|
|
410
|
+
result.parsedDate = date.format('YYYY-MM-DD');
|
|
411
|
+
result.timestamp = date.valueOf();
|
|
412
|
+
result.matchedKeyword = query.match(keyword.regex)[0];
|
|
413
|
+
|
|
414
|
+
// 尝试获取农历信息
|
|
415
|
+
try {
|
|
416
|
+
const lunar = lunisolar(date.toDate());
|
|
417
|
+
result.lunarInfo = {
|
|
418
|
+
year: lunar.lunarYear,
|
|
419
|
+
month: lunar.lunarMonth,
|
|
420
|
+
day: lunar.lunarDay,
|
|
421
|
+
isLeapMonth: lunar.isLeapMonth,
|
|
422
|
+
zodiac: lunar.yearZodiac ? lunar.yearZodiac.name : null,
|
|
423
|
+
term: lunar.solarTerm ? lunar.solarTerm.name : null
|
|
424
|
+
};
|
|
425
|
+
} catch (e) {
|
|
426
|
+
if (args.verbose) console.error('获取农历信息时出错:', e);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 获取节日和工作日信息
|
|
430
|
+
try {
|
|
431
|
+
result.festivalInfo = getFestival(date.toDate());
|
|
432
|
+
result.isWorkday = isWorkday(date.toDate());
|
|
433
|
+
result.isHoliday = isHoliday(date.toDate());
|
|
434
|
+
} catch (e) {
|
|
435
|
+
if (args.verbose) console.error('获取节日信息时出错:', e);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
break;
|
|
439
|
+
} catch (error) {
|
|
440
|
+
if (args.verbose) console.error('处理时间关键词时出错:', error);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// 获取春节日期(农历正月初一)- 备用方法
|
|
449
|
+
function getSpringFestival(year) {
|
|
450
|
+
try {
|
|
451
|
+
// 农历正月初一 - 使用正确的lunisolar API
|
|
452
|
+
const lunarNewYear = lunisolar.fromLunar({
|
|
453
|
+
year: year,
|
|
454
|
+
month: 1,
|
|
455
|
+
day: 1,
|
|
456
|
+
isLeapMonth: false
|
|
457
|
+
});
|
|
458
|
+
return dayjs(lunarNewYear.toDate());
|
|
459
|
+
} catch (error) {
|
|
460
|
+
if (args.verbose) console.error('获取春节日期时出错:', error);
|
|
461
|
+
// 如果获取失败,返回一个近似值(通常春节在1月或2月)
|
|
462
|
+
return dayjs(`${year}-02-01`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// 获取清明节日期(清明节气当日,4月4日或5日)
|
|
467
|
+
function getQingmingDate(year) {
|
|
468
|
+
try {
|
|
469
|
+
// 扫描4月1-10日,找到清明节气所在日期
|
|
470
|
+
for (let d = 1; d <= 10; d++) {
|
|
471
|
+
const date = new Date(year, 3, d); // month 3 = April
|
|
472
|
+
const ls = lunisolar(date);
|
|
473
|
+
if (ls.solarTerm && ls.solarTerm.name === '清明') {
|
|
474
|
+
return dayjs(date);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// 兜底:返回4月5日
|
|
478
|
+
return dayjs(`${year}-04-05`);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
if (args.verbose) console.error('获取清明节日期时出错:', error);
|
|
481
|
+
return dayjs(`${year}-04-05`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// 获取元宵节日期(农历正月十五)
|
|
486
|
+
function getLanternFestival(year) {
|
|
487
|
+
try {
|
|
488
|
+
const lunarDate = lunisolar.fromLunar({
|
|
489
|
+
year: year,
|
|
490
|
+
month: 1,
|
|
491
|
+
day: 15,
|
|
492
|
+
isLeapMonth: false
|
|
493
|
+
});
|
|
494
|
+
return dayjs(lunarDate.toDate());
|
|
495
|
+
} catch (error) {
|
|
496
|
+
if (args.verbose) console.error('获取元宵节日期时出错:', error);
|
|
497
|
+
// 兜底:元宵节通常在公历2月
|
|
498
|
+
return dayjs(`${year}-02-15`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 格式化输出
|
|
503
|
+
function formatOutput(result, format) {
|
|
504
|
+
switch (format) {
|
|
505
|
+
case 'text':
|
|
506
|
+
if (result.success) {
|
|
507
|
+
let output = `查询: ${result.originalQuery}\n`;
|
|
508
|
+
output += `匹配: ${result.matchedKeyword}\n`;
|
|
509
|
+
output += `日期: ${result.parsedDate}\n`;
|
|
510
|
+
output += `时间戳: ${result.timestamp}\n`;
|
|
511
|
+
if (result.festivalInfo) {
|
|
512
|
+
output += `节日: ${result.festivalInfo}\n`;
|
|
513
|
+
}
|
|
514
|
+
output += `工作日: ${result.isWorkday ? '是' : '否'}\n`;
|
|
515
|
+
output += `节假日: ${result.isHoliday ? '是' : '否'}`;
|
|
516
|
+
return output;
|
|
517
|
+
} else {
|
|
518
|
+
return `无法解析查询: ${result.originalQuery}`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
case 'simple':
|
|
522
|
+
if (result.success) {
|
|
523
|
+
return result.parsedDate;
|
|
524
|
+
} else {
|
|
525
|
+
return '解析失败';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
default: // json
|
|
529
|
+
return JSON.stringify(result, null, 2);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// 主函数
|
|
534
|
+
function main() {
|
|
535
|
+
try {
|
|
536
|
+
const result = parseTimeQuery(query, { year: args.year });
|
|
537
|
+
const output = formatOutput(result, args.format);
|
|
538
|
+
console.log(output);
|
|
539
|
+
|
|
540
|
+
if (!result.success) {
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error('解析时间时出错:', error);
|
|
545
|
+
console.log(JSON.stringify({
|
|
546
|
+
success: false,
|
|
547
|
+
error: error.message,
|
|
548
|
+
originalQuery: query
|
|
549
|
+
}, null, 2));
|
|
550
|
+
process.exit(1);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 导出函数供其他模块使用
|
|
555
|
+
export { parseTimeQuery };
|
|
556
|
+
|
|
557
|
+
// 仅在直接运行时执行主函数
|
|
558
|
+
if (process.argv[1]) {
|
|
559
|
+
const _metaPath = realpathSync(fileURLToPath(import.meta.url));
|
|
560
|
+
const _argvPath = realpathSync(resolve(process.argv[1]));
|
|
561
|
+
if (_metaPath === _argvPath) main();
|
|
562
|
+
}
|